[讨论] 关于STM32F03X串口空闲中断+DMA的应用

懒猫爱飞   2018-11-29 11:34 楼主
此内容由EEWORLD论坛网友懒猫爱飞原创,如需转载或用于商业用途需征得作者同意并注明出处
关于STM32F03X串口空闲中断+DMA的应用.pdf (842.89 KB)
(下载次数: 147, 2018-11-29 11:33 上传)
关于STM32F03X串口空闲中断+DMA的应用
AuthorMark Xu
前言
偶然得到一块STM32F030的开发板,业余时间手痒痒,写点小程序练练手,一般调试时,用串口比较多,结合网上的资料,整理了一下串口中断与DMA的结合方式,测试了一下,用来起还不错,发送与接收都是用的是DMA方式,当然如果有其它紧急任务需要用到DMA可以让你其它任务,这里暂时不讨论其它。
概述
为了测试方便,暂时没有用到硬件流摈,也没有用到循环队列存储数据,只做了一下简单的测试,STM32F03X某些型号有两个串口,而我这块开发板正好是有两个串口,故两个串口都把它用上了^_^
因为属于简单的应用,说白了就是串口初始化及一些简单的小程序,而又因为是事后结合代码作的总结,故实验结果没有贴上来,如果日后有机会,可以再补齐。关于串口的API主要包括两个源文件Dsk_Usart.c及Dsk_Usart.h
Dsk_Usart.c源文件
该源文件中主要包括的是串口的配置及一些简单的API,关于串口的配置主要涉及下面几个:
// 最多可以接收到字节数
#define USART_RXD_BUF_SIZE (64)
// 最多可以发送64字节
#define USART_TXD_BUF_SIZE (64)
// 最多可以打印64个字符
#define DEBUG_PRINT_BUF_LEN (64)
// 串口2缓存大小
#define USART2_RX_BUF_SIZE (32)
#define USART2_TX_BUF_SIZE (32)
// 发送方式配置
// 查询方式/DMA中断方式
#define UART_SNED_REQ (0)
#define UART_SEND_DMA (1)
#define USART1_RXD_MODE UART_SEND_DMA
#define USART2_RXD_MODE UART_SNED_REQ
接收缓存与发射缓存根据MCU的RAM而定吧,这个没有什么限制,只要够用就好。发送方式一般用查询方式或用DMA方式,查询方式对于发送少量数据没的应用没有什么影响,如果单次发送的数据量比较大,最好用DMA配合中断的方式,这样不浪费MCU的资源而且不会造成信息阻塞。
该源文件中关于USART的函数有以下几个:
  • void Uart1_Gpio_Init(void) - 端口初始化的,注意是否进行了重新映射
  • void Uart1_DMA_Init(void) - DMA通道初始化,注意是否进行了重新映射
  • void UART1_Api_Init(void) - 串口1初始化函数
  • void USART1_IRQHandler(void) –串口1中断函数,函数名要与启动文件中的名称一致
  • void Uart1_Data_Process(pRevprocessDat) – 算是接收到的数据
  • void Uart1_Send_Bytes(uint8_t*dat,uint16_t len) –通过串口1发送数据
  • int Debug_Printf(const char*fmt, ...) – 把串口1发送函数包装成了类似printf的函数,方便调试打印
  • void Uart2_Gpio_Init(void) – 与串口11类似
  • void Uart2_DMA_Init(void) –DMA初始化,与串口1类似
  • void UART2_Api_Init(void) – 串口2初始化化,与串口1类似
  • void USART2_IRQHandler(void) –串口2中断,与串口1类似
  • void Uart2_Data_Process(pRevprocessDat) – 处理接收数据
  • void Uart2_Send_Bytes(uint8_t*dat,uint16_t len) – 串口2发送数据
串口1与串口2的初始化函数及发送接收函数类似,本来可以整理成数组方式,比如把端口,DMA通道等加到相应的数组中,就可以同时初始化,可以节省一些代码空间,代码也可以更整洁一些。由于我开始只做了串口1的测试,没有做串口2的实验,后面想着既然实验就把两个都测试了,于是为了偷懒,直接复制了一份代码,这个不是好习惯,当然这样也代码也更清楚。好吧,不找理由,下次如果项目上用到,直接整合到一块^_^
下面是Dsk_Usart.c的源文件,我用的是3.5版本的库文件,如果您与我的版本不一样,注意修改一下寄存器或修改相关调用的库函数。
  1. /*********************************************************************************
  2. * Copyright (C) 2000-2018 Mark Xu WorkStudio,All Rights Reserved.
  3. *
  4. * File : Dsk_Uart.c
  5. *
  6. * Brief : Uart api Module
  7. *
  8. * Author :Mark Xu
  9. *
  10. * Remark :None
  11. ---------------------------------------------------------------------------------
  12. * Modify Content: NULL
  13. *
  14. * Modify Date : xxxx/xx/xx/ xx:xx
  15. *
  16. * Modify staff :xx xx
  17. **********************************************************************************/
  18. #define DSKUART_IMPLATION
  19. /*********************************************************************************
  20. * Include
  21. **********************************************************************************/
  22. #include <stdio.h>
  23. #include <stdint.h>
  24. #include <string.h>
  25. #include <stdarg.h> // 使用可变参数
  26. #include<stdlib.h> // 动态内存分配
  27. #include"stm32f0xx_Lib.h"
  28. #include"Dsk_Lib.h"
  29. /*********************************************************************************
  30. * Macros
  31. **********************************************************************************/
  32. // 最多可以接收到字节数
  33. #defineUSART_RXD_BUF_SIZE (64)
  34. // 最多可以发送64字节
  35. #defineUSART_TXD_BUF_SIZE (64)
  36. // 最多可以打印128个字符
  37. #defineDEBUG_PRINT_BUF_LEN (64)
  38. // 串口2缓存大小
  39. #defineUSART2_RX_BUF_SIZE (32)
  40. #defineUSART2_TX_BUF_SIZE (32)
  41. // 发送方式配置
  42. // 查询方式/DMA中断方式
  43. #define UART_SNED_REQ (0)
  44. #define UART_SEND_DMA (1)
  45. #define USART1_RXD_MODE UART_SEND_DMA
  46. #define USART2_RXD_MODE UART_SNED_REQ
  47. /*********************************************************************************
  48. * Variables
  49. **********************************************************************************/
  50. // The receive data buffer
  51. static uint8_trev_buff[USART_RXD_BUF_SIZE] = {0};
  52. static uint8_tdatBuff[USART_RXD_BUF_SIZE] = {0};
  53. static uint8_trevUart2[USART_RXD_BUF_SIZE] = {0};
  54. static uint8_tdatUart2Buff[USART_RXD_BUF_SIZE] = {0};
  55. static uint8_t sendBuff[DEBUG_PRINT_BUF_LEN]= {0};
  56. static uint8_tsend2Buff[USART2_TX_BUF_SIZE] = {0};
  57. // TRUE - been receiveddata FALSE - no data received
  58. static uint8_t revFlag = FALSE;
  59. static uint8_t revUart2Flag= FALSE;
  60. /*********************************************************************************
  61. * Name : Uart1_Gpio_Init
  62. *
  63. * Brief : Initialized the uart1 port.
  64. *
  65. * Input : None
  66. *
  67. * Output : None
  68. *
  69. * Remark :PA9-TX,PA10-RX,banudrate=9600
  70. ***********************************************************************************/
  71. void Uart1_Gpio_Init(void)
  72. {
  73. GPIO_InitTypeDef GPIO_InitStructure;
  74. USART_InitTypeDef USART_InitStructure;
  75. NVIC_InitTypeDef NVIC_InitStructure;
  76. // 开启串口时钟及端口时钟
  77. RCC_APB2PeriphClockCmd(USART1_RCC, ENABLE);
  78. RCC_AHBPeriphClockCmd(USART1_PORT_RCC,ENABLE);
  79. GPIO_PinAFConfig(USART1_MAP_PORT,UART1_TXD_PINSOURCE, UART1_AF_TXD);
  80. GPIO_PinAFConfig(USART1_MAP_PORT,UART1_RXD_PINSOURCE, UART1_AF_RXD);
  81. // 配置TX端口
  82. GPIO_InitStructure.GPIO_Pin = (UART1_TXD_PIN | UART1_RXD_PIN);
  83. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  84. GPIO_InitStructure.GPIO_OType =GPIO_OType_PP;
  85. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  86. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  87. GPIO_Init(USART1_MAP_PORT, &GPIO_InitStructure);
  88. // 配置串口模式
  89. USART_InitStructure.USART_BaudRate = USART1_BAUDRATE;
  90. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  91. USART_InitStructure.USART_StopBits = USART_Parity_No;
  92. USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;
  93. USART_InitStructure.USART_Mode = (USART_Mode_Rx |USART_Mode_Tx);
  94. USART_Init(USART1,&USART_InitStructure);
  95. // TXE发送中断,TC传输完成中断,RXNE接收中断,PE奇偶错误中断,可以是多个
  96. USART_ITConfig(USART1,USART_IT_TC,DISABLE);
  97. // 设置总线空闲产生中断
  98. USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
  99. // 必须先清除IDLE中断,否则会一直进IDLE中断
  100. USART1->ICR |= 1<<4;
  101. USART_ITConfig(USART1, USART_IT_RXNE,DISABLE);
  102. // 开启串口DMA接收
  103. USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
  104. // 开启串口1
  105. USART_Cmd(USART1,ENABLE);
  106. // 中断配置
  107. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  108. NVIC_InitStructure.NVIC_IRQChannelPriority= 2;
  109. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  110. NVIC_Init(&NVIC_InitStructure);
  111. }
  112. /*********************************************************************************
  113. * Name : Uart1_DMA_Init
  114. *
  115. * Brief : Initialized the uart1 DMA.
  116. *
  117. * Input : None
  118. *
  119. * Output : None
  120. *
  121. * Remark : None
  122. ***********************************************************************************/
  123. void Uart1_DMA_Init(void)
  124. {
  125. DMA_InitTypeDef DMA_Initstruct;
  126. // 开启DMA时钟
  127. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
  128. //DMA1通道3配置
  129. DMA_DeInit(USART1_RXD_MA_CHANNEL);
  130. // DMA配置
  131. // 外设地址
  132. DMA_Initstruct.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->RDR);
  133. // 内存地址
  134. DMA_Initstruct.DMA_MemoryBaseAddr =(uint32_t)rev_buff;
  135. // DMA传输方向单向
  136. DMA_Initstruct.DMA_DIR =DMA_DIR_PeripheralSRC;
  137. // 设置DMA在传输时缓冲区的长度
  138. DMA_Initstruct.DMA_BufferSize =USART_RXD_BUF_SIZE;
  139. // 设置DMA的外设递增模式,一个外设
  140. DMA_Initstruct.DMA_PeripheralInc =DMA_PeripheralInc_Disable;
  141. // 设置DMA的内存递增模式
  142. DMA_Initstruct.DMA_MemoryInc =DMA_MemoryInc_Enable;
  143. // 外设数据字长
  144. DMA_Initstruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  145. // 内存数据字长
  146. DMA_Initstruct.DMA_MemoryDataSize =DMA_MemoryDataSize_Byte;
  147. // 设置DMA的传输模式
  148. DMA_Initstruct.DMA_Mode =DMA_Mode_Normal;
  149. // 设置DMA的优先级别
  150. DMA_Initstruct.DMA_Priority = DMA_Priority_High; //DMA_Priority_VeryHigh;
  151. // 设置DMA的2个memory中的变量互相访问
  152. DMA_Initstruct.DMA_M2M =DMA_M2M_Disable;
  153. DMA_Init(USART1_RXD_MA_CHANNEL,&DMA_Initstruct);
  154. // USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
  155. // 开启DMA
  156. DMA_Cmd(USART1_RXD_MA_CHANNEL,ENABLE);
  157. #if( USART1_RXD_MODE ==UART_SEND_DMA )
  158. // DMA1通道TXD配置
  159. DMA_DeInit(USART1_TXD_MA_CHANNEL);
  160. // 外设地址,串口1
  161. DMA_Initstruct.DMA_PeripheralBaseAddr =(uint32_t)(&USART1->TDR);
  162. // 发送内存地址
  163. DMA_Initstruct.DMA_MemoryBaseAddr = (uint32_t)sendBuff;
  164. // 外设为传送数据目的地,即发送数据
  165. DMA_Initstruct.DMA_DIR =DMA_DIR_PeripheralDST;
  166. // 发送长度为0,即未有快递需要发送
  167. DMA_Initstruct.DMA_BufferSize = 0;
  168. // 初始化
  169. DMA_Init(USART1_TXD_MA_CHANNEL, &DMA_Initstruct);
  170. // 使能串口发送完成中断
  171. USART_ITConfig(USART1, USART_IT_TC,ENABLE);
  172. DMA_Cmd(USART1_TXD_MA_CHANNEL,ENABLE);
  173. // 使能DMA串口发送和接受请求
  174. USART_DMACmd(USART1,(USART_DMAReq_Tx|USART_DMAReq_Rx), ENABLE);
  175. #endif /* USART1_RXD_MODE */
  176. }
  177. /*********************************************************************************
  178. * Name : UART1_Api_Init
  179. *
  180. * Brief : Initialized the uart1
  181. *
  182. * Input : None
  183. *
  184. * Output : None
  185. *
  186. * Remark : None
  187. ***********************************************************************************/
  188. void UART1_Api_Init(void)
  189. {
  190. Uart1_Gpio_Init();
  191. Uart1_DMA_Init();
  192. }
  193. /*********************************************************************************
  194. * Name : USART1_IRQHandler
  195. *
  196. * Brief : USART1 interrupt process handle
  197. *
  198. * Input : None
  199. *
  200. * Output : None
  201. *
  202. * Remark : None
  203. ***********************************************************************************/
  204. void USART1_IRQHandler(void)
  205. {
  206. uint8_t len = 0;
  207. if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
  208. {
  209. USART1->ICR |= 1<<4; // 清除中断
  210. DMA_Cmd(USART1_RXD_MA_CHANNEL,DISABLE);
  211. len =USART_RXD_BUF_SIZE - DMA_GetCurrDataCounter(USART1_RXD_MA_CHANNEL);
  212. if(len >USART_RXD_BUF_SIZE)
  213. len= USART_RXD_BUF_SIZE;
  214. memcpy(datBuff,rev_buff,len);
  215. // 重新设置接收数据个数
  216. USART1_RXD_MA_CHANNEL->CNDTR =USART_RXD_BUF_SIZE;
  217. // 开启DMA
  218. DMA_Cmd(USART1_RXD_MA_CHANNEL,ENABLE);
  219. revFlag = TRUE;
  220. }
  221. #if( USART1_RXD_MODE ==UART_SEND_DMA )
  222. // 全部数据发送完成,产生该标记
  223. if(USART_GetITStatus(USART1,USART_IT_TC)!=RESET)
  224. {
  225. // 清除完成标记
  226. USART_ClearITPendingBit(USART1,USART_IT_TC);
  227. DMA_Cmd(USART1_TXD_MA_CHANNEL, DISABLE);
  228. // 清除数据长度
  229. USART1_TXD_MA_CHANNEL->CNDTR = 0;
  230. DMA_Cmd(USART1_TXD_MA_CHANNEL, ENABLE);
  231. }
  232. #endif /* USART1_RXD_MODE */
  233. }
  234. /*********************************************************************************
  235. * Name : Uart1_Data_Process
  236. *
  237. * Brief : process the received data
  238. *
  239. * Input : processDat - the process function point
  240. *
  241. * Output : None
  242. *
  243. * Remark : None
  244. ***********************************************************************************/
  245. void Uart1_Data_Process(pRevprocessDat)
  246. {
  247. if(!revFlag)
  248. return;
  249. revFlag = FALSE;
  250. processDat(datBuff);
  251. }
  252. /*********************************************************************************
  253. * Name : Uart1_Send_Bytes
  254. *
  255. * Brief : process the received data
  256. *
  257. * Input : dat - the data buffer
  258. *
  259. * len - the count of data to be send
  260. *
  261. * Output : None
  262. *
  263. * Remark : None
  264. ***********************************************************************************/
  265. voidUart1_Send_Bytes(uint8_t *dat,uint16_t len)
  266. {
  267. #if 0//( USART1_RXD_MODE == UART_SNED_REQ )
  268. while(len--)
  269. {
  270. USART_SendData(USART1, *dat++);
  271. /* Loop until the end of transmission */
  272. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
  273. }
  274. #else
  275. // 判断长度是否有效
  276. if(!len)
  277. return ;
  278. while(DMA_GetCurrDataCounter(USART1_TXD_MA_CHANNEL));
  279. // 检查DMA发送通道内是否还有数据
  280. if(dat)
  281. memcpy(sendBuff, dat,(len >DEBUG_PRINT_BUF_LEN ? DEBUG_PRINT_BUF_LEN:len));
  282. // DMA发送数据-要先关 设置发送长度 开启DMA
  283. DMA_Cmd(USART1_TXD_MA_CHANNEL, DISABLE);
  284. // 设置发送长度
  285. USART1_TXD_MA_CHANNEL->CNDTR = len;
  286. // 启动DMA发送
  287. DMA_Cmd(USART1_TXD_MA_CHANNEL, ENABLE);
  288. #endif /**/
  289. }
  290. /*********************************************************************************
  291. * Name : Debug_Printf
  292. *
  293. * Brief : print the debug information
  294. *
  295. * Input : *fmt - the print data type
  296. *
  297. * Output : None
  298. *
  299. * Remark : just like theprintf() function
  300. ***********************************************************************************/
  301. int Debug_Printf(const char*fmt, ...)
  302. {
  303. uint32_t ulLen = 0;
  304. #ifdef UART_DEBUG_PRINT
  305. va_list ap;
  306. // 开辟缓冲区
  307. // char *pBuf = (char*)OS_MALLOC(DEBUG_PRINT_BUF_LEN);
  308. char *pBuf = (char*)malloc(DEBUG_PRINT_BUF_LEN);
  309. va_start(ap,fmt);
  310. if(pBuf != NULL)
  311. {
  312. // 用虚拟打印函数实现
  313. ulLen = vsprintf(pBuf,fmt,ap);
  314. va_end(ap);
  315. // 从串口输出
  316. Uart1_Send_Bytes((uint8_t *)pBuf,ulLen );
  317. // 释放存储空间
  318. //OS_FREE(pBuf);
  319. free(pBuf);
  320. }
  321. #endif /* UART_DEBUG_PRINT*/
  322. return ulLen;
  323. }
  324. /////////////////////////////////////////////////////////////////////////////////////////////////////////
  325. // USART2 相关API - 主要用于级联信号
  326. /////////////////////////////////////////////////////////////////////////////////////////////////////////
  327. /*********************************************************************************
  328. * Name : Uart2_Gpio_Init
  329. *
  330. * Brief : Initialized the uart2 port.
  331. *
  332. * Input : None
  333. *
  334. * Output : None
  335. *
  336. * Remark :PA3-TX,PA2-RX,banudrate=9600
  337. ***********************************************************************************/
  338. void Uart2_Gpio_Init(void)
  339. {
  340. GPIO_InitTypeDef GPIO_InitStructure;
  341. USART_InitTypeDef USART_InitStructure;
  342. NVIC_InitTypeDef NVIC_InitStructure;
  343. // 开启串口时钟及端口时钟
  344. RCC_APB1PeriphClockCmd(USART2_RCC,ENABLE);
  345. RCC_AHBPeriphClockCmd(USART2_PORT_RCC,ENABLE);
  346. GPIO_PinAFConfig(USART2_MAP_PORT,UART2_TXD_PINSOURCE, UART2_AF_TXD);
  347. GPIO_PinAFConfig(USART2_MAP_PORT,UART2_RXD_PINSOURCE, UART2_AF_RXD);
  348. // 配置TX端口
  349. GPIO_InitStructure.GPIO_Pin = UART2_TXD_PIN | UART2_RXD_PIN;
  350. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  351. GPIO_InitStructure.GPIO_OType =GPIO_OType_PP;
  352. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  353. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  354. GPIO_Init(USART2_MAP_PORT, &GPIO_InitStructure);
  355. // 配置串口模式
  356. USART_InitStructure.USART_BaudRate = USART2_BAUDRATE;
  357. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  358. USART_InitStructure.USART_StopBits = USART_Parity_No;
  359. USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;
  360. USART_InitStructure.USART_Mode = USART_Mode_Rx |USART_Mode_Tx;
  361. USART_Init(USART2,&USART_InitStructure);
  362. //TXE发送中断,TC传输完成中断,RXNE接收中断,PE奇偶错误中断,可以是多个
  363. USART_ITConfig(USART2,USART_IT_TC,DISABLE);
  364. // 设置总线空闲产生中断
  365. USART_ITConfig(USART2,USART_IT_IDLE,ENABLE);
  366. USART2->ICR |= 1<<4; //必须先清除IDLE中断,否则会一直进IDLE中断
  367. USART_ITConfig(USART2, USART_IT_RXNE,DISABLE);
  368. // 开启串口DMA接收
  369. USART_DMACmd(USART2,USART_DMAReq_Rx,ENABLE);
  370. // 开启串口2
  371. USART_Cmd(USART2,ENABLE);
  372. // 中断配置
  373. NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  374. NVIC_InitStructure.NVIC_IRQChannelPriority= 2;
  375. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  376. NVIC_Init(&NVIC_InitStructure);
  377. }
  378. /*********************************************************************************
  379. * Name : Uart2_DMA_Init
  380. *
  381. * Brief : Initialized the uart2 DMA.
  382. *
  383. * Input : None
  384. *
  385. * Output : None
  386. *
  387. * Remark : None
  388. ***********************************************************************************/
  389. void Uart2_DMA_Init(void)
  390. {
  391. DMA_InitTypeDef DMA_Initstruct;
  392. // 开启DMA时钟
  393. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
  394. //DMA1通道5配置
  395. DMA_DeInit(USART2_RXD_DMA_CHAN);
  396. // DMA配置
  397. // 外设地址
  398. DMA_Initstruct.DMA_PeripheralBaseAddr = (uint32_t)(&USART2->RDR);
  399. // 内存地址
  400. DMA_Initstruct.DMA_MemoryBaseAddr =(uint32_t)revUart2;
  401. // DMA传输方向单向
  402. DMA_Initstruct.DMA_DIR =DMA_DIR_PeripheralSRC;
  403. // 设置DMA在传输时缓冲区的长度
  404. DMA_Initstruct.DMA_BufferSize =USART_RXD_BUF_SIZE;
  405. // 设置DMA的外设递增模式,一个外设
  406. DMA_Initstruct.DMA_PeripheralInc =DMA_PeripheralInc_Disable;
  407. // 设置DMA的内存递增模式
  408. DMA_Initstruct.DMA_MemoryInc =DMA_MemoryInc_Enable;
  409. // 外设数据字长
  410. DMA_Initstruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  411. // 内存数据字长
  412. DMA_Initstruct.DMA_MemoryDataSize =DMA_MemoryDataSize_Byte;
  413. // 设置DMA的传输模式
  414. DMA_Initstruct.DMA_Mode =DMA_Mode_Normal;
  415. // 设置DMA的优先级别
  416. DMA_Initstruct.DMA_Priority =DMA_Priority_High; // DMA_Priority_VeryHigh;
  417. // 设置DMA的2个memory中的变量互相访问
  418. DMA_Initstruct.DMA_M2M =DMA_M2M_Disable;
  419. DMA_Init(USART2_RXD_DMA_CHAN,&DMA_Initstruct);
  420. // 开启DMA
  421. DMA_Cmd(USART2_RXD_DMA_CHAN,ENABLE);
  422. #if( USART2_RXD_MODE == UART_SEND_DMA)
  423. // DMA通道TXD配置
  424. DMA_DeInit(USART2_TXD_DMA_CHAN);
  425. // 外设地址,串口1
  426. DMA_Initstruct.DMA_PeripheralBaseAddr =(uint32_t)(&USART1->TDR);
  427. // 发送内存地址
  428. DMA_Initstruct.DMA_MemoryBaseAddr = (uint32_t)send2Buff;
  429. // 外设为传送数据目的地,即发送数据
  430. DMA_Initstruct.DMA_DIR =DMA_DIR_PeripheralDST;
  431. // 发送长度为0,即未有快递需要发送
  432. DMA_Initstruct.DMA_BufferSize = 0;
  433. // 初始化
  434. DMA_Init(USART2_TXD_DMA_CHAN,&DMA_Initstruct);
  435. // 使能串口发送完成中断
  436. USART_ITConfig(USART2, USART_IT_TC,ENABLE);
  437. DMA_Cmd(USART2_TXD_DMA_CHAN,ENABLE);
  438. // 使能DMA串口发送和接受请求
  439. USART_DMACmd(USART2,(USART_DMAReq_Tx|USART_DMAReq_Rx), ENABLE);
  440. #endif /* USART1_RXD_MODE */
  441. }
  442. /*********************************************************************************
  443. * Name : UART2_Api_Init
  444. *
  445. * Brief : Initialized the uart1
  446. *
  447. * Input : None
  448. *
  449. * Output : None
  450. *
  451. * Remark : None
  452. ***********************************************************************************/
  453. void UART2_Api_Init(void)
  454. {
  455. Uart2_Gpio_Init();
  456. Uart2_DMA_Init();
  457. }
  458. /*********************************************************************************
  459. * Name : USART2_IRQHandler
  460. *
  461. * Brief : USART1 interrupt process handle
  462. *
  463. * Input : None
  464. *
  465. * Output : None
  466. *
  467. * Remark : None
  468. ***********************************************************************************/
  469. void USART2_IRQHandler(void)
  470. {
  471. uint8_t len = 0;
  472. if(USART_GetITStatus(USART2,USART_IT_IDLE) != RESET)
  473. {
  474. USART2->ICR |= 1<<4; // 清除中断
  475. DMA_Cmd(USART2_RXD_DMA_CHAN,DISABLE);
  476. len =USART_RXD_BUF_SIZE - DMA_GetCurrDataCounter(USART2_RXD_DMA_CHAN);
  477. if(len >USART_RXD_BUF_SIZE)
  478. len =USART_RXD_BUF_SIZE;
  479. memcpy(datUart2Buff,revUart2,len);
  480. // 重新设置接收数据个数
  481. USART2_RXD_DMA_CHAN->CNDTR =USART_RXD_BUF_SIZE;
  482. // 开启DMA
  483. DMA_Cmd(USART2_RXD_DMA_CHAN,ENABLE);
  484. revUart2Flag = TRUE;
  485. }
  486. #if( USART2_RXD_MODE ==UART_SEND_DMA )
  487. // 全部数据发送完成,产生该标记
  488. if(USART_GetITStatus(USART2,USART_IT_TC)!=RESET)
  489. {
  490. // 清除完成标记
  491. USART_ClearITPendingBit(USART2,USART_IT_TC);
  492. DMA_Cmd(USART2_TXD_DMA_CHAN,DISABLE);
  493. // 清除数据长度
  494. USART2_TXD_DMA_CHAN->CNDTR= 0;
  495. DMA_Cmd(USART2_TXD_DMA_CHAN, ENABLE);
  496. }
  497. #endif /* USART1_RXD_MODE */
  498. }
  499. /*********************************************************************************
  500. * Name :Uart2_Data_Process
  501. *
  502. * Brief : process the received data
  503. *
  504. * Input : processDat - the process function point
  505. *
  506. * Output : None
  507. *
  508. * Remark : None
  509. ***********************************************************************************/
  510. void Uart2_Data_Process(pRevprocessDat)
  511. {
  512. if(!revUart2Flag)
  513. return;
  514. revUart2Flag = FALSE;
  515. processDat(datUart2Buff);
  516. }
  517. /*********************************************************************************
  518. * Name : Uart2_Send_Bytes
  519. *
  520. * Brief : process the received data
  521. *
  522. * Input : dat - the data buffer
  523. *
  524. * len - the count of data to be send
  525. *
  526. * Output : None
  527. *
  528. * Remark : None
  529. ***********************************************************************************/
  530. void Uart2_Send_Bytes(uint8_t*dat,uint16_t len)
  531. {
  532. #if (USART2_RXD_MODE ==UART_SNED_REQ)
  533. while(len--)
  534. {
  535. USART_SendData(USART2, *dat++);
  536. /* Loop until the end of transmission */
  537. while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET);
  538. }
  539. #else
  540. // 判断长度是否有效
  541. if(!len)
  542. return ;
  543. while(DMA_GetCurrDataCounter(USART2_TXD_DMA_CHAN));
  544. // 检查DMA发送通道内是否还有数据
  545. if(dat)
  546. memcpy(send2Buff,dat,(len > USART2_TX_BUF_SIZE ? USART2_TX_BUF_SIZE:len));
  547. // DMA发送数据-要先关 设置发送长度 开启DMA
  548. DMA_Cmd(USART2_TXD_DMA_CHAN, DISABLE);
  549. // 设置发送长度
  550. USART2_TXD_DMA_CHAN->CNDTR = len;
  551. // 启动DMA发送
  552. DMA_Cmd(USART2_TXD_DMA_CHAN, ENABLE);
  553. #endif /* */
  554. }
本次代码是为了测试用的,所以有些地方写的比较随意,变量命名也不是太规范,这里检讨一下。串口接收到数据处理函数用的是回调函数,方便修改,在使用时,只要调用相关的函数指针即可。关于函数指针的类型,在下面Dsk_Usart.h中有定义。
Dsk_Usart.h源文件
笔者习惯一个.c文件跟一个.h文件,方便移植,也方面管理。用到哪个模块直接把两个文件一拷贝,再在相关文件中包含.h文件,就可以直接用,比较方便。有人会说有没有必要,一个.C跟一个.H,这个怎么说呢?看个人习惯吧,编程这玩意只有最适合项目工程的,没有说最好的。一般按功能模块划分,源文件感觉长了,还可以再细分不同的模块,笔者认为,加个头文件,至少不用在.c文件中调用某个函数再用extern声明那个外部函数,至少声明不会乱掉。
关于Dsk_Usart.h中主要是接口的声明,API的声明等,其源代码如下所示:
  1. /*********************************************************************************
  2. * Copyright (C) 2000-2018 Mark Xu Work Studio,All Rights Reserved.
  3. *
  4. * File : Dsk_Uart.h
  5. *
  6. * Brief : the defination and declarations for uart module
  7. *
  8. * Author :Mark Xu
  9. *
  10. * Remark :None
  11. ---------------------------------------------------------------------------------
  12. * Modify Content: NULL
  13. *
  14. * Modify Date : xxxx/xx/xx/ xx:xx
  15. *
  16. * Modify staff :xx xx
  17. **********************************************************************************/
  18. #ifndef __DSK_UART_H__
  19. #define __DSK_UART_H__
  20. #ifdef __cplusplus
  21. extern "C" {
  22. #endif /*__cplusplus */
  23. #ifdef DSKUART_IMPLATION
  24. #define DSKUART_EXTERN
  25. #else
  26. #define DSKUART_EXTERN extern
  27. #endif
  28. #include "stdint.h"
  29. #include "stm32f0xx.h"
  30. /*********************************************************************************
  31. * Port defination
  32. **********************************************************************************/
  33. // Config the uart1
  34. #define USART1_RCC RCC_APB2Periph_USART1
  35. #define USART1_PORT_RCC RCC_AHBPeriph_GPIOA
  36. #define USART1_BAUDRATE (9600)
  37. #define USART1_RXD_MA_CHANNEL DMA1_Channel3
  38. #define USART1_MAP_PORT GPIOA
  39. #define USART1_TXD_MA_CHANNEL DMA1_Channel2
  40. // #define USART1_MAP_PORT GPIOA
  41. #define UART1_TXD_PORT GPIOA
  42. #define UART1_TXD_PIN GPIO_Pin_9
  43. #define UART1_TXD_PINSOURCE GPIO_PinSource9
  44. #define UART1_AF_TXD GPIO_AF_1
  45. #define UART1_RXD_PORT GPIOA
  46. #define UART1_RXD_PIN GPIO_Pin_10
  47. #define UART1_RXD_PINSOURCE GPIO_PinSource10
  48. #define UART1_AF_RXD GPIO_AF_1
  49. // Config the uart2
  50. #define USART2_RCC RCC_APB1Periph_USART2
  51. #define USART2_PORT_RCC RCC_AHBPeriph_GPIOA
  52. #define USART2_BAUDRATE (9600)
  53. #define USART2_RXD_DMA_CHAN DMA1_Channel5
  54. #define USART2_TXD_DMA_CHAN DMA1_Channel4
  55. #define USART2_MAP_PORT GPIOA
  56. #define UART2_TXD_PORT GPIOA
  57. #define UART2_TXD_PIN GPIO_Pin_2
  58. #define UART2_TXD_PINSOURCE GPIO_PinSource2
  59. #define UART2_AF_TXD GPIO_AF_1
  60. #define UART2_RXD_PORT GPIOA
  61. #define UART2_RXD_PIN GPIO_Pin_3
  62. #define UART2_RXD_PINSOURCE GPIO_PinSource3
  63. #define UART2_AF_RXD GPIO_AF_1
  64. /*********************************************************************************
  65. * Exported Typedef
  66. **********************************************************************************/
  67. // 接收命令处理函数
  68. typedef void (*pRev)(uint8_t *);
  69. /*********************************************************************************
  70. * Exported Functions
  71. **********************************************************************************/
  72. DSKUART_EXTERN void UART1_Api_Init(void);
  73. DSKUART_EXTERN void Uart1_Send_Bytes(uint8_t *dat,uint16_t len);
  74. // 处理接收到的数据
  75. DSKUART_EXTERN void Uart1_Data_Process(pRev processDat);
  76. // 打印调试信息
  77. DSKUART_EXTERN int Debug_Printf(const char *fmt, ...);
  78. //---------------------------------------------------------------------------------------------------------------------------------
  79. // 关于UART2相关API
  80. //---------------------------------------------------------------------------------------------------------------------------------
  81. DSKUART_EXTERN void UART2_Api_Init(void);
  82. DSKUART_EXTERN void Uart2_Send_Bytes(uint8_t *dat,uint16_t len);
  83. DSKUART_EXTERN void Uart2_Data_Process(pRev processDat);
  84. #ifdef __cplusplus
  85. }
  86. #endif /* __cplusplus */
  87. #endif /* __DSK_UART_H__ */
结语
本篇文档主要总结一下如果设置串口的空闲中断+DMA的应用方式,也相当于代码备份吧,具体要怎么测试,测试如果如果,这里不再说细述,如果读者有兴趣,可以自己设置相关的测试^_^
本帖最后由 懒猫爱飞 于 2018-11-29 16:19 编辑
专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void

回复评论 (10)

论坛编辑文本不给力,还是下载附件查看吧
专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
点赞  2018-11-29 11:35
535.png
本来应该是这样的,可是word直接粘贴过来就成上面那样子了,哎,可惜了……
专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
点赞  2018-11-29 11:36
756.png

代码段本来是这样的,结果粘贴到论坛上,成了一坨翔
专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
点赞  2018-11-29 11:39
谢谢楼主分享,支持!
点赞  2018-11-29 15:49
既然是分享,就走心一些,花了点时间,重新编辑了一下,效果还可以,至少能看了
专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
点赞  2018-11-29 16:20
楼主,死等标志检测发送完成,不如弄个DMA传输完成中断建立标志?
1084534438 欢迎交流  [加油,一切皆有可能]
点赞  2018-11-29 20:57
引用: RCSN 发表于 2018-11-29 20:57
楼主,死等标志检测发送完成,不如弄个DMA传输完成中断建立标志?

好的,后面再改进一下^_^
专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
点赞  2018-11-30 08:22

谢谢楼主分享,支持!
人生啊 人生
点赞  2018-11-30 13:58
谢谢分享!
点赞  2018-11-30 15:24
谢谢分享,有封装好的文件就好了
点赞  2019-3-13 15:15
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复