• (STM32)从零开始的RT-Thread之旅--SPI驱动ST7735(3)使用DMA


    上一篇完成了ST7735驱动的移植,并已经可以通过SPI在屏幕上显示字符了,这一章会把SPI修改为DMA的传输方式。由于RTT对于STM32H7的SPI的DMA传输方式目前支持的并不好,这就让上一章裸机方式驱动屏幕派上了用场。我们可以先把SPI+DMA打通,然后再修改内核驱动框架。

    1.不使用驱动框架

    把上一章中开启使用RTT驱动框架中的SPI的宏定义注释掉:

    然后一定要在配置SPI前使能DMA的时钟:

    然后到 stm32h7xx_hal_msp.c 中SPI引脚初始化下面添加相应SPI的DMA配置:

    1. void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
    2. {
    3. GPIO_InitTypeDef GPIO_InitStruct = {0};
    4. RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
    5. if(hspi->Instance==SPI4)
    6. {
    7. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI4;
    8. PeriphClkInitStruct.Spi45ClockSelection = RCC_SPI45CLKSOURCE_D2PCLK1;
    9. if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    10. {
    11. Error_Handler();
    12. }
    13. __HAL_RCC_SPI4_CLK_ENABLE();
    14. __HAL_RCC_GPIOE_CLK_ENABLE();
    15. /**SPI4 GPIO Configuration
    16. PE12 ------> SPI4_SCK
    17. PE14 ------> SPI4_MOSI
    18. */
    19. GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_14;
    20. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    21. GPIO_InitStruct.Pull = GPIO_NOPULL;
    22. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    23. GPIO_InitStruct.Alternate = GPIO_AF5_SPI4;
    24. HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    25. HAL_GPIO_WritePin(GPIOE, GPIO_PIN_12, GPIO_PIN_SET);
    26. /* SPI4 DMA Init */
    27. /* SPI4_TX Init */
    28. hdma_spi4_tx.Instance = DMA1_Stream0;
    29. hdma_spi4_tx.Init.Request = DMA_REQUEST_SPI4_TX;
    30. hdma_spi4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    31. hdma_spi4_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    32. hdma_spi4_tx.Init.MemInc = DMA_MINC_ENABLE;
    33. hdma_spi4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    34. hdma_spi4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    35. hdma_spi4_tx.Init.Mode = DMA_NORMAL;
    36. hdma_spi4_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    37. hdma_spi4_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    38. hdma_spi4_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    39. hdma_spi4_tx.Init.MemBurst = DMA_MBURST_SINGLE;
    40. hdma_spi4_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;
    41. if (HAL_DMA_DeInit(&hdma_spi4_tx) != HAL_OK)
    42. {
    43. Error_Handler();
    44. }
    45. if (HAL_DMA_Init(&hdma_spi4_tx) != HAL_OK)
    46. {
    47. Error_Handler();
    48. }
    49. __HAL_LINKDMA(hspi,hdmatx,hdma_spi4_tx);
    50. HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 1, 0);
    51. HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
    52. HAL_NVIC_SetPriority(SPI4_IRQn, 1, 0);
    53. HAL_NVIC_EnableIRQ(SPI4_IRQn);
    54. }
    55. }

    这里注意外设和内存对齐:

    要和SPI前面配置的数据位数一致,我这里前面是:

    还有就是配置结尾要打开相关中断,这里我只使用发送

    此时配置基本已经完成,相关配置详细信息可以参考我文末附的几篇教程,都写得很好很详细。

    开启中断后,就必须要有中断函数,最最最开始的那一章我提到过,有关CubeMX生成的中断函数的那个.c我们没有加进整个工程里面,所以有些在CubeMX中添加了响应的中断,但是函数需要我们自己再添加进来,这里我添加的函数是:

    这里添加的函数需要根据SPI配置的是双工还是单工来选择,所有的回调如下:

    这里即使用的双工,也可以只配置一边是DMA,比如我使用的全双工,TX和RX都使用DMA,每次发送和接收只需调用 HAL_SPI_TransmitReceive_DMA ,它既包含发送又包含接收,那触发的中断肯定是 HAL_SPI_TxRxCpltCallback 了。这些由名字很好推断不再赘述。

    剩下还需要修改的就是发送的数据所在内存。因为DMA1和2不能访问RTT Studio默认的RAM,默认RAM是从0x2000 0000开始,大小为128K的空间。像我使用的STM32H743VIT6这块芯片,有1M的RAM,但是实际上是分散为几块的,这里修改链接文件:

    这里不要直接双击,而要选择打开方式:

    这里根据芯片手册,添加剩余的RAM:

    在.bss段后添加(ld脚本不熟悉可以看我很早之前写的关于GCC下stm32的ld文件的解析):

    这里我们就定义了一个名叫.spi4.txbuf的段,然后把这个段的全部数据放在RAM4的地址范围内。接下来,我们只需要把想要通过SPI的DMA传输的数据保存在这个段内就可以了。在GCC编译器下可以使用__attribute__ 关键字:

    uint8_t ram4_spi4_buf[RAM4_SPI4_BUF] __attribute__((section(".spi4.txbuf")));

    然后在map文件中查看是否定义成功:

    可以看到ram4_spi4_buf这个数组使用了0x3800 0000~0x3800 0400这段地址空间。 这段空间是可以被DMA1访问的。

    使用DMA之后最后还需要注意cache设置,这里可能出现数据一致性问题。什么是数据一致性问题?

    当MCU运行频率过高时,代码执行效率就会收到RAM频率影响,因为很多时候代码执行需要把寄存器的数据加载到RAM中或者从RAM中读取到寄存器中,但是RAM的频率比MCU低时,MCU必须等待RAM的读写操作,大大降低了MCU的执行效率。这时候出现了一种方案,在RAM和MCU之间多加一块缓存,就是cache,它的读写很快能够跟上MCU,每次MCU需要RAM数据时,会先取一部分数据保存在cache中,如果MCU需要的数据已经在cache中有了,那就直接使用cache中的即可。这就会出现一个问题,我们DMA一定使用的是RAM到外设,而MCU有可能使用的cache中的缓存数据,这两个数据是每时每刻都一样的吗?肯定不是,MCU<-CACHE<-RAM,这cache和RAM之间数据肯定是需要时机和一定时间同步的,说到底,快只是快在MCU和cache已经保存有MCU需要的RAM数据的情况,当MCU需要的数据cache里面没有时,cache一样需要慢慢等待RAM的数据备份到cache里。当MCU需要的数据在cache中存在时,即使DMA改变了RAM中相应的数据,MCU并不知道,它还是会使用cache中的缓存数据,此时就会出现一致性问题。更多详细内容可以参考文末附的文章。

    要解决这个问题,我们可以通过配置MPU来关闭某块内存的cache功能。 在 hw_board_init 中可以看到,当有定义使能cache的函数时,会自动调用这些函数:

    在这之前我们配置RAM4的MPU:

    完整函数如下:

    1. /**
    2. * This function will initial STM32 board.
    3. */
    4. void hw_board_init(char *clock_src, int32_t clock_src_freq, int32_t clock_target_freq)
    5. {
    6. extern void rt_hw_systick_init(void);
    7. extern void clk_init(char *clk_source, int source_freq, int target_freq);
    8. MPU_Region_InitTypeDef MPU_InitStruct;
    9. HAL_MPU_Disable();
    10. MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    11. MPU_InitStruct.BaseAddress = 0x38000000;
    12. MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
    13. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    14. MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
    15. MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
    16. MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
    17. MPU_InitStruct.Number = MPU_REGION_NUMBER2;
    18. MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
    19. MPU_InitStruct.SubRegionDisable = 0x00;
    20. MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
    21. HAL_MPU_ConfigRegion(&MPU_InitStruct);
    22. HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    23. #ifdef SCB_EnableICache
    24. /* Enable I-Cache---------------------------------------------------------*/
    25. SCB_EnableICache();
    26. #endif
    27. #ifdef SCB_EnableDCache
    28. /* Enable D-Cache---------------------------------------------------------*/
    29. SCB_EnableDCache();
    30. #endif
    31. /* HAL_Init() function is called at the beginning of the program */
    32. HAL_Init();
    33. /* enable interrupt */
    34. __set_PRIMASK(0);
    35. /* System clock initialization */
    36. clk_init(clock_src, clock_src_freq, clock_target_freq);
    37. /* disbale interrupt */
    38. __set_PRIMASK(1);
    39. rt_hw_systick_init();
    40. /* Pin driver initialization is open by default */
    41. #ifdef RT_USING_PIN
    42. extern int rt_hw_pin_init(void);
    43. rt_hw_pin_init();
    44. #endif
    45. /* USART driver initialization is open by default */
    46. #ifdef RT_USING_SERIAL
    47. extern int rt_hw_usart_init(void);
    48. rt_hw_usart_init();
    49. #endif
    50. }

    如果你使用的不是RAM4,可以只简单修改起始地址和大小。

    最后回到mspi.c中修改发送接收函数。其中 mspi_send_reg 与 mspi_read_reg 函数我决定不再修改,因为读写reg通常都是一个字节,而使用DMA需要先把我们需要传输的数据copy到 ram4_spi4_buf 这个分配在RAM4的buf中,再使用SPI_DMA传输函数发送,感觉提升并不明显。所以只修改了 mspi_send_data 函数,它通常用来发送需要显示的大量数据,修改如下:

    可以看到主要就是先把数据copy到ram4_spi4_buf里,然后用专用于DMA发送的HAL_SPI_Transmit_DMA函数传输。这里DMA传输用的是阻塞等待,发送前把一个全局状态置为busy,等待发送完成后的回调函数里再把状态置idle:

    这个函数前面讲需要添加的中断里已经见到过了 ,至于状态定义,如下:

    至此所有修改已经完成,正常已经可以测试DMA是否使用正常。 

    2.使用驱动框架

    首先在board.h中添加使用SPI的TX的DMA的宏定义:

    此时编译会发现有好几个错误:

    那是因为内核驱动框架根本没有适配STM32H7,我们先修改drv_dma.h中对于DMA相关结构体定义:

    然后再修改dma_config.h中对于SPI4的配置:

    这里可以根据上面不使用内核驱动框架的时候的 stm32h7xx_hal_msp.c 中的配置修改,反正使用DMA1还是2的哪个流都可以。注意H7没有channel,但有request。 然后配置完成后,就可以把 stm32h7xx_hal_msp.c 中相关配置给注释掉了。这些配置实际应用是在 spi_config.h 中:

    有关这些配置的使用都在drv_spi.c中的rt_hw_spi_bus_init函数里,对照上面注释掉的配置,修改如下,具体参数请根据实际需求配置:

    注意drv_spi.c中是有SPI4_IRQHandler和SPI4_DMA_TX_IRQHandler中断函数的,所以请注意mspi.c中的中断函数会和它重复定义,请把mspi.c中的中断函数加到USE_RTT_SPI宏定义else代码块内。

    添加完这些正常应该可以使用了,但是实际使用会发现代码卡在,返回状态一直是HAL_SPI_STATE_BUSY_TX

    调用它的上层函数是spixfer

    在这个函数中可以看到当只有发送buf,且使能了TX发送DMA时调用的是:

    这个函数中正常发送会把hspi->State 赋值为HAL_SPI_STATE_BUSY_TX:

    而这个状态在发送完成后没有被修改回来就会一直处于busy状态,它是在哪里被修改回来呢?

    答案是 HAL_SPI_IRQHandler 函数 。而RTT内核默认是没有使用SPI的中断传输方式的,这可以从 spixfer 这个函数中调用的底层发送接收函数得知,所以当开启DMA后,又没有开启SPI中断就会出现这种情况。可以在中断函数内打上断点测试:

    修改方式也很简单,假如没有使用RTT,可以直接在DMA使能中断的地方直接添加SPI中断配置与使能,这里我介绍一下在使用RTT下如何修改。在drv_spi.h中修改结构体:

    在spi_config.h中添加:

    在 drv_spi.c 中初始化函数 stm32_spi_init 的最后使能DMA中断处添加:

    至此框架修改完毕,最后在上一章的基础上修改我们的发送接收函数,因为我们调用的是驱动框架的接口,不修改接口的情况下没办法像不使用框架那样,把普通SPI传输和SPI-DMA传输混合使用,这里只能都使用DMA传输,修改如下:

    其他几个函数附在文章最后,仅供参考。 

    mspi.c 

    1. /*
    2. * Copyright (c) 2006-2021, RT-Thread Development Team
    3. *
    4. * SPDX-License-Identifier: Apache-2.0
    5. *
    6. * Change Logs:
    7. * Date Author Notes
    8. * 2022-11-14 cx the first version
    9. */
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #include "st7735_reg.h"
    17. #include "st7735.h"
    18. enum
    19. {
    20. idle,
    21. busy,
    22. error
    23. };
    24. volatile uint32_t spi4_dma_status = idle;
    25. uint8_t ram4_spi4_buf[RAM4_SPI4_BUF] __attribute__((section(".spi4.txbuf")));
    26. #define USE_RTT_SPI
    27. SPI_HandleTypeDef *spi_handle;
    28. #ifdef USE_RTT_SPI
    29. static struct rt_spi_device *spi_lcd;
    30. #define LCD_RD_HIGH rt_pin_write(SPI_RD_PIN_NUM, PIN_HIGH)
    31. #define LCD_RD_LOW rt_pin_write(SPI_RD_PIN_NUM, PIN_LOW)
    32. #else
    33. extern SPI_HandleTypeDef hspi4;
    34. #define SPI_Drv (&hspi4)
    35. #define LCD_CS_HIGH HAL_GPIO_WritePin(GPIOE,GPIO_PIN_11,GPIO_PIN_SET)
    36. #define LCD_CS_LOW HAL_GPIO_WritePin(GPIOE,GPIO_PIN_11,GPIO_PIN_RESET)
    37. #define LCD_RD_HIGH HAL_GPIO_WritePin(GPIOE,GPIO_PIN_13,GPIO_PIN_SET)
    38. #define LCD_RD_LOW HAL_GPIO_WritePin(GPIOE,GPIO_PIN_13,GPIO_PIN_RESET)
    39. #endif
    40. #ifdef USE_RTT_SPI
    41. int32_t mspi_send_reg(uint8_t reg,uint8_t *data,uint32_t len)
    42. {
    43. struct rt_spi_message msg;
    44. uint32_t remsg = RT_NULL;
    45. ram4_spi4_buf[0] = reg;
    46. memcpy(&ram4_spi4_buf[1],data,len);
    47. msg.send_buf = &ram4_spi4_buf[0];
    48. msg.recv_buf = RT_NULL;
    49. msg.length = 1;
    50. msg.cs_take = 1;
    51. msg.cs_release = 0;
    52. msg.next = RT_NULL;
    53. LCD_RD_LOW;
    54. remsg = (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
    55. LCD_RD_HIGH;
    56. if(len > 0)
    57. {
    58. msg.send_buf = &ram4_spi4_buf[1];
    59. msg.recv_buf = RT_NULL;
    60. msg.length = len;
    61. msg.cs_take = 0;
    62. msg.cs_release = 1;
    63. msg.next = RT_NULL;
    64. remsg += (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
    65. }
    66. if(remsg!=RT_NULL)
    67. return -1;
    68. else
    69. return 0;
    70. }
    71. int32_t mspi_read_reg(uint8_t reg,uint8_t *data)
    72. {
    73. struct rt_spi_message msg;
    74. uint32_t remsg = RT_NULL;
    75. ram4_spi4_buf[0] = reg;
    76. msg.send_buf = &ram4_spi4_buf[0];
    77. msg.recv_buf = RT_NULL;
    78. msg.length = 1;
    79. msg.cs_take = 1;
    80. msg.cs_release = 0;
    81. msg.next = RT_NULL;
    82. LCD_RD_LOW;
    83. remsg = (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
    84. LCD_RD_HIGH;
    85. if(remsg == 0)
    86. {
    87. msg.send_buf = RT_NULL;
    88. msg.recv_buf = data;
    89. msg.length = 1;
    90. msg.cs_take = 0;
    91. msg.cs_release = 1;
    92. msg.next = RT_NULL;
    93. remsg += (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
    94. }
    95. if(remsg!=RT_NULL)
    96. return -1;
    97. else
    98. return 0;
    99. }
    100. int32_t mspi_send_data(uint8_t *data,uint32_t len)
    101. {
    102. struct rt_spi_message msg;
    103. memcpy(ram4_spi4_buf,data,len);
    104. msg.send_buf = ram4_spi4_buf;
    105. msg.recv_buf = RT_NULL;
    106. msg.length = len;
    107. msg.cs_take = 1;
    108. msg.cs_release = 1;
    109. msg.next = RT_NULL;
    110. return (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
    111. }
    112. int32_t mspi_read_data(uint8_t *data,uint32_t len)
    113. {
    114. struct rt_spi_message msg;
    115. msg.send_buf = RT_NULL;
    116. msg.recv_buf = data;
    117. msg.length = len;
    118. msg.cs_take = 1;
    119. msg.cs_release = 1;
    120. msg.next = RT_NULL;
    121. return (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
    122. }
    123. #else
    124. int32_t mspi_send_reg(uint8_t reg,uint8_t *data,uint32_t len)
    125. {
    126. int32_t result;
    127. LCD_CS_LOW;
    128. LCD_RD_LOW;
    129. result = HAL_SPI_Transmit(SPI_Drv,®,1,100);
    130. LCD_RD_HIGH;
    131. if(len > 0)
    132. result += HAL_SPI_Transmit(SPI_Drv,data,len,500);
    133. LCD_CS_HIGH;
    134. if(result>0){
    135. result = -1;}
    136. else{
    137. result = 0;}
    138. return result;
    139. }
    140. int32_t mspi_read_reg(uint8_t reg,uint8_t *data)
    141. {
    142. int32_t result;
    143. LCD_CS_LOW;
    144. LCD_RD_LOW;
    145. result = HAL_SPI_Transmit(SPI_Drv,®,1,100);
    146. LCD_RD_HIGH;
    147. result += HAL_SPI_Receive(SPI_Drv,data,1,500);
    148. LCD_CS_HIGH;
    149. if(result>0){
    150. result = -1;}
    151. else{
    152. result = 0;}
    153. return result;
    154. }
    155. int32_t mspi_send_data(uint8_t *data,uint32_t len)
    156. {
    157. int32_t result;
    158. memcpy(ram4_spi4_buf,data,len);
    159. LCD_CS_LOW;
    160. // result =HAL_SPI_Transmit(SPI_Drv,data,len,100);
    161. spi4_dma_status = busy;
    162. if(HAL_SPI_Transmit_DMA(SPI_Drv,ram4_spi4_buf,len) != HAL_OK)
    163. {
    164. rt_kprintf("SPI4 DMA trans err\n");
    165. return -1;
    166. }
    167. else
    168. {
    169. while(spi4_dma_status==busy);
    170. result = 0;
    171. }
    172. LCD_CS_HIGH;
    173. if(result>0)
    174. {
    175. result = -1;
    176. }
    177. else
    178. {
    179. result = 0;
    180. }
    181. return result;
    182. }
    183. int32_t mspi_read_data(uint8_t *data,uint32_t len)
    184. {
    185. int32_t result;
    186. LCD_CS_LOW;
    187. result = HAL_SPI_Receive(SPI_Drv,data,len,500);
    188. LCD_CS_HIGH;
    189. if(result>0)
    190. {
    191. result = -1;
    192. }
    193. else
    194. {
    195. result = 0;
    196. }
    197. return result;
    198. }
    199. extern DMA_HandleTypeDef hdma_spi4_tx;
    200. void DMA1_Stream0_IRQHandler(void)
    201. {
    202. HAL_DMA_IRQHandler(&hdma_spi4_tx);
    203. }
    204. void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
    205. {
    206. spi4_dma_status = idle;
    207. }
    208. void SPI4_IRQHandler(void)
    209. {
    210. HAL_SPI_IRQHandler(&hspi4);
    211. }
    212. void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
    213. {
    214. spi4_dma_status = error;
    215. }
    216. #endif
    217. int32_t mspi_get_tick(void)
    218. {
    219. return HAL_GetTick();
    220. }
    221. void mspi_rw_gpio_init(void)
    222. {
    223. #ifdef USE_RTT_SPI
    224. rt_pin_mode(SPI_RD_PIN_NUM, PIN_MODE_OUTPUT);
    225. rt_pin_write(SPI_RD_PIN_NUM, PIN_HIGH);
    226. #else
    227. GPIO_InitTypeDef GPIO_InitStruct = {0};
    228. __HAL_RCC_GPIOE_CLK_ENABLE();
    229. HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11|GPIO_PIN_13, GPIO_PIN_SET);
    230. GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_13;
    231. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    232. GPIO_InitStruct.Pull = GPIO_NOPULL;
    233. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    234. HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    235. #endif
    236. }
    237. void mspi_init(void)
    238. {
    239. mspi_rw_gpio_init();
    240. #ifdef USE_RTT_SPI
    241. struct rt_spi_configuration cfg;
    242. rt_hw_spi_device_attach("spi4", "spi40", GPIOE, GPIO_PIN_11);
    243. spi_lcd = (struct rt_spi_device *)rt_device_find("spi40");
    244. if(!spi_lcd)
    245. {
    246. rt_kprintf("spi40 can't find\n");
    247. }
    248. else
    249. {
    250. spi_lcd->bus->owner = spi_lcd;
    251. cfg.data_width = 8;
    252. cfg.mode = RT_SPI_MASTER | RT_SPI_3WIRE | RT_SPI_MODE_0 | RT_SPI_MSB;
    253. cfg.max_hz = 12.5 * 1000 * 1000;
    254. rt_spi_configure(spi_lcd, &cfg);
    255. }
    256. //也可以初始化使用框架提供的函数,发送接收使用HAL库函数,操作对象就是下面的 spi_handle
    257. // struct stm32_spi *spi_drv = rt_container_of(spi_lcd->bus, struct stm32_spi, spi_bus);
    258. // spi_handle = &spi_drv->handle;
    259. #else
    260. __HAL_RCC_DMA1_CLK_ENABLE();
    261. hspi4.Instance = SPI4;
    262. hspi4.Init.Mode = SPI_MODE_MASTER;
    263. hspi4.Init.Direction = SPI_DIRECTION_1LINE;
    264. hspi4.Init.DataSize = SPI_DATASIZE_8BIT;
    265. hspi4.Init.CLKPolarity = SPI_POLARITY_LOW;
    266. hspi4.Init.CLKPhase = SPI_PHASE_1EDGE;
    267. hspi4.Init.NSS = SPI_NSS_SOFT;
    268. hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
    269. hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB;
    270. hspi4.Init.TIMode = SPI_TIMODE_DISABLE;
    271. hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    272. hspi4.Init.CRCPolynomial = 0x0;
    273. hspi4.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
    274. hspi4.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
    275. hspi4.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
    276. hspi4.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
    277. hspi4.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
    278. hspi4.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
    279. hspi4.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
    280. hspi4.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
    281. hspi4.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
    282. hspi4.Init.IOSwap = SPI_IO_SWAP_DISABLE;
    283. if (HAL_SPI_Init(&hspi4) != HAL_OK)
    284. {
    285. rt_kprintf("ERROR\n");
    286. }
    287. #endif
    288. }

    参考文章:

    【STM32H7教程】第72章 STM32H7的SPI总线基础知识和HAL库API

    【STM32H7教程】第94章 STM32H7的SPI总线应用之双机通信(DMA方式)

     STM32 H7 配置SPI&DMA小结

    STM32H7---高速缓存Cache(一)

  • 相关阅读:
    生成式AI的新战场:逻辑推断与推理计算
    echarts 柱状图加背景加渐变
    剑指offer 16. 在O(1)时间删除链表结点
    leetcode 13
    论人类下一代语言的可能—6.3.2等价-替换原理
    dbeaver 连接HANA数据库不同租户模式
    linux 配置安装node.js
    135.如何进行离线计算-1
    无线定位中TDOA时延估计算法matlab仿真
    游览器搜索最近1年的文章
  • 原文地址:https://blog.csdn.net/qwe5959798/article/details/127968974