OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 127|回复: 0

《M144Z-M3最小系统板使用指南——STM32F103版》第四十一章 SPI实验

[复制链接]

1070

主题

1081

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4443
金钱
4443
注册时间
2019-5-8
在线时间
1199 小时
发表于 2024-5-10 14:54:05 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2024-5-9 16:37 编辑

第四十一章 SPI实验

1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板

2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boar ... _mini_sysboard.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子STM32技术交流QQ群:725095144

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

本章将介绍使用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的连接原理图,如下如图所示:                                 
1111.png
图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,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240509163526.png
表41.2.1.1 函数HAL_SPI_Init()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240509163533.png
表41.2.1.2 函数HAL_SPI_Init()返回值描述
该函数需要传入SPI的句柄指针,该句柄中就包含了SPI的初始化配置参数结构体,该结构体的定义如下所示:
  1. typedef struct
  2. {
  3.     uint32_t Mode;              /* 模式 */
  4.     uint32_t Direction;         /* 方向 */
  5.     uint32_t DataSize;          /* 数据位宽 */
  6.     uint32_t CLKPolarity;       /* 时钟极性 */
  7.     uint32_t CLKPhase;          /* 时钟相位 */
  8.     uint32_t NSS;               /* 片选 */
  9.     uint32_t BaudRatePrescaler; /* 波特率分频系数 */
  10.     uint32_t FirstBit;          /* 高位 */
  11.     uint32_t TIMode;            /* 帧格式 */
  12.     uint32_t CRCCalculation;    /* CRC使能 */
  13.     uint32_t CRCPolynomial;     /* CRC多项式 */
  14. }SPI_InitTypeDef;
复制代码
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.    SPI_HandleTypeDef spi_handle = {0};
  5.    
  6.     /* 初始化SPI */
  7.    spi_handle.Instance = SPI1;
  8.    spi_handle.Init.Mode = SPI_MODE_MASTER;
  9.    spi_handle.Init.Direction =SPI_DIRECTION_2LINES;
  10.    spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;
  11.    spi_handle.Init.CLKPolarity = SPI_POLARITY_HIGH;
  12.    spi_handle.Init.CLKPhase = SPI_PHASE_2EDGE;
  13.    spi_handle.Init.NSS = SPI_NSS_SOFT;
  14.    spi_handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;
  15.    spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
  16.    spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
  17.    spi_handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;
  18.    spi_handle.Init.CRCPolynomial= 1;
  19.    HAL_SPI_Init(&spi_handle);
  20. }
复制代码
②:SPI发送并接收数据
该函数用于SPI发送并接收数据,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_SPI_TransmitReceive( SPI_HandleTypeDef *hspi,
  2.                                              uint8_t *pTxData,
  3.                                              uint8_t *pRxData,
  4.                                              uint16_t Size,
  5.                                              uint32_t Timeout);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240509163546.png
表41.2.1.3 函数HAL_SPI_TransmitReceive()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240509163557.png
表41.2.1.4 函数HAL_SPI_TransmitRceive()返回值描述
该函数的使用示例,如下所示:
  1. #include"stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.     uint8_t txbuf;
  5.     uint8_t rxbuf;
  6.    
  7.     /* SPI发送并接收数据 */
  8.     HAL_SPI_TransmitReceive(&spi_handle, &txbuf, &rxbuf, 1, HAL_MAX_DELAY);
  9. }
复制代码
41.2.2SPI驱动
本章实验的SPI驱动主要负责向NOR Flash驱动提供SPI的各种操作函数,例如:SPI初始化、SPI读写等。本章实验中,SPI的驱动代码包括spi.c和spi.h两个文件。
SPI驱动中,对SPI、GPIO相关的宏定义,如下所示:
  1. #define SPI2_SPI SPI2
  2. #define SPI2_SPI_CLK_ENABLE()          \
  3.     do {                                \
  4.          __HAL_RCC_SPI2_CLK_ENABLE();   \
  5.     }while (0)
  6. #define SPI2_SCK_GPIO_PORT GPIOB
  7. #define SPI2_SCK_GPIO_PIN  GPIO_PIN_13
  8. #define SPI2_SCK_GPIO_CLK_ENABLE()     \
  9.     do {                                \
  10.          __HAL_RCC_GPIOB_CLK_ENABLE();  \
  11.     }while (0)
  12. #define SPI2_MISO_GPIO_PORT GPIOB
  13. #define SPI2_MISO_GPIO_PIN GPIO_PIN_14
  14. #define SPI2_MISO_GPIO_CLK_ENABLE()    \
  15.     do {                                \
  16.          __HAL_RCC_GPIOB_CLK_ENABLE();  \
  17.     }while (0)
  18. #define SPI2_MOSI_GPIO_PORT GPIOB
  19. #define SPI2_MOSI_GPIO_PIN GPIO_PIN_15
  20. #define SPI2_MOSI_GPIO_CLK_ENABLE()    \
  21.     do {                                \
  22.          __HAL_RCC_GPIOB_CLK_ENABLE();  \
  23.     }while (0)
  24. SPI驱动中,SPI的初始化函数,如下所示:
  25. /**
  26. *@brief   初始化SPI2
  27. *@param   无
  28. *@retval  无
  29. */
  30. void spi2_init(void)
  31. {
  32.     /* 配置SPI2 */
  33.    g_spi2_handle.Instance = SPI2_SPI;
  34.    g_spi2_handle.Init.Mode = SPI_MODE_MASTER;
  35.    g_spi2_handle.Init.Direction =SPI_DIRECTION_2LINES;
  36.    g_spi2_handle.Init.DataSize = SPI_DATASIZE_8BIT;
  37.    g_spi2_handle.Init.CLKPolarity = SPI_POLARITY_HIGH;
  38.    g_spi2_handle.Init.CLKPhase = SPI_PHASE_2EDGE;
  39.    g_spi2_handle.Init.NSS = SPI_NSS_SOFT;
  40.    g_spi2_handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;
  41.    g_spi2_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
  42.    g_spi2_handle.Init.TIMode = SPI_TIMODE_DISABLE;
  43.    g_spi2_handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;
  44.    g_spi2_handle.Init.CRCPolynomial= 1;
  45.    HAL_SPI_Init(&g_spi2_handle);
  46. }
  47. /**
  48. *@brief   HAL库SPI2初始化MSP函数
  49. *@param   无
  50. *@retval  无
  51. */
  52. voidHAL_SPI_MspInit(SPI_HandleTypeDef*hspi)
  53. {
  54.    GPIO_InitTypeDef gpio_init_struct = {0};
  55.    
  56.     if (hspi->Instance == SPI2_SPI)
  57.     {
  58.          /* 时钟使能 */
  59.          SPI2_SPI_CLK_ENABLE();
  60.          SPI2_SCK_GPIO_CLK_ENABLE();
  61.          SPI2_MISO_GPIO_CLK_ENABLE();
  62.          SPI2_MOSI_GPIO_CLK_ENABLE();
  63.          
  64.          /* 配置SCK引脚 */
  65.          gpio_init_struct.Pin = SPI2_SCK_GPIO_PIN;
  66.          gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  67.          gpio_init_struct.Pull = GPIO_PULLUP;
  68.          gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  69.          HAL_GPIO_Init(SPI2_SCK_GPIO_PORT, &gpio_init_struct);
  70.          
  71.          /* 配置MISO引脚 */
  72.          gpio_init_struct.Pin = SPI2_MISO_GPIO_PIN;
  73.          gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  74.          gpio_init_struct.Pull = GPIO_PULLUP;
  75.          gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  76.          HAL_GPIO_Init(SPI2_MISO_GPIO_PORT, &gpio_init_struct);
  77.          
  78.          /* 配置MOSI引脚 */
  79.          gpio_init_struct.Pin = SPI2_MOSI_GPIO_PIN;
  80.          gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  81.          gpio_init_struct.Pull = GPIO_PULLUP;
  82.          gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  83.          HAL_GPIO_Init(SPI2_MOSI_GPIO_PORT, &gpio_init_struct);
  84.     }
  85. }
复制代码
可以看到,该函数会初始化SPI2。
SPI驱动中,使用SPI传输1字节数据的函数,如下所示:
  1. /**
  2. *@brief   SPI2读写一字节数据
  3. *@param   txdata: 待写入的一字节数据
  4. *@retval  读取到的一字节数据
  5. */
  6. uint8_t spi2_read_write_byte(uint8_t txdata)
  7. {
  8.     uint8_t rxdata;
  9.    
  10.     if (HAL_SPI_TransmitReceive(&g_spi2_handle, &txdata, &rxdata, 1, 1000) !=
  11.          HAL_OK)
  12.     {
  13.          return 0;
  14.     }
  15.    
  16.     return rxdata;
  17. }
复制代码
该函数使用函数HAL_SPI_TransmitReceive()发送并接收一个字节数据。

41.2.3 NOR Flash驱动
本章实验的NOR Flash驱动主要负责向应用层提供NOR Flash的初始化和读写操作等函数。本章实验中,NOR Flash的驱动代码包括norflash.c和norflash.h两个文件。
NOR Flash驱动中,对GPIO的相关宏定义,如下所示:
  1. #define NORFLASH_CS_GPIO_PORT  GPIOB
  2. #define NORFLASH_CS_GPIO_PIN   GPIO_PIN_12
  3. #define NORFLASH_CS_GPIO_CLK_ENABLE()  \
  4.     do {                                \
  5.          __HAL_RCC_GPIOB_CLK_ENABLE();  \
  6.     }while (0)
  7. #define NORFLASH_CS(x) do { (x) ?                  \
  8.          HAL_GPIO_WritePin( NORFLASH_CS_GPIO_PORT, \
  9.                              NORFLASH_CS_GPIO_PIN,  \
  10.                              GPIO_PIN_SET):          \
  11.          HAL_GPIO_WritePin( NORFLASH_CS_GPIO_PORT, \
  12.                              NORFLASH_CS_GPIO_PIN,  \
  13.                              GPIO_PIN_RESET);        \
  14.     }while (0)
复制代码
NOR Flash驱动中,初始化NOR Flash的函数,如下所示:
  1. /**
  2. *@brief   初始化NOR Flash
  3. *@param   无
  4. *@retval  无
  5. */
  6. voidnorflash_init(void)
  7. {
  8.    GPIO_InitTypeDef gpio_init_struct = {0};
  9.     uint8_t temp;
  10.    
  11.     /* 时钟使能 */
  12.    NORFLASH_CS_GPIO_CLK_ENABLE();
  13.    
  14.     /* 配置CS引脚 */
  15.    gpio_init_struct.Pin = NORFLASH_CS_GPIO_PIN;
  16.    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
  17.    gpio_init_struct.Pull = GPIO_PULLUP;
  18.    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  19.    HAL_GPIO_Init(NORFLASH_CS_GPIO_PORT, &gpio_init_struct);
  20.    NORFLASH_CS(1);
  21.    
  22.     /* 配置SPI接口 */
  23.    spi2_init();
  24.    spi2_set_speed(SPI_BAUDRATEPRESCALER_4);
  25.    
  26.    g_norflash_type = norflash_read_id();   /* 读取NOR Flash芯片ID */
  27.     if (g_norflash_type == W25Q256)     /* W25Q256需使能4字节地址模式 */
  28.     {
  29.          temp = norflash_read_sr(3);    /* 读状态寄存器3,判断地址模式 */
  30.          if ((temp & 0x01) == 0)        /* 如果不是4字节地址模式,则需进行相应配置 */
  31.          {
  32.              norflash_write_enable();   /* NOR Flash写使能 */
  33.              temp |= (1 << 1);           /* ADP=1,配置上电4字节地址模式 */
  34.              norflash_write_sr(3, temp); /* 写状态寄存器3 */
  35.             
  36.              NORFLASH_CS(0);             /* 使能NOR Flash片选 */
  37.              spi2_read_write_byte(NORFLASH_Enable4ByteAddr);/* 使能4字节地址模式 */
  38.              NORFLASH_CS(1);             /* 失能NOR Flash片选 */
  39.          }
  40.     }
  41. }
复制代码
可以看到,在NOR Flash的初始化函数中,先初始化了控制NOR Flash片选的GPIO引脚,然后再是初始化与NOR Flash通讯的SPI并配置其通讯波特率,最后还会根据不同容量的NOR Flash做相应的配置操作。
NOR Flash驱动中其他对NOR Flash的操作函数,例如,NOR Flash的读写函数、擦除函数等,请读者结合25Q128 NOR Flash芯片的数据手册查看本实验的配套实验源码。

41.2.4 实验应用代码
本章实验的应用代码,如下所示:
  1. static const uint8_t g_text_buf[] = {"STM32 SPITEST"};
  2. #define TEXT_SIZE sizeof(g_text_buf)
  3. int main(void)
  4. {
  5.     uint8_t t = 0;
  6.     uint8_t key;
  7.     uint8_t data[TEXT_SIZE];
  8.     uint16_t id;
  9.     uint32_t flashsize;
  10.    
  11.    HAL_Init();                         /* 初始化HAL库 */
  12.    sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
  13.    delay_init(72);                     /* 初始化延时 */
  14.    usart_init(115200);                 /* 初始化串口 */
  15.    led_init();                         /* 初始化LED */
  16.    key_init();                         /* 初始化按键 */
  17.    lcd_init();                         /* 初始化LCD */
  18.    norflash_init();                    /* 初始化NOR Flash */
  19.    
  20.    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  21.    lcd_show_string(30, 70, 200, 16, 16, "SPITEST", RED);
  22.    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  23.    
  24.    lcd_show_string(30, 110, 200, 16, 16, "WK_UP:WriteKEY0:Read", RED);
  25.    
  26.     id = norflash_read_id();
  27.     while ((id == 0) || (id == 0xFFFF))
  28.     {
  29.          lcd_show_string(30, 130, 200, 16, 16, "NOR FlashCheck Failed!", RED);
  30.          delay_ms(500);
  31.          lcd_show_string(30, 130, 200, 16, 16, "Please Check!          ", RED);
  32.          delay_ms(500);
  33.          LED0_TOGGLE();
  34.     }
  35.    lcd_show_string(30, 130, 200, 16, 16, "NOR FlashReady!", RED);
  36.    flashsize = 16 * 1024 * 1024;
  37.    
  38.     while (1)
  39.     {
  40.          key = key_scan(0);
  41.          if (key == WKUP_PRES)
  42.          {
  43.              /* 往NOR Flash写入数据 */
  44.              lcd_fill(0, 150, 239, 319, WHITE);
  45.              lcd_show_string(30, 150, 200, 16, 16, "Start WriteFlash....", BLUE);
  46.              norflash_write( (uint8_t *)g_text_buf,
  47.                              flashsize - TEXT_SIZE,
  48.                              TEXT_SIZE);
  49.              lcd_show_string(30, 150, 200, 16, 16, "Flash WriteFinished!", BLUE);
  50.          }
  51.          else if (key == KEY0_PRES)
  52.          {
  53.              /* 从NOR Flash读取数据 */
  54.              lcd_show_string(30, 150, 200, 16, 16, "Start ReadFlash.... ", BLUE);
  55.              norflash_read(data, flashsize - TEXT_SIZE, TEXT_SIZE);
  56.              lcd_show_string(30, 150, 200, 16, 16, "The DataReaded Is:  ", BLUE);
  57.              lcd_show_string(30, 170, 200, 16, 16, (char *)data, BLUE);
  58.          }
  59.          
  60.          if (++t == 20)
  61.          {
  62.              t = 0;
  63.              LED0_TOGGLE();
  64.          }
  65.          
  66.          delay_ms(10);
  67.     }
  68. }
复制代码
从本章实验的应用代码中可以看到,在初始化完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中读回的数据。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-6-8 15:20

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表