优化实现串口驱动,SPI驱动 W25QXX还需要初始化验证修复
This commit is contained in:
@ -4,3 +4,4 @@ cmake_minimum_required(VERSION 3.22)
|
||||
add_subdirectory(led)
|
||||
add_subdirectory(delay)
|
||||
add_subdirectory(uart)
|
||||
add_subdirectory(w25qxx)
|
||||
|
||||
@ -8,12 +8,57 @@ UART模块用于实现串口通信功能,支持数据发送和接收,提供
|
||||
|
||||
## 接口说明
|
||||
|
||||
### 枚举类型
|
||||
|
||||
#### hal_uart_parity_t
|
||||
UART校验位枚举
|
||||
```c
|
||||
typedef enum {
|
||||
HAL_UART_PARITY_NONE = 0U, /*!< 无奇偶校验 */
|
||||
HAL_UART_PARITY_ODD = 1U, /*!< 奇校验 */
|
||||
HAL_UART_PARITY_EVEN = 2U /*!< 偶校验 */
|
||||
} hal_uart_parity_t;
|
||||
```
|
||||
|
||||
#### hal_uart_stopbits_t
|
||||
UART停止位枚举
|
||||
```c
|
||||
typedef enum {
|
||||
HAL_UART_STOPBITS_1 = 0U, /*!< 1位停止位 */
|
||||
HAL_UART_STOPBITS_2 = 1U /*!< 2位停止位 */
|
||||
} hal_uart_stopbits_t;
|
||||
```
|
||||
|
||||
#### hal_uart_databits_t
|
||||
UART数据位枚举
|
||||
```c
|
||||
typedef enum {
|
||||
HAL_UART_DATABITS_8 = 0U, /*!< 8位数据位 */
|
||||
HAL_UART_DATABITS_9 = 1U /*!< 9位数据位 */
|
||||
} hal_uart_databits_t;
|
||||
```
|
||||
|
||||
#### hal_uart_instance_t
|
||||
UART实例枚举
|
||||
```c
|
||||
typedef enum {
|
||||
HAL_UART_INSTANCE_1 = 0U, /*!< UART1实例 */
|
||||
HAL_UART_INSTANCE_2, /*!< UART2实例 */
|
||||
HAL_UART_INSTANCE_3, /*!< UART3实例 */
|
||||
HAL_UART_INSTANCE_4, /*!< UART4实例 */
|
||||
HAL_UART_INSTANCE_5, /*!< UART5实例 */
|
||||
HAL_UART_INSTANCE_6, /*!< UART6实例 */
|
||||
HAL_UART_INSTANCE_MAX /*!< 实例数量最大值 */
|
||||
} hal_uart_instance_t;
|
||||
```
|
||||
|
||||
### 数据结构
|
||||
|
||||
#### uart_config_t
|
||||
UART配置结构体,用于初始化UART模块
|
||||
```c
|
||||
typedef struct {
|
||||
hal_uart_instance_t instance; /*!< UART实例 */
|
||||
uint32_t baudrate; /*!< 波特率 */
|
||||
hal_uart_parity_t parity; /*!< 校验位 */
|
||||
hal_uart_stopbits_t stopbits; /*!< 停止位 */
|
||||
@ -36,12 +81,19 @@ typedef struct {
|
||||
```c
|
||||
void uart_init(void);
|
||||
```
|
||||
初始化UART模块
|
||||
初始化UART模块,设置默认配置
|
||||
|
||||
**参数**:无
|
||||
|
||||
**返回值**:无
|
||||
|
||||
**默认配置**:
|
||||
- 实例:HAL_UART_INSTANCE_1
|
||||
- 波特率:115200
|
||||
- 校验位:HAL_UART_PARITY_NONE
|
||||
- 停止位:HAL_UART_STOPBITS_1
|
||||
- 数据位:HAL_UART_DATABITS_8
|
||||
|
||||
#### uart_config
|
||||
```c
|
||||
void uart_config(const uart_config_t *config);
|
||||
@ -118,6 +170,16 @@ uint8_t uart_is_rx_ready(void);
|
||||
/* 初始化UART模块 */
|
||||
uart_init();
|
||||
|
||||
/* 配置UART参数 */
|
||||
uart_config_t config = {
|
||||
.instance = HAL_UART_INSTANCE_1,
|
||||
.baudrate = 115200,
|
||||
.parity = HAL_UART_PARITY_NONE,
|
||||
.stopbits = HAL_UART_STOPBITS_1,
|
||||
.databits = HAL_UART_DATABITS_8
|
||||
};
|
||||
uart_config(&config);
|
||||
|
||||
/* 发送字符串 */
|
||||
uart_send_string("Hello, UART!\r\n");
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
* @brief UART configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
hal_uart_instance_t instance;
|
||||
uint32_t baudrate;
|
||||
hal_uart_parity_t parity;
|
||||
hal_uart_stopbits_t stopbits;
|
||||
|
||||
@ -21,6 +21,7 @@ void uart_init(void) {
|
||||
hal_uart_init();
|
||||
|
||||
/* Set default configuration */
|
||||
uart.config.instance = HAL_UART_INSTANCE_1;
|
||||
uart.config.baudrate = 115200;
|
||||
uart.config.parity = HAL_UART_PARITY_NONE;
|
||||
uart.config.stopbits = HAL_UART_STOPBITS_1;
|
||||
@ -58,7 +59,7 @@ void uart_send(const uint8_t *data, size_t length) {
|
||||
return;
|
||||
}
|
||||
|
||||
hal_uart_send(data, length);
|
||||
hal_uart_send(uart.config.instance, data, length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,7 +71,7 @@ void uart_send_string(const char *str) {
|
||||
return;
|
||||
}
|
||||
|
||||
hal_uart_send((const uint8_t *)str, strlen(str));
|
||||
hal_uart_send(uart.config.instance, (const uint8_t *)str, strlen(str));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +85,7 @@ size_t uart_receive(uint8_t *data, size_t length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hal_uart_receive(data, length);
|
||||
return hal_uart_receive(uart.config.instance, data, length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,7 +93,7 @@ size_t uart_receive(uint8_t *data, size_t length) {
|
||||
* @retval 1 if ready, 0 otherwise
|
||||
*/
|
||||
uint8_t uart_is_tx_ready(void) {
|
||||
return hal_uart_is_tx_ready();
|
||||
return hal_uart_is_tx_ready(uart.config.instance);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,5 +101,5 @@ uint8_t uart_is_tx_ready(void) {
|
||||
* @retval 1 if data available, 0 otherwise
|
||||
*/
|
||||
uint8_t uart_is_rx_ready(void) {
|
||||
return hal_uart_is_rx_ready();
|
||||
return hal_uart_is_rx_ready(uart.config.instance);
|
||||
}
|
||||
20
Modules/w25qxx/CMakeLists.txt
Normal file
20
Modules/w25qxx/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
# Create w25qxx library
|
||||
add_library(w25qxx STATIC)
|
||||
|
||||
# Add w25qxx sources
|
||||
target_sources(w25qxx PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Src/w25qxx.c
|
||||
)
|
||||
|
||||
# Add w25qxx include directories
|
||||
target_include_directories(w25qxx PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Inc
|
||||
)
|
||||
|
||||
# Link with stm32cubemx and hal libraries
|
||||
target_link_libraries(w25qxx PRIVATE
|
||||
stm32cubemx
|
||||
hal
|
||||
)
|
||||
209
Modules/w25qxx/Inc/w25qxx.h
Normal file
209
Modules/w25qxx/Inc/w25qxx.h
Normal file
@ -0,0 +1,209 @@
|
||||
/* USER CODE BEGIN Header */
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file : w25qxx.h
|
||||
* @brief : W25QXX flash memory driver header file
|
||||
******************************************************************************
|
||||
*/
|
||||
/* USER CODE END Header */
|
||||
|
||||
#ifndef __W25QXX_H__
|
||||
#define __W25QXX_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* @brief W25QXX command definitions
|
||||
*/
|
||||
#define W25QXX_CMD_WRITE_ENABLE 0x06
|
||||
#define W25QXX_CMD_WRITE_DISABLE 0x04
|
||||
#define W25QXX_CMD_READ_STATUS_REG1 0x05
|
||||
#define W25QXX_CMD_READ_STATUS_REG2 0x35
|
||||
#define W25QXX_CMD_WRITE_STATUS_REG 0x01
|
||||
#define W25QXX_CMD_PAGE_PROGRAM 0x02
|
||||
#define W25QXX_CMD_QUAD_PAGE_PROGRAM 0x32
|
||||
#define W25QXX_CMD_BLOCK_ERASE_4KB 0x20
|
||||
#define W25QXX_CMD_BLOCK_ERASE_32KB 0x52
|
||||
#define W25QXX_CMD_BLOCK_ERASE_64KB 0xD8
|
||||
#define W25QXX_CMD_CHIP_ERASE 0xC7
|
||||
#define W25QXX_CMD_ERASE_SECURITY_REG 0x44
|
||||
#define W25QXX_CMD_PROGRAM_SECURITY_REG 0x42
|
||||
#define W25QXX_CMD_READ_SECURITY_REG 0x48
|
||||
#define W25QXX_CMD_RDID 0x9F
|
||||
#define W25QXX_CMD_REMS 0x90
|
||||
#define W25QXX_CMD_REMS_DUAL 0xAB
|
||||
#define W25QXX_CMD_REMS_QUAD 0xAF
|
||||
#define W25QXX_CMD_READ 0x03
|
||||
#define W25QXX_CMD_FAST_READ 0x0B
|
||||
#define W25QXX_CMD_FAST_READ_DUAL 0x3B
|
||||
#define W25QXX_CMD_FAST_READ_QUAD 0x6B
|
||||
#define W25QXX_CMD_WORD_READ_QUAD 0x9B
|
||||
#define W25QXX_CMD_OCTAL_WORD_READ 0xEB
|
||||
#define W25QXX_CMD_RESET_ENABLE 0x66
|
||||
#define W25QXX_CMD_RESET 0x99
|
||||
#define W25QXX_CMD_RELEASE_POWER_DOWN 0xAB
|
||||
#define W25QXX_CMD_POWER_DOWN 0xB9
|
||||
#define W25QXX_CMD_SET_BURST_WRAP 0x77
|
||||
#define W25QXX_CMD_ENABLE_QPI 0x38
|
||||
#define W25QXX_CMD_DISABLE_QPI 0xFF
|
||||
|
||||
/**
|
||||
* @brief W25QXX status register bits
|
||||
*/
|
||||
#define W25QXX_STATUS_BUSY_BIT 0x01
|
||||
#define W25QXX_STATUS_WEL_BIT 0x02
|
||||
#define W25QXX_STATUS_BP0_BIT 0x04
|
||||
#define W25QXX_STATUS_BP1_BIT 0x08
|
||||
#define W25QXX_STATUS_BP2_BIT 0x10
|
||||
#define W25QXX_STATUS_BP3_BIT 0x20
|
||||
#define W25QXX_STATUS_QE_BIT 0x40
|
||||
#define W25QXX_STATUS_SR1_BIT 0x80
|
||||
|
||||
/**
|
||||
* @brief W25QXX device information structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t manufacturer_id; /* Manufacturer ID */
|
||||
uint8_t device_id; /* Device ID */
|
||||
uint32_t capacity; /* Total capacity in bytes */
|
||||
uint16_t page_size; /* Page size in bytes */
|
||||
uint32_t sector_size; /* Sector size in bytes */
|
||||
uint32_t block_size; /* Block size in bytes */
|
||||
} w25qxx_device_info_t;
|
||||
|
||||
/**
|
||||
* @brief W25QXX SPI communication interface
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Send SPI data
|
||||
* @param data Pointer to data buffer to send
|
||||
* @param size Size of data to send
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool (*spi_send)(const uint8_t *data, uint16_t size);
|
||||
|
||||
/**
|
||||
* @brief Receive SPI data
|
||||
* @param data Pointer to data buffer to receive
|
||||
* @param size Size of data to receive
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool (*spi_receive)(uint8_t *data, uint16_t size);
|
||||
|
||||
/**
|
||||
* @brief Send and receive SPI data
|
||||
* @param tx_data Pointer to transmit data buffer
|
||||
* @param rx_data Pointer to receive data buffer
|
||||
* @param size Size of data to transfer
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool (*spi_transceive)(const uint8_t *tx_data, uint8_t *rx_data, uint16_t size);
|
||||
|
||||
/**
|
||||
* @brief Set chip select pin state
|
||||
* @param state true for selected, false for deselected
|
||||
*/
|
||||
void (*cs_set)(bool state);
|
||||
|
||||
/**
|
||||
* @brief Delay in milliseconds
|
||||
* @param ms Delay time in milliseconds
|
||||
*/
|
||||
void (*delay_ms)(uint32_t ms);
|
||||
} w25qxx_spi_interface_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize W25QXX flash memory
|
||||
* @param interface SPI communication interface
|
||||
* @return true if initialization is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_init(const w25qxx_spi_interface_t *interface);
|
||||
|
||||
/**
|
||||
* @brief Get W25QXX device information
|
||||
* @param info Pointer to device information structure to fill
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_get_device_info(w25qxx_device_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Read data from W25QXX flash memory
|
||||
* @param address Starting address to read from
|
||||
* @param data Pointer to data buffer to store read data
|
||||
* @param size Size of data to read
|
||||
* @return true if read is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_read(uint32_t address, uint8_t *data, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Write data to W25QXX flash memory
|
||||
* @param address Starting address to write to
|
||||
* @param data Pointer to data buffer to write
|
||||
* @param size Size of data to write
|
||||
* @return true if write is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_write(uint32_t address, const uint8_t *data, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Erase 4KB block
|
||||
* @param address Address within the block to erase
|
||||
* @return true if erase is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_erase_block_4kb(uint32_t address);
|
||||
|
||||
/**
|
||||
* @brief Erase 32KB block
|
||||
* @param address Address within the block to erase
|
||||
* @return true if erase is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_erase_block_32kb(uint32_t address);
|
||||
|
||||
/**
|
||||
* @brief Erase 64KB block
|
||||
* @param address Address within the block to erase
|
||||
* @return true if erase is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_erase_block_64kb(uint32_t address);
|
||||
|
||||
/**
|
||||
* @brief Erase entire chip
|
||||
* @return true if erase is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_erase_chip(void);
|
||||
|
||||
/**
|
||||
* @brief Read status register 1
|
||||
* @return Status register 1 value
|
||||
*/
|
||||
uint8_t w25qxx_read_status_reg1(void);
|
||||
|
||||
/**
|
||||
* @brief Read status register 2
|
||||
* @return Status register 2 value
|
||||
*/
|
||||
uint8_t w25qxx_read_status_reg2(void);
|
||||
|
||||
/**
|
||||
* @brief Write status register
|
||||
* @param reg1 Status register 1 value
|
||||
* @param reg2 Status register 2 value
|
||||
* @return true if write is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_write_status_reg(uint8_t reg1, uint8_t reg2);
|
||||
|
||||
/**
|
||||
* @brief Put W25QXX into power down mode
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_power_down(void);
|
||||
|
||||
/**
|
||||
* @brief Release W25QXX from power down mode
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_release_power_down(void);
|
||||
|
||||
#endif /* __W25QXX_H__ */
|
||||
486
Modules/w25qxx/Src/w25qxx.c
Normal file
486
Modules/w25qxx/Src/w25qxx.c
Normal file
@ -0,0 +1,486 @@
|
||||
/* USER CODE BEGIN Header */
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file : w25qxx.c
|
||||
* @brief : W25QXX flash memory driver source file
|
||||
******************************************************************************
|
||||
*/
|
||||
/* USER CODE END Header */
|
||||
|
||||
#include "w25qxx.h"
|
||||
|
||||
/* Private variables */
|
||||
static w25qxx_spi_interface_t g_spi_interface;
|
||||
static w25qxx_device_info_t g_device_info;
|
||||
|
||||
/**
|
||||
* @brief Wait for W25QXX to be ready
|
||||
*/
|
||||
static void w25qxx_wait_ready(void) {
|
||||
uint8_t status;
|
||||
do {
|
||||
status = w25qxx_read_status_reg1();
|
||||
} while (status & W25QXX_STATUS_BUSY_BIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send write enable command
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
static bool w25qxx_write_enable(void) {
|
||||
uint8_t cmd = W25QXX_CMD_WRITE_ENABLE;
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
bool result = g_spi_interface.spi_send(&cmd, 1);
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize W25QXX flash memory
|
||||
* @param interface SPI communication interface
|
||||
* @return true if initialization is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_init(const w25qxx_spi_interface_t *interface) {
|
||||
if (interface == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Save SPI interface */
|
||||
g_spi_interface = *interface;
|
||||
|
||||
/* Initialize device info */
|
||||
g_device_info.manufacturer_id = 0;
|
||||
g_device_info.device_id = 0;
|
||||
g_device_info.capacity = 0;
|
||||
g_device_info.page_size = 256; /* Default page size for W25QXX */
|
||||
g_device_info.sector_size = 4096; /* Default sector size */
|
||||
g_device_info.block_size = 65536; /* Default block size */
|
||||
|
||||
/* Get device ID */
|
||||
uint8_t cmd = W25QXX_CMD_RDID;
|
||||
uint8_t id_data[3] = {0};
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
if (!g_spi_interface.spi_send(&cmd, 1)) {
|
||||
g_spi_interface.cs_set(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_spi_interface.spi_receive(id_data, 3)) {
|
||||
g_spi_interface.cs_set(false);
|
||||
return false;
|
||||
}
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
/* Parse device ID */
|
||||
g_device_info.manufacturer_id = id_data[0];
|
||||
g_device_info.device_id = (id_data[1] << 8) | id_data[2];
|
||||
|
||||
/* Set capacity based on device ID */
|
||||
switch (g_device_info.device_id) {
|
||||
case 0x4013: /* W25Q80 */
|
||||
g_device_info.capacity = 1 * 1024 * 1024;
|
||||
break;
|
||||
case 0x4014: /* W25Q16 */
|
||||
g_device_info.capacity = 2 * 1024 * 1024;
|
||||
break;
|
||||
case 0x4015: /* W25Q32 */
|
||||
g_device_info.capacity = 4 * 1024 * 1024;
|
||||
break;
|
||||
case 0x4016: /* W25Q64 */
|
||||
g_device_info.capacity = 8 * 1024 * 1024;
|
||||
break;
|
||||
case 0x4017: /* W25Q128 */
|
||||
g_device_info.capacity = 16 * 1024 * 1024;
|
||||
break;
|
||||
case 0x4018: /* W25Q256 */
|
||||
g_device_info.capacity = 32 * 1024 * 1024;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get W25QXX device information
|
||||
* @param info Pointer to device information structure to fill
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_get_device_info(w25qxx_device_info_t *info) {
|
||||
if (info == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*info = g_device_info;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read data from W25QXX flash memory
|
||||
* @param address Starting address to read from
|
||||
* @param data Pointer to data buffer to store read data
|
||||
* @param size Size of data to read
|
||||
* @return true if read is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_read(uint32_t address, uint8_t *data, uint32_t size) {
|
||||
if (data == NULL || size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check address range */
|
||||
if (address + size > g_device_info.capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t cmd[4];
|
||||
cmd[0] = W25QXX_CMD_READ;
|
||||
cmd[1] = (uint8_t)((address >> 16) & 0xFF);
|
||||
cmd[2] = (uint8_t)((address >> 8) & 0xFF);
|
||||
cmd[3] = (uint8_t)(address & 0xFF);
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
if (!g_spi_interface.spi_send(cmd, 4)) {
|
||||
g_spi_interface.cs_set(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = g_spi_interface.spi_receive(data, size);
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a page to W25QXX flash memory
|
||||
* @param address Starting address to write to
|
||||
* @param data Pointer to data buffer to write
|
||||
* @param size Size of data to write
|
||||
* @return true if write is successful, false otherwise
|
||||
*/
|
||||
static bool w25qxx_write_page(uint32_t address, const uint8_t *data, uint32_t size) {
|
||||
if (data == NULL || size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check page boundary */
|
||||
uint32_t page_start = address & 0xFFFFFF00;
|
||||
uint32_t page_end = page_start + 255;
|
||||
if (address + size > page_end + 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send write enable command */
|
||||
if (!w25qxx_write_enable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send page program command */
|
||||
uint8_t cmd[4];
|
||||
cmd[0] = W25QXX_CMD_PAGE_PROGRAM;
|
||||
cmd[1] = (uint8_t)((address >> 16) & 0xFF);
|
||||
cmd[2] = (uint8_t)((address >> 8) & 0xFF);
|
||||
cmd[3] = (uint8_t)(address & 0xFF);
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
if (!g_spi_interface.spi_send(cmd, 4)) {
|
||||
g_spi_interface.cs_set(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_spi_interface.spi_send(data, size)) {
|
||||
g_spi_interface.cs_set(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
/* Wait for write to complete */
|
||||
w25qxx_wait_ready();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write data to W25QXX flash memory
|
||||
* @param address Starting address to write to
|
||||
* @param data Pointer to data buffer to write
|
||||
* @param size Size of data to write
|
||||
* @return true if write is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_write(uint32_t address, const uint8_t *data, uint32_t size) {
|
||||
if (data == NULL || size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check address range */
|
||||
if (address + size > g_device_info.capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t current_address = address;
|
||||
uint32_t remaining_size = size;
|
||||
uint32_t page_offset = address % 256;
|
||||
uint32_t write_size;
|
||||
|
||||
/* Write first partial page if needed */
|
||||
if (page_offset > 0) {
|
||||
write_size = (256 - page_offset) < remaining_size ? (256 - page_offset) : remaining_size;
|
||||
if (!w25qxx_write_page(current_address, data, write_size)) {
|
||||
return false;
|
||||
}
|
||||
current_address += write_size;
|
||||
data += write_size;
|
||||
remaining_size -= write_size;
|
||||
}
|
||||
|
||||
/* Write full pages */
|
||||
while (remaining_size >= 256) {
|
||||
if (!w25qxx_write_page(current_address, data, 256)) {
|
||||
return false;
|
||||
}
|
||||
current_address += 256;
|
||||
data += 256;
|
||||
remaining_size -= 256;
|
||||
}
|
||||
|
||||
/* Write last partial page if needed */
|
||||
if (remaining_size > 0) {
|
||||
if (!w25qxx_write_page(current_address, data, remaining_size)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase 4KB block
|
||||
* @param address Address within the block to erase
|
||||
* @return true if erase is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_erase_block_4kb(uint32_t address) {
|
||||
/* Check address range */
|
||||
if (address >= g_device_info.capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send write enable command */
|
||||
if (!w25qxx_write_enable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send block erase command */
|
||||
uint8_t cmd[4];
|
||||
cmd[0] = W25QXX_CMD_BLOCK_ERASE_4KB;
|
||||
cmd[1] = (uint8_t)((address >> 16) & 0xFF);
|
||||
cmd[2] = (uint8_t)((address >> 8) & 0xFF);
|
||||
cmd[3] = (uint8_t)(address & 0xFF);
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
if (!g_spi_interface.spi_send(cmd, 4)) {
|
||||
g_spi_interface.cs_set(false);
|
||||
return false;
|
||||
}
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
/* Wait for erase to complete */
|
||||
w25qxx_wait_ready();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase 32KB block
|
||||
* @param address Address within the block to erase
|
||||
* @return true if erase is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_erase_block_32kb(uint32_t address) {
|
||||
/* Check address range */
|
||||
if (address >= g_device_info.capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send write enable command */
|
||||
if (!w25qxx_write_enable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send block erase command */
|
||||
uint8_t cmd[4];
|
||||
cmd[0] = W25QXX_CMD_BLOCK_ERASE_32KB;
|
||||
cmd[1] = (uint8_t)((address >> 16) & 0xFF);
|
||||
cmd[2] = (uint8_t)((address >> 8) & 0xFF);
|
||||
cmd[3] = (uint8_t)(address & 0xFF);
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
if (!g_spi_interface.spi_send(cmd, 4)) {
|
||||
g_spi_interface.cs_set(false);
|
||||
return false;
|
||||
}
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
/* Wait for erase to complete */
|
||||
w25qxx_wait_ready();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase 64KB block
|
||||
* @param address Address within the block to erase
|
||||
* @return true if erase is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_erase_block_64kb(uint32_t address) {
|
||||
/* Check address range */
|
||||
if (address >= g_device_info.capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send write enable command */
|
||||
if (!w25qxx_write_enable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send block erase command */
|
||||
uint8_t cmd[4];
|
||||
cmd[0] = W25QXX_CMD_BLOCK_ERASE_64KB;
|
||||
cmd[1] = (uint8_t)((address >> 16) & 0xFF);
|
||||
cmd[2] = (uint8_t)((address >> 8) & 0xFF);
|
||||
cmd[3] = (uint8_t)(address & 0xFF);
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
if (!g_spi_interface.spi_send(cmd, 4)) {
|
||||
g_spi_interface.cs_set(false);
|
||||
return false;
|
||||
}
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
/* Wait for erase to complete */
|
||||
w25qxx_wait_ready();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase entire chip
|
||||
* @return true if erase is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_erase_chip(void) {
|
||||
/* Send write enable command */
|
||||
if (!w25qxx_write_enable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send chip erase command */
|
||||
uint8_t cmd = W25QXX_CMD_CHIP_ERASE;
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
if (!g_spi_interface.spi_send(&cmd, 1)) {
|
||||
g_spi_interface.cs_set(false);
|
||||
return false;
|
||||
}
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
/* Wait for erase to complete */
|
||||
w25qxx_wait_ready();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read status register 1
|
||||
* @return Status register 1 value
|
||||
*/
|
||||
uint8_t w25qxx_read_status_reg1(void) {
|
||||
uint8_t cmd = W25QXX_CMD_READ_STATUS_REG1;
|
||||
uint8_t status;
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
g_spi_interface.spi_send(&cmd, 1);
|
||||
g_spi_interface.spi_receive(&status, 1);
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read status register 2
|
||||
* @return Status register 2 value
|
||||
*/
|
||||
uint8_t w25qxx_read_status_reg2(void) {
|
||||
uint8_t cmd = W25QXX_CMD_READ_STATUS_REG2;
|
||||
uint8_t status;
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
g_spi_interface.spi_send(&cmd, 1);
|
||||
g_spi_interface.spi_receive(&status, 1);
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write status register
|
||||
* @param reg1 Status register 1 value
|
||||
* @param reg2 Status register 2 value
|
||||
* @return true if write is successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_write_status_reg(uint8_t reg1, uint8_t reg2) {
|
||||
/* Send write enable command */
|
||||
if (!w25qxx_write_enable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send write status register command */
|
||||
uint8_t cmd[3];
|
||||
cmd[0] = W25QXX_CMD_WRITE_STATUS_REG;
|
||||
cmd[1] = reg1;
|
||||
cmd[2] = reg2;
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
bool result = g_spi_interface.spi_send(cmd, 3);
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
/* Wait for write to complete */
|
||||
w25qxx_wait_ready();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put W25QXX into power down mode
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_power_down(void) {
|
||||
uint8_t cmd = W25QXX_CMD_POWER_DOWN;
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
bool result = g_spi_interface.spi_send(&cmd, 1);
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
/* Wait for power down to complete */
|
||||
g_spi_interface.delay_ms(1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Release W25QXX from power down mode
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool w25qxx_release_power_down(void) {
|
||||
uint8_t cmd = W25QXX_CMD_RELEASE_POWER_DOWN;
|
||||
|
||||
g_spi_interface.cs_set(true);
|
||||
bool result = g_spi_interface.spi_send(&cmd, 1);
|
||||
g_spi_interface.cs_set(false);
|
||||
|
||||
/* Wait for release to complete */
|
||||
g_spi_interface.delay_ms(1);
|
||||
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user