本帖最后由 sujingliang 于 2025-6-6 10:33 编辑
很荣幸在《@开发者们!100+开发板免费领,上海慕尼黑 电子展见》活动中获得一个块APM32E030R开发板。
此次,我首次通过回帖的方式收获幸运,这究竟是幸运之神偶然的恩赐,还是我本就是幸运之子,此刻不过是命运轨迹中一次恰到好处的回归?
不管怎样,感谢极海半导体,感谢21IC。
下面是APM32E030R开发板:

原理图:
可以看到原理图上留有FLASH的扩展(开发板上这部分没有焊接)。
所以我在开发板上补上了一个W25Q80并进行了驱动
1、SPI初始化
对时钟、引脚、SPI等进行初始化
void SPI_InitForFlash(void)
{
SPI_Config_T spiConfig;
GPIO_Config_T gpioConfig;
// 使能时钟
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SPI1);
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
/* Config alter function*/
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引脚
// PA5 - SCK, PA6 - MISO, PA7 - MOSI
gpioConfig.pin = GPIO_PIN_5 | GPIO_PIN_7;
gpioConfig.mode = GPIO_MODE_AF;
gpioConfig.speed = GPIO_SPEED_50MHz;
gpioConfig.outtype = GPIO_OUT_TYPE_PP;
gpioConfig.pupd = GPIO_PUPD_PU;
GPIO_Config(GPIOA, &gpioConfig);
// 配置CS引脚(假设使用PA4)
gpioConfig.pin = GPIO_PIN_4;
gpioConfig.outtype = GPIO_OUT_TYPE_PP;
gpioConfig.speed = GPIO_SPEED_50MHz;
gpioConfig.pupd = GPIO_PUPD_PU;
gpioConfig.mode = GPIO_MODE_OUT;
GPIO_Config(GPIOA, &gpioConfig);
GPIO_SetBit(GPIOA, GPIO_PIN_4); // 初始高电平(不选中)
/* config PIN_6 MISO*/
gpioConfig.pin = GPIO_PIN_6;
gpioConfig.mode = GPIO_MODE_AF;
gpioConfig.pupd = GPIO_PUPD_PU;
gpioConfig.speed = GPIO_SPEED_50MHz;
GPIO_Config(GPIOA, &gpioConfig);
/* SPI RESET*/
SPI_Reset(SPI1);
SPI_ConfigStructInit(&spiConfig);
/* SPI configuration*/
/* Set Clock polarity is Low, but Slave is High*/
spiConfig.polarity = SPI_CLKPOL_LOW;
/* select master mode*/
spiConfig.mode = SPI_MODE_MASTER;
/* SPI Clock Phase is 1EDGE, but Slave is 1EDGE*/
spiConfig.phase = SPI_CLKPHA_1EDGE;
/* Enable Software slave control */
spiConfig.slaveSelect = SPI_SSC_ENABLE;
/* Set SPI BaudRate divider*/
spiConfig.baudrateDiv = SPI_BAUDRATE_DIV_256;
/* SPI data length*/
spiConfig.length = SPI_DATA_LENGTH_8B;
/* Set internal slave*/
SPI_EnableInternalSlave(SPI1);
SPI_Config(SPI1, &spiConfig);
SPI_ConfigFIFOThreshold(SPI1, SPI_RXFIFO_QUARTER);
SPI_Enable(SPI1);
}
2、片选控制
// 片选控制
static void FLASH_CS_Low(void) {
GPIO_ClearBit(GPIOA, GPIO_PIN_4);
}
static void FLASH_CS_High(void) {
GPIO_SetBit(GPIOA, GPIO_PIN_4);
}
3、发送单字节
// SPI发送接收单字节
static uint8_t SPI_TransmitReceiveByte(uint8_t data)
{
while(SPI_ReadStatusFlag(SPI1, SPI_FLAG_TXBE) == RESET);
SPI_TxData8(SPI1, data);
while(SPI_ReadStatusFlag(SPI1, SPI_FLAG_RXBNE) == RESET);
return SPI_RxData8(SPI1);
}
4、写使能
static void FLASH_WriteEnable(void)
{
FLASH_CS_Low();
SPI_TransmitReceiveByte(0x06); // 写使能命令
FLASH_CS_High();
}
5、读取数据
void FLASH_ReadData(uint32_t addr, uint8_t *pData, uint32_t size)
{
FLASH_CS_Low();
SPI_TransmitReceiveByte(0x03); // 读数据命令
// 发送地址(24位)
SPI_TransmitReceiveByte((addr >> 16) & 0xFF);
SPI_TransmitReceiveByte((addr >> 8) & 0xFF);
SPI_TransmitReceiveByte(addr & 0xFF);
// 读取数据
while(size--) {
*pData++ = SPI_TransmitReceiveByte(0xFF);
}
FLASH_CS_High();
}
6、页编程(写入数据,通常256字节一页)
void FLASH_PageProgram(uint32_t addr, uint8_t *pData, uint32_t size)
{
FLASH_WriteEnable();
FLASH_WaitForReady();
FLASH_CS_Low();
SPI_TransmitReceiveByte(0x02); // 页编程命令
// 发送地址
SPI_TransmitReceiveByte((addr >> 16) & 0xFF);
SPI_TransmitReceiveByte((addr >> 8) & 0xFF);
SPI_TransmitReceiveByte(addr & 0xFF);
// 写入数据
while(size--) {
SPI_TransmitReceiveByte(*pData++);
}
FLASH_CS_High();
FLASH_WaitForReady(); // 等待写入完成
}
7、扇区擦除(通常4KB)
void FLASH_SectorErase(uint32_t addr)
{
FLASH_WriteEnable();
FLASH_WaitForReady();
FLASH_CS_Low();
SPI_TransmitReceiveByte(0x20); // 扇区擦除命令
// 发送地址
SPI_TransmitReceiveByte((addr >> 16) & 0xFF);
SPI_TransmitReceiveByte((addr >> 8) & 0xFF);
SPI_TransmitReceiveByte(addr & 0xFF);
FLASH_CS_High();
FLASH_WaitForReady(); // 等待擦除完成
}
8、等待FLASH就绪
static void FLASH_WaitForReady(void)
{
uint8_t status;
FLASH_CS_Low();
SPI_TransmitReceiveByte(0x05); // 读状态寄存器命令
do {
status = SPI_TransmitReceiveByte(0xFF);
} while(status & 0x01); // 检查BUSY位
FLASH_CS_High();
}
9、读取FLASH ID (用于测试通信)
uint32_t FLASH_ReadID(void)
{
uint32_t id = 0;
FLASH_CS_Low();
SPI_TransmitReceiveByte(0x9F); // 读ID命令
id |= (SPI_TransmitReceiveByte(0xFF) << 16);
id |= (SPI_TransmitReceiveByte(0xFF) << 8);
id |= SPI_TransmitReceiveByte(0xFF);
FLASH_CS_High();
return id;
}
10、flash测试程序
void flash_test(void)
{
uint8_t readBuffer[256];
uint8_t writeBuffer[256];
uint32_t flashID;
// 读取FLASH ID
flashID = FLASH_ReadID();
printf("FLASH ID: 0x%06lX\n", (unsigned long)flashID);
// 准备测试数据
for(int i = 0; i < 256; i++) {
writeBuffer[i] = i;
}
// 擦除扇区(假设擦除地址0x000000)
FLASH_SectorErase(0x000000);
// 写入数据
FLASH_PageProgram(0x000000, writeBuffer, 256);
// 读取验证
FLASH_ReadData(0x000000, readBuffer, 256);
for(int i = 0; i < 256; i++) {
printf("0x%02X ",readBuffer[i] );
}
}
11、main函数
int main(void)
{
APM_TINY_COMInit(COM1);
// 初始化
SPI_InitForFlash();
printf("Flash demo program start\r\n");
flash_test();
while (1)
{
}
}
12、运行
可以看到已经读取到FLASH ID,并且读写测试输出符合预期
|