本帖最后由 xld0932 于 2022-7-16 10:26 编辑
CW32F030系列MCU带有2路串行外设SPI接口,支持3种通讯模式:双向全双工、单线半双工、单工通讯;通过寄存器可将其配置工作在主机或者从机模式,且支持多主机通讯模式,支持直接内存访问(DMA)。主要特点有: - 支持主机模式、从机模式
- 支持全双工、单线半双工、单工
- 可选的4位到16位数据帧宽度
- 支持收发数据LSB或MSB方式
- 可编程时钟极性和时钟相位
- 主机模式下通讯速率高达PCLK/2
- 从机模式下通讯速率高达PCLK/4
- 支持多机通讯模式
- 8个带标志位的中断源
- 支持直接内存访问(DMA)
在CW32F030CxTx StartKit开发板上板载了一颗W25Q64的SPI FLASH,它是一颗64M-BIT(8M-BYTE)容量的SPI接口通讯的NOR FLASH;使用CW32F030的硬件SPI接口结合SFUD通用串行SPI FLASH驱动库程序,来实现对W25Q64的读、写、擦除等操作。需要注意的是,开发板上W25Q64电路是一个独立的模块,提供操作接口CN10,当我们需要实现W25Q64实验操作时,需要将CN10上的VDDF连接到VDD电源,将NCS/MISO/MOSI/SCK分别连接到MCU的SPI1端口对应的引脚上。
SFUD 是一款开源的串行SPI FLASH通用驱动库。由于现有市面的串行FLASH种类居多,各个FLASH的规格及命令存在差异,SFUD就是为了解决这些FLASH的差异现状而设计,让我们的产品能够支持不同品牌及规格的FLASH,提高了涉及到FLASH功能的软件的可重用性及可扩展性,同时也可以规避FLASH缺货或停产给产品所带来的风险。 - 主要特点:支持 SPI/QSPI 接口、面向对象(同时支持多个FLASH对象)、可灵活裁剪、扩展性强、支持 4 字节地址等。
- 资源占用:标准占用RAM:0.2KB & ROM:5.5KB、最小占用RAM:0.1KB & ROM:3.6KB
为什么选用SFUD - 避免项目因FLASH缺货、FLASH停产或产品扩容而带来的风险;
- 越来越多的项目将固件存储到串行FLASH中,例如:ESP8266 的固件、主板中的BIOS及其他常见电子产品中的固件等等,但是各种FLASH规格及命令不统一。使用SFUD即可避免,在相同功能的软件平台基础下,无法适配不同FLASH种类的硬件平台的问题,提高软件的可重用性;
- 简化软件流程,降低开发难度。现在只需要配置好SPI通信,即可畅快的开始玩串行FLASH了;
- 可以用来制作FLASH编程器/烧写器;
SFUD在CW32F030CxTx StartKit开发板上移植和演示 - 可以通过https://gitee.com/Armink/SFUD下载最新版本的SFUD开源代码,并将程序源码添加到工程中:
- 在sfud_cfg.h文件添加当前需要支持的SPI FLASH列表信息,如下所示:
- 其后在sfud_port.c文件中结合CW32F030的SPI底层驱动来完善SFUD所需要的接口功能,具体代码如下所示:
typedef struct {
SPI_TypeDef *spix;
GPIO_TypeDef *cs_gpiox;
uint16_t cs_gpio_pin;
} spi_user_data, *spi_user_data_t;
static char log_buf[256];
void sfud_log_debug(const char *file, const long line, const char *format, ...);
/**
* SPI write data then read data
*/
static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
size_t read_size) {
sfud_err result = SFUD_SUCCESS;
uint8_t send_data, read_data;
spi_user_data_t spi_dev = (spi_user_data_t) spi->user_data;
if (write_size) {
SFUD_ASSERT(write_buf);
}
if (read_size) {
SFUD_ASSERT(read_buf);
}
/**
* add your spi write and read code
*/
GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin, GPIO_Pin_RESET);
for (size_t i = 0; i < (write_size + read_size); i++) {
if (i < write_size) {
send_data = *write_buf++;
} else {
send_data = SFUD_DUMMY_DATA;
}
while (SPI_GetFlagStatus(spi_dev->spix, SPI_FLAG_TXE ) == RESET);
SPI_SendData(spi_dev->spix, send_data);
while (SPI_GetFlagStatus(spi_dev->spix, SPI_FLAG_RXNE) == RESET);
read_data = SPI_ReceiveData(spi_dev->spix);
if (i >= write_size) {
*read_buf++ = read_data;
}
}
GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin, GPIO_Pin_SET);
return result;
}
#ifdef SFUD_USING_QSPI
/**
* read flash data by QSPI
*/
static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
uint8_t *read_buf, size_t read_size) {
sfud_err result = SFUD_SUCCESS;
/**
* add your qspi read flash data code
*/
return result;
}
#endif /* SFUD_USING_QSPI */
static void rcc_initialize(spi_user_data_t spi) {
if (spi->spix == CW_SPI1) {
RCC_AHBPeriphClk_Enable( RCC_AHB_PERIPH_GPIOA, ENABLE);
RCC_APBPeriphClk_Enable2(RCC_APB2_PERIPH_SPI1, ENABLE);
}
}
static void gpio_initialize(spi_user_data_t spi) {
GPIO_InitTypeDef GPIO_InitStructure;
if (spi->spix == CW_SPI1) {
PA10_AFx_SPI1SCK ();
PA11_AFx_SPI1MISO();
PA12_AFx_SPI1MOSI();
GPIO_InitStructure.Pins = GPIO_PIN_10 | GPIO_PIN_12 | GPIO_PIN_15;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.Pins = GPIO_PIN_11;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
GPIO_WritePin(CW_GPIOA, GPIO_PIN_15, GPIO_Pin_SET);
}
}
static void spi_device_initialize(spi_user_data_t spi) {
SPI_InitTypeDef SPI_InitStructure;
if (spi->spix == CW_SPI1) {
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_Speed = SPI_Speed_Low;
SPI_Init(CW_SPI1, &SPI_InitStructure);
SPI_Cmd(CW_SPI1, ENABLE);
}
}
static void spi_lock(const sfud_spi *spi) {
__disable_irq();
}
static void spi_unlock(const sfud_spi *spi) {
__enable_irq();
}
static void retry_delay_100us(void) {
uint32_t delay = 120;
while (delay--);
}
static spi_user_data spi1 =
{
.spix = CW_SPI1,
.cs_gpiox = CW_GPIOA,
.cs_gpio_pin = GPIO_PIN_15
};
sfud_err sfud_spi_port_init(sfud_flash *flash) {
sfud_err result = SFUD_SUCCESS;
/**
* add your port spi bus and device object initialize code like this:
* 1. rcc initialize
* 2. gpio initialize
* 3. spi device initialize
* 4. flash->spi and flash->retry item initialize
* flash->spi.wr = spi_write_read; //Required
* flash->spi.qspi_read = qspi_read; //Required when QSPI mode enable
* flash->spi.lock = spi_lock;
* flash->spi.unlock = spi_unlock;
* flash->spi.user_data = &spix;
* flash->retry.delay = null;
* flash->retry.times = 10000; //Required
*/
switch (flash->index) {
case SFUD_W25Q64_DEVICE_INDEX:
rcc_initialize(&spi1);
gpio_initialize(&spi1);
spi_device_initialize(&spi1);
flash->spi.wr = spi_write_read;
flash->spi.qspi_read = qspi_read;
flash->spi.lock = spi_lock;
flash->spi.unlock = spi_unlock;
flash->spi.user_data = &spi1;
flash->retry.delay = retry_delay_100us;
flash->retry.times = 60 * 10000;
break;
default: break;
}
return result;
}
/**
* This function is print debug info.
*
* @param file the file which has call this function
* @param line the line number which has call this function
* @param format output format
* @param ... args
*/
void sfud_log_debug(const char *file, const long line, const char *format, ...) {
va_list args;
/* args point to the first variable parameter */
va_start(args, format);
printf("[SFUD](%s:%ld) ", file, line);
/* must use vprintf to print */
vsnprintf(log_buf, sizeof(log_buf), format, args);
printf("%s\r\n", log_buf);
va_end(args);
}
/**
* This function is print routine info.
*
* @param format output format
* @param ... args
*/
void sfud_log_info(const char *format, ...) {
va_list args;
/* args point to the first variable parameter */
va_start(args, format);
printf("[SFUD]");
/* must use vprintf to print */
vsnprintf(log_buf, sizeof(log_buf), format, args);
printf("%s\r\n", log_buf);
va_end(args);
}
- 最后我们初始化SFUD,对SPI FLASH进行读、写、擦除等操作测试,具体代码和运行结果如下所示:
void sfud_demo(uint32_t addr, size_t size, uint8_t *data)
{
sfud_err result = SFUD_SUCCESS;
const sfud_flash *flash = sfud_get_device_table() + 0;
size_t i;
/* prepare write data */
for(i = 0; i < size; i++)
{
data[i] = i;
}
/* erase test */
result = sfud_erase(flash, addr, size);
if(result == SFUD_SUCCESS)
{
printf("\r\nErase the %s flash data finish. Start from 0x%08X, size is %d.\r\n", flash->name, addr, size);
}
else
{
printf("\r\nErase the %s flash data failed.\r\n", flash->name);
return;
}
/* write test */
result = sfud_write(flash, addr, size, data);
if(result == SFUD_SUCCESS)
{
printf("\r\nWrite the %s flash data finish. Start from 0x%08X, size is %d.\r\n", flash->name, addr, size);
}
else
{
printf("\r\nWrite the %s flash data failed.\r\n", flash->name);
return;
}
/* read test */
result = sfud_read(flash, addr, size, data);
if(result == SFUD_SUCCESS)
{
printf("\r\nRead the %s flash data success. Start from 0x%08X, size is %d. The data is:\r\n", flash->name, addr, size);
printf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");
for(i = 0; i < size; i++)
{
if((i % 16) == 0)
{
printf("[%08X] ", addr + i);
}
printf("%02X ", data[i]);
if(((i + 1) % 16 == 0) || (i == size - 1))
{
printf("\r\n");
}
}
printf("\r\n");
}
else
{
printf("\r\nRead the %s flash data failed.\r\n", flash->name);
}
/* data check */
for(i = 0; i < size; i++)
{
if(data[i] != i % 256)
{
printf("\r\nRead and check write data has an error. Write the %s flash data failed.\r\n", flash->name);
break;
}
}
if(i == size)
{
printf("\r\nThe %s flash test is success.\r\n", flash->name);
}
}
void W25Q64_Init(void)
{
/* SFUD initialize */
if(sfud_init() == SFUD_SUCCESS)
{
sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
}
}
运行结果:
附件
|