• GD32F103 硬件SPI通信


    1. SPI的通信原理

    SPI既可以做主机也可以做从机。

    当做主机时。MOSI,SCK,CS都是作为输出。 而作为从机时。MOSI,SCK,CS都是作为输入。

     所以SPI的硬件电路应该实现这样的功能。

    2. GD32/STM32的SPI框图 

    1. GD32框图

    如下图做主机的数据流向:

     

    如下图做从机的数据流向: 

     

    2. STM32框图 

    通过一些寄存器的配置来控制电路。跟GD32的差不多。

    波特率配置越高,采样越快。SPI的速率越快。

    3. SPI的寄存器介绍

     1. 控制寄存器0(SPI_CTL0)

     

     

    2. 控制寄存器1(SPI_CTL1) 

    3. 状态寄存器(SPI_STAT 

     

    4. 数据寄存器(SPI_DATA 

    4. SPI主模式配置

     

    1. 发送数据 

    先判断发送主机发送缓冲器是否为空。

    2. 接收数据

    接收数据缓冲器是否为空。如果为空就等待,否则就接收。

     

    5. dome (硬件SPI访问w25Q32)

    NSS\SCK\MISO\MOSI  对应的 PA4\PA5\PA6\PA7引脚。

    1. 具体的SPI配置步骤。

    1. SPI时钟使能,SPI对应的GPIO时钟使能。复用时钟使能。

    2. SPI的GOIP配置。

    3. SPI的初始化配置

    4. SPI使能。

    2. 代码实现

    spi.h

    1. #ifndef _SPI_H
    2. #define _SPI_H
    3. #include "gd32f10x.h"
    4. void w25qxx_rcu_init(void);
    5. void w25qxx_io_init(void);
    6. void w25qxx_spi_init(void);
    7. #endif

    spi.c

    1. #include "spi.h"
    2. // 使能外设时钟
    3. void w25qxx_rcu_init(void)
    4. {
    5. rcu_periph_clock_enable(RCU_GPIOA); //使能GPIOA时钟
    6. rcu_periph_clock_enable(RCU_AF); //使能AF时钟
    7. rcu_periph_clock_enable(RCU_SPI0); //使能SPI0时钟
    8. }
    9. // IO口进行配置,使之复用为SPI0, PA4\PA5\PA6\PA7,NSS\SCK\MISO\MOSI
    10. void w25qxx_io_init(void)
    11. {
    12. gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6); // MISO 浮空输入
    13. gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7); // SCK\MOSI 复用推挽
    14. gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);// NSS片选口 普通的推挽输出
    15. }
    16. // SPI0初始化
    17. void w25qxx_spi_init(void)
    18. {
    19. spi_parameter_struct spi_struct;
    20. spi_struct.device_mode = SPI_MASTER; /*!< SPI master 做主机*/
    21. spi_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; /*!< SPI transfer type 全双工 */
    22. spi_struct.frame_size = SPI_FRAMESIZE_8BIT; /*!< SPI frame size 一次8字节 */
    23. spi_struct.nss = SPI_NSS_SOFT; /*!< SPI NSS control by software 软件CS */
    24. spi_struct.endian = SPI_ENDIAN_MSB; /*!< SPI big endian or little endian 传输高字节在前*/
    25. spi_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; /*!< SPI clock phase and polarity 空闲低电平 第一个边沿进行采样*/
    26. spi_struct.prescale = SPI_PSC_8; /*!< SPI prescaler factor 8分频*/
    27. spi_init(SPI0, &spi_struct);
    28. }

    w25qxx.h 

    1. #ifndef _W25QXX_SPI_H
    2. #define _W25QXX_SPI_H
    3. #include "gd32f10x.h"
    4. #include "w25qxx_ins.h"
    5. #include "gd32f10x_spi.h"
    6. #define W25QXX_ID_1 1
    7. #define W25QXX_SR_ID_1 1
    8. #define W25QXX_SR_ID_2 2
    9. #define W25QXX_SR_ID_3 3
    10. void w25qxx_init(void);
    11. void w25qxx_wait_busy(void);
    12. uint8_t w25qxx_read_sr(uint8_t sregister_id); // 读状态寄存器
    13. void w25qxx_read(uint8_t *p_buffer, uint32_t read_addr, uint16_t num_read_bytes);
    14. void w25qxx_write(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes);
    15. void w25qxx_write_nocheck(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes); //
    16. void w25qxx_write_page(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes); // page program
    17. void w25qxx_erase_sector(uint32_t sector_addr);
    18. void w25qxx_erase_chip(void);
    19. void w25qxx_write_enable(void);
    20. void w25qxx_write_disable(void);
    21. void w25qxx_power_down(void);
    22. void w25qxx_wake_up(void);
    23. void w25qxx_cs_enable(uint8_t cs_id);
    24. void w25qxx_cs_disable(uint8_t cs_id);
    25. uint8_t w25qxx_swap(uint8_t byte_to_send);
    26. #endif

    w25qxx.c

    1. #include "w25qxx.h"
    2. #include "spi.h"
    3. void w25qxx_init(void){
    4. // 使能外设时钟
    5. w25qxx_rcu_init();
    6. // IO口进行配置,使之复用为SPI0, PA4\PA5\PA6\PA7,NSS\SCK\MISO\MOSI
    7. w25qxx_io_init();
    8. // SPI0初始化
    9. w25qxx_spi_init();
    10. // SPI使能
    11. spi_enable(SPI0);
    12. }
    13. // 如果SR-1的BUSY位为1的话,一直等待,直到BUSY位为0,结束等待
    14. void w25qxx_wait_busy(void){
    15. while((w25qxx_read_sr(W25QXX_SR_ID_1) & 0x01) == 0x01){
    16. ;
    17. }
    18. }
    19. // 读状态寄存器
    20. uint8_t w25qxx_read_sr(uint8_t sregister_id){
    21. uint8_t command, result;
    22. switch(sregister_id){
    23. case W25QXX_SR_ID_1:
    24. command = W25QXX_READ_STATUS_REGISTER_1;
    25. break;
    26. case W25QXX_SR_ID_2:
    27. command = W25QXX_READ_STATUS_REGISTER_2;
    28. break;
    29. case W25QXX_SR_ID_3:
    30. command = W25QXX_READ_STATUS_REGISTER_3;
    31. break;
    32. default:
    33. command = W25QXX_READ_STATUS_REGISTER_1;
    34. break;
    35. }
    36. w25qxx_cs_enable(W25QXX_ID_1);
    37. w25qxx_swap(command);
    38. result = w25qxx_swap(0xFF);
    39. w25qxx_cs_disable(W25QXX_ID_1);
    40. return result;
    41. }
    42. // 读flash的数据
    43. // *p_buffer 读回的数据的存放位置
    44. void w25qxx_read(uint8_t *p_buffer, uint32_t read_addr, uint16_t num_read_bytes){
    45. uint16_t i;
    46. w25qxx_cs_enable(W25QXX_ID_1);
    47. w25qxx_swap(W25QXX_READ_DATA); //发送读数据的指令
    48. w25qxx_swap(read_addr >> 16); //发送24bit地址
    49. w25qxx_swap(read_addr >> 8);
    50. w25qxx_swap(read_addr);
    51. for(i=0; i < num_read_bytes; i++){
    52. p_buffer[i] = w25qxx_swap(0xFF);
    53. }
    54. w25qxx_cs_disable(W25QXX_ID_1);
    55. }
    56. //
    57. uint8_t W25QXX_Buffer[4096]; //用来存放从sector读出的bytes
    58. void w25qxx_write(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes){
    59. uint32_t sec_num;
    60. uint16_t sec_remain;
    61. uint16_t sec_off;
    62. uint16_t i;
    63. sec_num = write_addr / 4096; //要写入的位置处在第sec_num个扇区上
    64. sec_off = write_addr % 4096;
    65. sec_remain = 4096 - sec_off;
    66. if(num_write_bytes <= sec_remain){
    67. w25qxx_read(W25QXX_Buffer, sec_num * 4096, 4096); //扇区的数据读出来
    68. for(i = 0; i < sec_remain; i++){
    69. if(W25QXX_Buffer[i + sec_off] != 0xFF) //说明这个扇区的第i+sec_off位没有擦除
    70. break;
    71. }
    72. if(i < sec_remain){ // 扇区没有擦除
    73. w25qxx_erase_sector(sec_num * 4096);
    74. for(i = 0; i < sec_remain; i++){
    75. W25QXX_Buffer[i + sec_off] = p_buffer[i];
    76. }
    77. w25qxx_write_nocheck(W25QXX_Buffer, sec_num * 4096, 4096);
    78. }else{ // 扇区sec_remain部分是擦除过的
    79. w25qxx_write_nocheck(p_buffer, write_addr, num_write_bytes);
    80. }
    81. }else{
    82. w25qxx_read(W25QXX_Buffer, sec_num * 4096, 4096); //扇区的数据读出来
    83. for(i = 0; i < sec_remain; i++){
    84. if(W25QXX_Buffer[i + sec_off] != 0xFF) //说明这个扇区的第i+sec_off位没有擦除
    85. break;
    86. }
    87. if(i < sec_remain){ // 扇区没有擦除
    88. w25qxx_erase_sector(sec_num * 4096);
    89. for(i = 0; i < sec_remain; i++){
    90. W25QXX_Buffer[i + sec_off] = p_buffer[i];
    91. }
    92. w25qxx_write_nocheck(W25QXX_Buffer, sec_num * 4096, 4096);
    93. }else{ // 扇区sec_remain部分是擦除过的
    94. w25qxx_write_nocheck(p_buffer, write_addr, sec_remain);
    95. }
    96. write_addr += sec_remain;
    97. p_buffer += sec_remain;
    98. num_write_bytes -= sec_remain;
    99. w25qxx_write(p_buffer, write_addr, num_write_bytes);
    100. }
    101. //判断读出来的数据是否都为0xFF
    102. ;//扇区是否删除
    103. //判断是否跨页
    104. }
    105. // 调用之前先确保扇区删除
    106. void w25qxx_write_nocheck(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes){
    107. uint16_t page_remain = 256 - write_addr % 256;
    108. if(num_write_bytes <= page_remain){
    109. w25qxx_write_page(p_buffer, write_addr, num_write_bytes);
    110. }else{
    111. w25qxx_write_page(p_buffer, write_addr, page_remain);
    112. p_buffer += page_remain;
    113. write_addr += page_remain;
    114. num_write_bytes -= page_remain;
    115. w25qxx_write_nocheck(p_buffer, write_addr, num_write_bytes);
    116. }
    117. }
    118. // page program
    119. // 保证没有跨页写的前提下调用此函数往某个页上写内容
    120. void w25qxx_write_page(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes){
    121. uint16_t i;
    122. w25qxx_write_enable();
    123. w25qxx_cs_enable(W25QXX_ID_1);
    124. w25qxx_swap(W25QXX_PAGE_PROGRAM);
    125. w25qxx_swap(write_addr >> 16); //发送24bit地址
    126. w25qxx_swap(write_addr >> 8);
    127. w25qxx_swap(write_addr);
    128. for(i = 0; i < num_write_bytes; i++){
    129. w25qxx_swap(p_buffer[i]);
    130. }
    131. w25qxx_cs_disable(W25QXX_ID_1);
    132. w25qxx_wait_busy();
    133. }
    134. void w25qxx_erase_sector(uint32_t sector_addr){
    135. w25qxx_write_enable();
    136. w25qxx_cs_enable(W25QXX_ID_1);
    137. w25qxx_swap(W25QXX_SECTOR_ERASE_4KB);
    138. w25qxx_swap(sector_addr >> 16);
    139. w25qxx_swap(sector_addr >> 8);
    140. w25qxx_swap(sector_addr);
    141. w25qxx_cs_disable(W25QXX_ID_1);
    142. w25qxx_wait_busy();
    143. }
    144. void w25qxx_erase_chip(void){
    145. w25qxx_write_enable();
    146. w25qxx_cs_enable(W25QXX_ID_1);
    147. w25qxx_swap(W25QXX_CHIP_ERASE);
    148. w25qxx_cs_disable(W25QXX_ID_1);
    149. w25qxx_wait_busy();
    150. }
    151. void w25qxx_write_enable(void){
    152. w25qxx_cs_enable(W25QXX_ID_1);
    153. w25qxx_swap(W25QXX_WRITE_ENABLE);
    154. w25qxx_cs_disable(W25QXX_ID_1);
    155. }
    156. void w25qxx_write_disable(void){
    157. w25qxx_cs_enable(W25QXX_ID_1);
    158. w25qxx_swap(W25QXX_WRITE_DISABLE);
    159. w25qxx_cs_disable(W25QXX_ID_1);
    160. }
    161. // 低电量休眠
    162. void w25qxx_power_down(void){
    163. w25qxx_cs_enable(W25QXX_ID_1);
    164. w25qxx_swap(W25QXX_POWER_DOWN);
    165. w25qxx_cs_disable(W25QXX_ID_1);
    166. }
    167. // 唤醒
    168. void w25qxx_wake_up(void){
    169. w25qxx_cs_enable(W25QXX_ID_1);
    170. w25qxx_swap(W25QXX_RELEASE_POWER_DOWN_HPM_DEVICE_ID);
    171. w25qxx_cs_disable(W25QXX_ID_1);
    172. }
    173. /*
    174. brief:使能片选引脚cs
    175. cs_id: cs引脚的序号,即第几个w25qxx flash
    176. */
    177. void w25qxx_cs_enable(uint8_t cs_id){
    178. switch(cs_id){
    179. case W25QXX_ID_1:
    180. gpio_bit_reset(GPIOA, GPIO_PIN_4);
    181. break;
    182. default:
    183. break;
    184. }
    185. }
    186. void w25qxx_cs_disable(uint8_t cs_id){
    187. switch(cs_id){
    188. case W25QXX_ID_1:
    189. gpio_bit_set(GPIOA, GPIO_PIN_4);
    190. break;
    191. default:
    192. break;
    193. }
    194. }
    195. /*
    196. 主从数据交换
    197. */
    198. uint8_t w25qxx_swap(uint8_t byte_to_send){
    199. while(spi_i2s_flag_get(SPI0, SPI_FLAG_TBE) == RESET){ // 等待SPI发送缓冲器为空
    200. ;
    201. }
    202. spi_i2s_data_transmit(SPI0, byte_to_send); // 把数据放到发生缓冲器
    203. while(spi_i2s_flag_get(SPI0, SPI_FLAG_TRANS) == SET){ // 等待通信结束
    204. ;
    205. }
    206. while(spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE) == RESET){ // 等待SPI接收缓冲器非空
    207. ;
    208. }
    209. return spi_i2s_data_receive(SPI0); /* 把接收到的数据返回(从接收缓冲器里拿出) */
    210. }

  • 相关阅读:
    AtCoder abc148
    第七讲(二):双指针,指针运用
    《DREEAM Guiding Attention with Evidence for Improving Document-Level Relation Extraction》阅读笔记
    Elasticsearch之join关联查询
    前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— Web APIs(六)
    两个难搞的Java Error/Exception
    四面大厂自动化测试岗位,跳槽成功涨薪3倍
    leetcode单调栈系列
    wxpython:wx.grid 表格显示 Excel xlsx文件
    java毕业设计基于javaweb+mysql数据库实现的在线学习网站|在线课堂含论文+开题报告
  • 原文地址:https://blog.csdn.net/qq_41328470/article/details/133690947