本帖最后由 正点原子运营 于 2024-5-9 16:37 编辑
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板
2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版
6)正点原子STM32技术交流QQ群:725095144
本章将介绍使用STM32F103驱动板载的NOR Flash进行读写操作。通过本章的学习,读者将学习到使用SPI驱动NOR Flash的使用。 本章分为如下几个小节: 41.1 硬件设计 41.2 程序设计 41.3 下载验证
41.1 硬件设计 41.1.1 例程功能 1. 按下WKUP按键可往NOR Flash写入“STM32 SPI TEST” 2. 按下KEY0按键可从NOR Flash读取到“STM32 SPI TEST”,并显示至LCD 3. LED0闪烁,提示程序正在运行
41.1.2 硬件资源 1. LED LED0 - PB5 2. 正点原子2.8/3.5/4.3/7/10寸TFTLCD模块 3. 按键 WKUP - PA0 KEY0 - PE4 5. NOR Flash SPI2_SCK - PB13 SPI2_MISO - PB14 SPI2_MOSI - PB15 SPI2_CS - PB12
41.1.3 原理图 本章实验使用了板载的25Q128芯片,该芯片是一个NOR Flash,MCU是通过SPI与该NOR Flash进行连接与通信的,该NOR Flash与MCU的连接原理图,如下如图所示: 图41.1.3.1 NOR Flash与MCU的连接原理图 41.2 程序设计 41.2.1 HAL库的SPI驱动 本章实验通过SPI2驱动NOR Flash,因此需要对SPI2进行相应的配置,并使用SPI2与NOR Flash进行通信,其具体的步骤如下所示: ①:初始化SPI ②:SPI发送并接收数据 在HAL库中对应的驱动函数如下: ①:初始化SPI 该函数用于初始化SPI,其函数原型如下所示: - HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);
复制代码该函数的形参描述,如下表所示: 表41.2.1.1 函数HAL_SPI_Init()形参描述 该函数的返回值描述,如下表所示: 表41.2.1.2 函数HAL_SPI_Init()返回值描述 该函数需要传入SPI的句柄指针,该句柄中就包含了SPI的初始化配置参数结构体,该结构体的定义如下所示: - typedef struct
- {
- uint32_t Mode; /* 模式 */
- uint32_t Direction; /* 方向 */
- uint32_t DataSize; /* 数据位宽 */
- uint32_t CLKPolarity; /* 时钟极性 */
- uint32_t CLKPhase; /* 时钟相位 */
- uint32_t NSS; /* 片选 */
- uint32_t BaudRatePrescaler; /* 波特率分频系数 */
- uint32_t FirstBit; /* 高位 */
- uint32_t TIMode; /* 帧格式 */
- uint32_t CRCCalculation; /* CRC使能 */
- uint32_t CRCPolynomial; /* CRC多项式 */
- }SPI_InitTypeDef;
复制代码该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- SPI_HandleTypeDef spi_handle = {0};
-
- /* 初始化SPI */
- spi_handle.Instance = SPI1;
- spi_handle.Init.Mode = SPI_MODE_MASTER;
- spi_handle.Init.Direction =SPI_DIRECTION_2LINES;
- spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;
- spi_handle.Init.CLKPolarity = SPI_POLARITY_HIGH;
- spi_handle.Init.CLKPhase = SPI_PHASE_2EDGE;
- spi_handle.Init.NSS = SPI_NSS_SOFT;
- spi_handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;
- spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
- spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
- spi_handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;
- spi_handle.Init.CRCPolynomial= 1;
- HAL_SPI_Init(&spi_handle);
- }
复制代码②:SPI发送并接收数据 该函数用于SPI发送并接收数据,其函数原型如下所示: - HAL_StatusTypeDef HAL_SPI_TransmitReceive( SPI_HandleTypeDef *hspi,
- uint8_t *pTxData,
- uint8_t *pRxData,
- uint16_t Size,
- uint32_t Timeout);
复制代码该函数的形参描述,如下表所示: 表41.2.1.3 函数HAL_SPI_TransmitReceive()形参描述 该函数的返回值描述,如下表所示: 表41.2.1.4 函数HAL_SPI_TransmitRceive()返回值描述 该函数的使用示例,如下所示: - #include"stm32f1xx_hal.h"
- void example_fun(void)
- {
- uint8_t txbuf;
- uint8_t rxbuf;
-
- /* SPI发送并接收数据 */
- HAL_SPI_TransmitReceive(&spi_handle, &txbuf, &rxbuf, 1, HAL_MAX_DELAY);
- }
复制代码41.2.2SPI驱动 本章实验的SPI驱动主要负责向NOR Flash驱动提供SPI的各种操作函数,例如:SPI初始化、SPI读写等。本章实验中,SPI的驱动代码包括spi.c和spi.h两个文件。 SPI驱动中,对SPI、GPIO相关的宏定义,如下所示: - #define SPI2_SPI SPI2
- #define SPI2_SPI_CLK_ENABLE() \
- do { \
- __HAL_RCC_SPI2_CLK_ENABLE(); \
- }while (0)
- #define SPI2_SCK_GPIO_PORT GPIOB
- #define SPI2_SCK_GPIO_PIN GPIO_PIN_13
- #define SPI2_SCK_GPIO_CLK_ENABLE() \
- do { \
- __HAL_RCC_GPIOB_CLK_ENABLE(); \
- }while (0)
- #define SPI2_MISO_GPIO_PORT GPIOB
- #define SPI2_MISO_GPIO_PIN GPIO_PIN_14
- #define SPI2_MISO_GPIO_CLK_ENABLE() \
- do { \
- __HAL_RCC_GPIOB_CLK_ENABLE(); \
- }while (0)
- #define SPI2_MOSI_GPIO_PORT GPIOB
- #define SPI2_MOSI_GPIO_PIN GPIO_PIN_15
- #define SPI2_MOSI_GPIO_CLK_ENABLE() \
- do { \
- __HAL_RCC_GPIOB_CLK_ENABLE(); \
- }while (0)
- SPI驱动中,SPI的初始化函数,如下所示:
- /**
- *@brief 初始化SPI2
- *@param 无
- *@retval 无
- */
- void spi2_init(void)
- {
- /* 配置SPI2 */
- g_spi2_handle.Instance = SPI2_SPI;
- g_spi2_handle.Init.Mode = SPI_MODE_MASTER;
- g_spi2_handle.Init.Direction =SPI_DIRECTION_2LINES;
- g_spi2_handle.Init.DataSize = SPI_DATASIZE_8BIT;
- g_spi2_handle.Init.CLKPolarity = SPI_POLARITY_HIGH;
- g_spi2_handle.Init.CLKPhase = SPI_PHASE_2EDGE;
- g_spi2_handle.Init.NSS = SPI_NSS_SOFT;
- g_spi2_handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;
- g_spi2_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
- g_spi2_handle.Init.TIMode = SPI_TIMODE_DISABLE;
- g_spi2_handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;
- g_spi2_handle.Init.CRCPolynomial= 1;
- HAL_SPI_Init(&g_spi2_handle);
- }
- /**
- *@brief HAL库SPI2初始化MSP函数
- *@param 无
- *@retval 无
- */
- voidHAL_SPI_MspInit(SPI_HandleTypeDef*hspi)
- {
- GPIO_InitTypeDef gpio_init_struct = {0};
-
- if (hspi->Instance == SPI2_SPI)
- {
- /* 时钟使能 */
- SPI2_SPI_CLK_ENABLE();
- SPI2_SCK_GPIO_CLK_ENABLE();
- SPI2_MISO_GPIO_CLK_ENABLE();
- SPI2_MOSI_GPIO_CLK_ENABLE();
-
- /* 配置SCK引脚 */
- gpio_init_struct.Pin = SPI2_SCK_GPIO_PIN;
- gpio_init_struct.Mode = GPIO_MODE_AF_PP;
- gpio_init_struct.Pull = GPIO_PULLUP;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(SPI2_SCK_GPIO_PORT, &gpio_init_struct);
-
- /* 配置MISO引脚 */
- gpio_init_struct.Pin = SPI2_MISO_GPIO_PIN;
- gpio_init_struct.Mode = GPIO_MODE_AF_PP;
- gpio_init_struct.Pull = GPIO_PULLUP;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(SPI2_MISO_GPIO_PORT, &gpio_init_struct);
-
- /* 配置MOSI引脚 */
- gpio_init_struct.Pin = SPI2_MOSI_GPIO_PIN;
- gpio_init_struct.Mode = GPIO_MODE_AF_PP;
- gpio_init_struct.Pull = GPIO_PULLUP;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(SPI2_MOSI_GPIO_PORT, &gpio_init_struct);
- }
- }
复制代码可以看到,该函数会初始化SPI2。 SPI驱动中,使用SPI传输1字节数据的函数,如下所示: - /**
- *@brief SPI2读写一字节数据
- *@param txdata: 待写入的一字节数据
- *@retval 读取到的一字节数据
- */
- uint8_t spi2_read_write_byte(uint8_t txdata)
- {
- uint8_t rxdata;
-
- if (HAL_SPI_TransmitReceive(&g_spi2_handle, &txdata, &rxdata, 1, 1000) !=
- HAL_OK)
- {
- return 0;
- }
-
- return rxdata;
- }
复制代码该函数使用函数HAL_SPI_TransmitReceive()发送并接收一个字节数据。
41.2.3 NOR Flash驱动 本章实验的NOR Flash驱动主要负责向应用层提供NOR Flash的初始化和读写操作等函数。本章实验中,NOR Flash的驱动代码包括norflash.c和norflash.h两个文件。 NOR Flash驱动中,对GPIO的相关宏定义,如下所示: - #define NORFLASH_CS_GPIO_PORT GPIOB
- #define NORFLASH_CS_GPIO_PIN GPIO_PIN_12
- #define NORFLASH_CS_GPIO_CLK_ENABLE() \
- do { \
- __HAL_RCC_GPIOB_CLK_ENABLE(); \
- }while (0)
- #define NORFLASH_CS(x) do { (x) ? \
- HAL_GPIO_WritePin( NORFLASH_CS_GPIO_PORT, \
- NORFLASH_CS_GPIO_PIN, \
- GPIO_PIN_SET): \
- HAL_GPIO_WritePin( NORFLASH_CS_GPIO_PORT, \
- NORFLASH_CS_GPIO_PIN, \
- GPIO_PIN_RESET); \
- }while (0)
复制代码NOR Flash驱动中,初始化NOR Flash的函数,如下所示: - /**
- *@brief 初始化NOR Flash
- *@param 无
- *@retval 无
- */
- voidnorflash_init(void)
- {
- GPIO_InitTypeDef gpio_init_struct = {0};
- uint8_t temp;
-
- /* 时钟使能 */
- NORFLASH_CS_GPIO_CLK_ENABLE();
-
- /* 配置CS引脚 */
- gpio_init_struct.Pin = NORFLASH_CS_GPIO_PIN;
- gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
- gpio_init_struct.Pull = GPIO_PULLUP;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(NORFLASH_CS_GPIO_PORT, &gpio_init_struct);
- NORFLASH_CS(1);
-
- /* 配置SPI接口 */
- spi2_init();
- spi2_set_speed(SPI_BAUDRATEPRESCALER_4);
-
- g_norflash_type = norflash_read_id(); /* 读取NOR Flash芯片ID */
- if (g_norflash_type == W25Q256) /* W25Q256需使能4字节地址模式 */
- {
- temp = norflash_read_sr(3); /* 读状态寄存器3,判断地址模式 */
- if ((temp & 0x01) == 0) /* 如果不是4字节地址模式,则需进行相应配置 */
- {
- norflash_write_enable(); /* NOR Flash写使能 */
- temp |= (1 << 1); /* ADP=1,配置上电4字节地址模式 */
- norflash_write_sr(3, temp); /* 写状态寄存器3 */
-
- NORFLASH_CS(0); /* 使能NOR Flash片选 */
- spi2_read_write_byte(NORFLASH_Enable4ByteAddr);/* 使能4字节地址模式 */
- NORFLASH_CS(1); /* 失能NOR Flash片选 */
- }
- }
- }
复制代码可以看到,在NOR Flash的初始化函数中,先初始化了控制NOR Flash片选的GPIO引脚,然后再是初始化与NOR Flash通讯的SPI并配置其通讯波特率,最后还会根据不同容量的NOR Flash做相应的配置操作。 NOR Flash驱动中其他对NOR Flash的操作函数,例如,NOR Flash的读写函数、擦除函数等,请读者结合25Q128 NOR Flash芯片的数据手册查看本实验的配套实验源码。
41.2.4 实验应用代码 本章实验的应用代码,如下所示: - static const uint8_t g_text_buf[] = {"STM32 SPITEST"};
- #define TEXT_SIZE sizeof(g_text_buf)
- int main(void)
- {
- uint8_t t = 0;
- uint8_t key;
- uint8_t data[TEXT_SIZE];
- uint16_t id;
- uint32_t flashsize;
-
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
- delay_init(72); /* 初始化延时 */
- usart_init(115200); /* 初始化串口 */
- led_init(); /* 初始化LED */
- key_init(); /* 初始化按键 */
- lcd_init(); /* 初始化LCD */
- norflash_init(); /* 初始化NOR Flash */
-
- lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
- lcd_show_string(30, 70, 200, 16, 16, "SPITEST", RED);
- lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
-
- lcd_show_string(30, 110, 200, 16, 16, "WK_UP:WriteKEY0:Read", RED);
-
- id = norflash_read_id();
- while ((id == 0) || (id == 0xFFFF))
- {
- lcd_show_string(30, 130, 200, 16, 16, "NOR FlashCheck Failed!", RED);
- delay_ms(500);
- lcd_show_string(30, 130, 200, 16, 16, "Please Check! ", RED);
- delay_ms(500);
- LED0_TOGGLE();
- }
- lcd_show_string(30, 130, 200, 16, 16, "NOR FlashReady!", RED);
- flashsize = 16 * 1024 * 1024;
-
- while (1)
- {
- key = key_scan(0);
- if (key == WKUP_PRES)
- {
- /* 往NOR Flash写入数据 */
- lcd_fill(0, 150, 239, 319, WHITE);
- lcd_show_string(30, 150, 200, 16, 16, "Start WriteFlash....", BLUE);
- norflash_write( (uint8_t *)g_text_buf,
- flashsize - TEXT_SIZE,
- TEXT_SIZE);
- lcd_show_string(30, 150, 200, 16, 16, "Flash WriteFinished!", BLUE);
- }
- else if (key == KEY0_PRES)
- {
- /* 从NOR Flash读取数据 */
- lcd_show_string(30, 150, 200, 16, 16, "Start ReadFlash.... ", BLUE);
- norflash_read(data, flashsize - TEXT_SIZE, TEXT_SIZE);
- lcd_show_string(30, 150, 200, 16, 16, "The DataReaded Is: ", BLUE);
- lcd_show_string(30, 170, 200, 16, 16, (char *)data, BLUE);
- }
-
- if (++t == 20)
- {
- t = 0;
- LED0_TOGGLE();
- }
-
- delay_ms(10);
- }
- }
复制代码从本章实验的应用代码中可以看到,在初始化完NOR Flash后,会检测与NOR Flash的连接是否正常,若与NOR Flash的连接正常,则会不断地等待按键输入,若检测到WKUP按键被按下,则会往NOR Flash的指定地址中写入指定的数据,若检测KEY0按键被按下,则会从NOR Flash的指定地址中读取数据,并在LCD上进行显示。
41.3 下载验证 在完成编译和烧录操作后,若MCU与NOR Flash的连接无误,则可以在LCD上看到“NOR Flash Ready!”的提示信息,此时可以按下WKUP按键往NOR Flash的指定地址写入指定的数据,然后再按下KEY_0按键从NOR Flash的指定地址将写入的数据读回来在LCD上进行显示,此时便可以看到LCD上显示“STM32 SPI TEST”的提示信息,该提示信息就是从NOR Flash中读回的数据。 |