APM32E030的SPI驱动
本帖最后由 口天土立口 于 2025-9-7 18:34 编辑APM32E030支持SPI通信;
本次代码基于开发板:APM32E030R Micro-EVB
IO:
CS -> PA4
SCK -> PA5
MISO -> PA6
MOSI -> PA7
SPI初始化代码如下:
/* 初始化SPI引脚 */
static void bsp_spi_gpio_init(void)
{
GPIO_Config_T gpioConfig;
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
GPIO_ConfigStructInit(&gpioConfig);
/*
* CS -> PA4
* SCK-> PA5
* MISO -> PA6
* MOSI -> PA7
*/
gpioConfig.pin = GPIO_PIN_4;
gpioConfig.mode = GPIO_MODE_AF;
gpioConfig.outtype = GPIO_OUT_TYPE_PP;
gpioConfig.speed = GPIO_SPEED_50MHz;
gpioConfig.pupd = GPIO_PUPD_PU; /* CS低电平有效,默认拉高 */
GPIO_Config(GPIOA, &gpioConfig);
gpioConfig.pupd = GPIO_PUPD_PD; /* SPI模式0,SCK默认为低电平 */
gpioConfig.pin = GPIO_PIN_5;
GPIO_Config(GPIOA, &gpioConfig);
gpioConfig.pupd = GPIO_PUPD_PU;
gpioConfig.pin = GPIO_PIN_7;
GPIO_Config(GPIOA, &gpioConfig);
gpioConfig.pupd = GPIO_PUPD_NO;
gpioConfig.pin = GPIO_PIN_6;
GPIO_Config(GPIOA, &gpioConfig);
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_4, GPIO_AF_PIN0);
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_5, GPIO_AF_PIN0);
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_6, GPIO_AF_PIN0);
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_7, GPIO_AF_PIN0);
}/* 初始化SPI */
void bsp_spi_init(void)
{
SPI_Config_T spiConfig;
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SPI1);
bsp_spi_gpio_init();
SPI_ConfigStructInit(&spiConfig);
SPI_Reset(SPI1);
spiConfig.mode = SPI_MODE_MASTER;
spiConfig.length = SPI_DATA_LENGTH_8B;
spiConfig.phase = SPI_CLKPHA_1EDGE; /* MODE0 */
spiConfig.polarity = SPI_CLKPOL_LOW;
spiConfig.slaveSelect = SPI_SSC_ENABLE;
spiConfig.firstBit = SPI_FIRST_BIT_MSB;
spiConfig.direction = SPI_DIRECTION_2LINES_FULLDUPLEX;
spiConfig.baudrateDiv = SPI_BAUDRATE_DIV_4;
spiConfig.crcPolynomial = 7;
SPI_Config(SPI1, &spiConfig);
SPI_EnableInternalSlave(SPI1);
SPI_ConfigFIFOThreshold(SPI1, SPI_RXFIFO_QUARTER);
SPI_DisableSSoutput(SPI1);
SPI_Enable(SPI1);
}
W25Q32驱动代码:
/* 最大容量 4MB */
#define W25Q32_MAX_CAPACITY ((uint32_t)0x400000)
/* 扇区大小 4KB */
#define W25Q32_SECTOR_SIZE ((uint16_t)0x1000)
/* 块大小 64KB */
#define W25Q32_BLOCK_SIZE ((uint16_t)0x10000)
/* 页编程大小 */
#define W25Q32_PAGE_PROGRAM_SIZE ((uint16_t)0x100)
/* 制造商ID */
#define W25Q32_MANUFACTURE_ID ((uint32_t)0xEF)
/* JEDEC ID */
#define W25Q32_JEDEC_ID ((W25Q32_MANUFACTURE_ID << 16) | 0x4016)
/* 制造商设备ID */
#define W25Q32_MANUFACTURE_DEVICE_ID ((W25Q32_MANUFACTURE_ID << 8) | (uint8_t)0x15)
/* 超时 */
#define W25Q32_TIMEOUT ((uint32_t)0xFF000)/*
* @brief 初始化
*
* @param None
*
* @retval 0: 成功; -1: 失败
*
*/
int8_t bsp_w25q32_init(void)
{
uint32_t JEDEC_id = 0;
uint16_t Manufacturer_id = 0;
bsp_spi_init();
/* JEDEC ID */
if ((bsp_w25q32_read_JEDEC_id(&JEDEC_id) != 0) || (JEDEC_id != W25Q32_JEDEC_ID)) {
return -1;
}
/* 制造商设备ID */
if ((bsp_w25q32_read_manufacturer_devid(&Manufacturer_id) != 0) || (Manufacturer_id != W25Q32_MANUFACTURE_DEVICE_ID)) {
return -1;
}
return 0;
}/*
* @brief 写寄存器
*
* @param reg: 寄存器
* data: 寄存器数据缓存
* data_num: 寄存器数据数量
* data_ex: 额外数据缓存
* data_ex_num: 额外数据数量
*
* @retval 0: 成功; -1: 失败
*
*/
static int8_t bsp_w25q32_write_reg(uint8_t reg, uint8_t *data, uint32_t data_num, uint8_t *data_ex, uint32_t data_ex_num)
{
uint32_t timeout = 0;
uint32_t i = 0;
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (SPI_ReadStatusFlag(SPI1, SPI_FLAG_BUSY) == SET) {
timeout--;
if (timeout == 0) {
return -1;
}
}
/* 等待发送为空 */
timeout = W25Q32_TIMEOUT;
while (SPI_ReadStatusFlag(SPI1, SPI_FLAG_TXBE) == RESET) {
timeout--;
if (timeout == 0) {
return -1;
}
}
SPI_EnableSSoutput(SPI1);
/* 发送寄存器 */
SPI_TxData8(SPI1, reg);
/* 等待发送完成 */
timeout = W25Q32_TIMEOUT;
while (SPI_ReadStatusFlag(SPI1, SPI_FLAG_TXBE) == RESET) {
timeout--;
if (timeout == 0) {
SPI_DisableSSoutput(SPI1);
return -1;
}
}
/* 获取接收数据 */
(void)SPI_RxData8(SPI1);
/* 发送数据 */
while (i < data_num) {
SPI_TxData8(SPI1, data);
/* 等待发送完成 */
timeout = W25Q32_TIMEOUT;
while (SPI_ReadStatusFlag(SPI1, SPI_FLAG_TXBE) == RESET) {
timeout--;
if (timeout == 0) {
SPI_DisableSSoutput(SPI1);
return -1;
}
}
/* 获取接收数据 */
(void)SPI_RxData8(SPI1);
}
/* 发送数据 */
i = 0;
while (i < data_ex_num) {
SPI_TxData8(SPI1, data_ex);
/* 等待发送完成 */
timeout = W25Q32_TIMEOUT;
while (SPI_ReadStatusFlag(SPI1, SPI_FLAG_TXBE) == RESET) {
timeout--;
if (timeout == 0) {
SPI_DisableSSoutput(SPI1);
return -1;
}
}
/* 获取接收数据 */
(void)SPI_RxData8(SPI1);
}
SPI_DisableSSoutput(SPI1);
return 0;
}/*
* @brief 读寄存器
*
* @param reg: 寄存器
* w_data: 写寄存器数据缓存
* w_data_num: 写寄存器数据数量
* r_data: 读数据缓存
* r_data_num: 读数据数量
*
* @retval 0: 成功; -1: 失败
*
*/
static int8_t bsp_w25q32_read_reg(uint8_t reg, uint8_t *w_data, uint32_t w_data_num, uint8_t *r_data, uint32_t r_data_num)
{
uint32_t timeout = 0;
uint32_t i = 0;
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (SPI_ReadStatusFlag(SPI1, SPI_FLAG_BUSY) == SET) {
timeout--;
if (timeout == 0) {
return -1;
}
}
/* 等待发送为空 */
timeout = W25Q32_TIMEOUT;
while (SPI_ReadStatusFlag(SPI1, SPI_FLAG_TXBE) == RESET) {
timeout--;
if (timeout == 0) {
return -1;
}
}
SPI_EnableSSoutput(SPI1);
/* 发送寄存器 */
SPI_TxData8(SPI1, reg);
/* 等待发送完成 */
timeout = W25Q32_TIMEOUT;
while (SPI_ReadStatusFlag(SPI1, SPI_FLAG_TXBE) == RESET) {
timeout--;
if (timeout == 0) {
SPI_DisableSSoutput(SPI1);
return -1;
}
}
/* 获取接收数据 */
(void)SPI_RxData8(SPI1);
/* 发送数据 */
i = 0;
while (i < w_data_num) {
SPI_TxData8(SPI1, w_data);
/* 等待发送完成 */
timeout = W25Q32_TIMEOUT;
while (SPI_ReadStatusFlag(SPI1, SPI_FLAG_TXBE) == RESET) {
timeout--;
if (timeout == 0) {
SPI_DisableSSoutput(SPI1);
return -1;
}
}
/* 获取接收数据 */
(void)SPI_RxData8(SPI1);
}
/* 接收数据 */
i = 0;
while (i < r_data_num) {
/* 制造时钟信号 */
SPI_TxData8(SPI1, 0xFF);
/* 等待发送完成 */
timeout = W25Q32_TIMEOUT;
while (SPI_ReadStatusFlag(SPI1, SPI_FLAG_TXBE) == RESET) {
timeout--;
if (timeout == 0) {
SPI_DisableSSoutput(SPI1);
return -1;
}
}
/* 获取接收数据 */
r_data = SPI_RxData8(SPI1);
}
SPI_DisableSSoutput(SPI1);
return 0;
}/*
* @brief 读状态1
*
* @param status1: 状态
*
* @retval None
*
*/
static int8_t bsp_w25q32_read_status1(uint8_t *status1)
{
return bsp_w25q32_read_reg(0x05, NULL, 0, status1, 1);
}/*
* @brief 检查忙
*
* @param status1: 状态
*
* @retval 0: 非忙; 1: 忙; -1: 失败
*
*/
static int8_t bsp_w25q32_is_busy(void)
{
uint8_t status1 = 0;
if (bsp_w25q32_read_reg(0x05, NULL, 0, &status1, 1) == 0) {
if ((status1 & 0x01) == 0) {
return 0; /* 非忙 */
} else {
return 1; /* 忙 */
}
}
return -1;/* 异常 */
}/*
* @brief 写使能
*
* @param status1: 状态
*
* @retval 0: 成功; -1: 失败
*
*/
static int8_t bsp_w25q32_write_enable(void)
{
uint32_t timeout = 0;
uint8_t status1 = 0;
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (bsp_w25q32_is_busy() != 0) {
timeout--;
if (timeout == 0) {
return 0;
}
}
if (bsp_w25q32_write_reg(0x06, NULL, 0, NULL, 0) == 0) {
/* 检查忙 */
do {
if (bsp_w25q32_read_status1(&status1) == 0) {
if ((status1 & 0x01) == 0) {
if ((status1 & 0x02) != 0) {
return 0; /* 写使能成功 */
}
return -1; /* 写使能失败 */
} else {
continue; /* 忙 */
}
} else {
return -1; /* 异常 */
}
} while (1);
}
return -1;
}/*
* @brief 读数据
*
* @param addr: 数据地址
* buf: 数据缓存
* data_size: 数据数量
*
* @retval 0: 失败; 其他: 数据数量
*
*/
uint32_t bsp_w25q32_read_data(uint32_t addr, uint8_t *buf, uint32_t data_size)
{
uint8_t w_data = {0};
int8_t status1 = 0;
uint32_t timeout = 0;
/* 检查入参 */
if ((data_size == 0) || (buf == NULL) || (addr >= W25Q32_MAX_CAPACITY) || ((addr + data_size) > W25Q32_MAX_CAPACITY)) {
return 0;
}
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (bsp_w25q32_is_busy() != 0) {
timeout--;
if (timeout == 0) {
return 0;
}
}
w_data = (addr >> 16) & 0xFF;
w_data = (addr >> 8) & 0xFF;
w_data = addr & 0xFF;
if (bsp_w25q32_read_reg(0x03, w_data, 3, buf, data_size) == 0) {
/* 检查忙 */
do {
status1 = bsp_w25q32_is_busy();
if (status1 == 0) {
return data_size;
} else if (status1 == -1) {
break;
}
} while (1);
}
return 0;
}/*
* @brief 写数据
*
* @param addr: 数据地址
* buf: 数据缓存
* data_size: 数据数量
*
* @retval 0: 失败; 其他: 数据数量
*
*/
uint32_t bsp_w25q32_write_data(uint32_t addr, uint8_t *buf, uint32_t data_size)
{
uint32_t timeout = 0;
uint8_t w_data = {0};
int8_t status1 = 0;
uint16_t write_bytes = 0;
uint16_t current_page_write_size = 0; /* 当前页写大小 */
/* 检查入参 */
if ((data_size == 0) || (buf == NULL) || (addr >= W25Q32_MAX_CAPACITY) || ((addr + data_size) > W25Q32_MAX_CAPACITY)) {
return 0;
}
do {
/* 单页写数据超长将回环覆盖 */
current_page_write_size = W25Q32_PAGE_PROGRAM_SIZE - (addr % W25Q32_PAGE_PROGRAM_SIZE);
current_page_write_size = (current_page_write_size <= (data_size - write_bytes)) ? current_page_write_size : (data_size - write_bytes);
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (bsp_w25q32_is_busy() != 0) {
timeout--;
if (timeout == 0) {
return 0;
}
}
if (bsp_w25q32_write_enable() != 0) {
return 0;
}
w_data = (addr >> 16) & 0xFF;
w_data = (addr >> 8) & 0xFF;
w_data = addr & 0xFF;
if (bsp_w25q32_write_reg(0x02, w_data, 3, &buf, current_page_write_size) == 0) {
/* 检查忙 */
do {
status1 = bsp_w25q32_is_busy();
if (status1 == 0) {
write_bytes += current_page_write_size;
addr += current_page_write_size;
break;
} else if (status1 == -1) {
break;
}
} while (1);
} else {
break;
}
} while (write_bytes < data_size);
return write_bytes;
}/*
* @brief 扇区擦除
*
* @param addr: 数据地址
*
* @retval 0: 成功; -1: 失败
*
*/
int8_t bsp_w25q32_sector_erase(uint32_t addr)
{
uint32_t timeout = 0;
uint8_t w_data = {0};
int8_t status1 = 0;
/* 4KB对齐 */
addr = addr & (~0xFFFU);
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (bsp_w25q32_is_busy() != 0) {
timeout--;
if (timeout == 0) {
return 0;
}
}
if (bsp_w25q32_write_enable() != 0) {
return -1;
}
w_data = (addr >> 16) & 0xFF;
w_data = (addr >> 8) & 0xFF;
w_data = addr & 0xFF;
if (bsp_w25q32_write_reg(0x20, w_data, 3, NULL, 0) == 0) {
/* 检查忙 */
do {
status1 = bsp_w25q32_is_busy();
if (status1 == 0) {
return 0;
} else if (status1 == -1) {
break;
}
} while (1);
}
return -1;
}/*
* @brief 块32KB擦除
*
* @param addr: 数据地址
*
* @retval 0: 成功; -1: 失败
*
*/
int8_t bsp_w25q32_block_32kb_erase(uint32_t addr)
{
uint32_t timeout = 0;
uint8_t w_data = {0};
int8_t status1 = 0;
/* 32KB对齐 */
addr = addr & (~0x7FFFU);
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (bsp_w25q32_is_busy() != 0) {
timeout--;
if (timeout == 0) {
return 0;
}
}
if (bsp_w25q32_write_enable() != 0) {
return -1;
}
w_data = (addr >> 16) & 0xFF;
w_data = (addr >> 8) & 0xFF;
w_data = addr & 0xFF;
if (bsp_w25q32_write_reg(0x52, w_data, 3, NULL, 0) == 0) {
/* 检查忙 */
do {
status1 = bsp_w25q32_is_busy();
if (status1 == 0) {
return 0;
} else if (status1 == -1) {
break;
}
} while (1);
}
return -1;
}/*
* @brief 块64KB擦除
*
* @param addr: 数据地址
*
* @retval 0: 成功; -1: 失败
*
*/
int8_t bsp_w25q32_block_64kb_erase(uint32_t addr)
{
uint32_t timeout = 0;
uint8_t w_data = {0};
int8_t status1 = 0;
/* 64KB对齐 */
addr = addr & (~0xFFFFU);
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (bsp_w25q32_is_busy() != 0) {
timeout--;
if (timeout == 0) {
return 0;
}
}
if (bsp_w25q32_write_enable() != 0) {
return -1;
}
w_data = (addr >> 16) & 0xFF;
w_data = (addr >> 8) & 0xFF;
w_data = addr & 0xFF;
if (bsp_w25q32_write_reg(0xD8, w_data, 3, NULL, 0) == 0) {
/* 检查忙 */
do {
status1 = bsp_w25q32_is_busy();
if (status1 == 0) {
return 0;
} else if (status1 == -1) {
break;
}
} while (1);
}
return -1;
}/*
* @brief 整片擦除
*
* @param None
*
* @retval 0: 成功; -1: 失败
*
*/
int8_t bsp_w25q32_chip_erase(void)
{
uint32_t timeout = 0;
int8_t status1 = 0;
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (bsp_w25q32_is_busy() != 0) {
timeout--;
if (timeout == 0) {
return 0;
}
}
if (bsp_w25q32_write_enable() != 0) {
return -1;
}
if (bsp_w25q32_write_reg(0x60, NULL, 0, NULL, 0) == 0) {
/* 检查忙 */
do {
status1 = bsp_w25q32_is_busy();
if (status1 == 0) {
return 0;
} else if (status1 == -1) {
break;
}
} while (1);
}
return -1;
}/*
* @brief 复位
*
* @param None
*
* @retval 0: 成功; -1: 失败
*
*/
int8_t bsp_w25q32_reset_device(void)
{
uint8_t w_data = 0x99;
int8_t status1 = 0;
if (bsp_w25q32_write_reg(0x66, &w_data, 1, NULL, 0) == 0) {
/* 检查忙 */
do {
status1 = bsp_w25q32_is_busy();
if (status1 == 0) {
return 0;
} else if (status1 == -1) {
break;
}
} while (1);
}
return -1;
}/*
* @brief 读制造商设备ID
*
* @param id: 制造商设备ID
*
* @retval 0: 成功; -1: 失败
*
*/
int8_t bsp_w25q32_read_manufacturer_devid(uint16_t *id)
{
uint32_t timeout = 0;
uint8_t w_data = {0, 0, 0};
uint8_t r_data = {0};
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (bsp_w25q32_is_busy() != 0) {
timeout--;
if (timeout == 0) {
return 0;
}
}
if (bsp_w25q32_read_reg(0x90, w_data, 3, r_data, 2) == 0) {
*id = (((uint16_t)r_data) << 8) + r_data;
} else {
return -1;
}
return 0;
}/*
* @brief 读 JEDEC ID
*
* @param id: JEDEC ID
*
* @retval 0: 成功; -1: 失败
*
*/
int8_t bsp_w25q32_read_JEDEC_id(uint32_t *id)
{
uint32_t timeout = 0;
uint8_t r_data = {0};
/* 等待空闲 */
timeout = W25Q32_TIMEOUT;
while (bsp_w25q32_is_busy() != 0) {
timeout--;
if (timeout == 0) {
return 0;
}
}
if (bsp_w25q32_read_reg(0x9F, NULL, 0, r_data, 3) == 0) {
*id = (((uint32_t)r_data) << 16) + (((uint16_t)r_data) << 8) + r_data;
} else {
return -1;
}
return 0;
}
测试代码如下:
#define BUF_SIZE (4096)
#define TEST_4KB (0x1000)
uint8_t test_buf;
uint32_t test_addr = 0;
uint32_t i = 0;
int8_t result = 0;
uint32_t num_index = 0;
// 应用初始化
void app_init(void)
{
bsp_w25q32_init();
/* 4KB测试 */
num_index = 0;
result = 0;
do {
/* 1.扇区擦除 */
if (bsp_w25q32_sector_erase(test_addr) == 0) {
/* 回读检查 */
memset(test_buf, 0, sizeof(test_buf));
if (bsp_w25q32_read_data(test_addr, test_buf, sizeof(test_buf)) == sizeof(test_buf)) {
for (i = 0; i < TEST_4KB; i++) {
if (test_buf != 0xFF) {
result = -1;
break;
}
}
if (result != 0) {
break;
}
/* 2.写新数据 */
num_index += 10;
for (i = 0; i < TEST_4KB; i++) {
test_buf = num_index + i;
}
if (bsp_w25q32_write_data(test_addr, test_buf, sizeof(test_buf)) == sizeof(test_buf)) {
/* 回读检查 */
memset(test_buf, 0, sizeof(test_buf));
if (bsp_w25q32_read_data(test_addr, test_buf, sizeof(test_buf)) == sizeof(test_buf)) {
for (i = 0; i < TEST_4KB; i++) {
if (test_buf != (uint8_t)(num_index + i)) {
result = -1;
break;
}
}
if (result != 0) {
break;
}
/* 3.擦除数据 */
if (bsp_w25q32_sector_erase(test_addr) == 0) {
/* 回读检查 */
memset(test_buf, 0, sizeof(test_buf));
if (bsp_w25q32_read_data(test_addr, test_buf, sizeof(test_buf)) == sizeof(test_buf)) {
for (i = 0; i < TEST_4KB; i++) {
if (test_buf != 0xFF) {
result = -1;
break;
}
}
} else {
result = -1;
break;
}
} else {
result = -1;
break;
}
} else {
result = -1;
break;
}
} else {
result = -1;
break;
}
} else {
result = -1;
break;
}
} else {
result = -1;
break;
}
if (result != 0) {
break;
}
test_addr += TEST_4KB;
} while (test_addr < 0x400000);
if (result == 0) {
result = 2;
}
}
// 应用任务
void app_task(void)
{
}
详细代码,请查看附件:
贴了好长的代码啊 幻影书记 发表于 2025-9-8 14:15
贴了好长的代码啊
同时方便不想下载源码的朋友直接看代码 楼主,SPI发送里面为什么要有超时判断?
我主发送不是一定能成功的吗? 空灵回声 发表于 2025-9-9 15:41
楼主,SPI发送里面为什么要有超时判断?
我主发送不是一定能成功的吗?
外设的状态会有异常,MCU有可能不会给你想要的结果,安全的设计必须考虑异常情况的处理
页:
[1]