贴中 49 楼总结

    无论是串口还是 SPI 口,作为通信接口有多种处理方式,没有说 timeout 和定长接收或者中断中处理一定是哪种好,也分场合,小结了一下有这么几种做法:

    1. 轮询,收到一个字节处理一个字节,每个字节的处理可以用一个状态机。这种处理方法适合于低速、ram 较小的通信场合。
    2. 中断,在中断中直接处理每个字节的内容。这种方法适合于低速、ram 较少的通信场合。
    3. 中断,数据存入软件的 fifo 队列,应用层通过读取软件的 fifo 来实现串口操作。这种方法适合于低速,ram 足够的场合,且每帧具有固定的帧长度,或帧长度可以从固定长度的帧头获取。
      4.DMA,每次接收定长数据。适合于高速通信,ram 足够的场合,且每帧具有固定的帧长度,或帧长度可以从固定长度的帧头获取。(需要注意的是当帧由于某种原因一直没有接收满时,程序是否会死等在那里。说白了还是需要一个超时功能来保证程序的可靠性)
    4. 第一个字用中断,开启 DMA 接收剩余数据,并开启定时器。(这么做第一个字节之后可能会丢失一部分数据)。这种方法适合用在有前导字节的通信帧中,且需要整帧接收处理的情况下,适合处理传输速度较快的通信,占用 cpu 较少。
      6.DMA + 接收结束判断。这接收结束判断可以是字节的 timeout,也可以是 USART_IT_IDLE 线路空闲的标志等。这种方式对于需要整帧接收,且通信速度较快或需要占用 cpu 较少的情况。
    5. 硬件 fifo + 接收结束判断。每次 fifo 到达阀值在终端中取一次数据,当超时时标示收完一帧。用于需要整帧接收,且通信速度较快或需要占用 cpu 较少的情况。

    这里罗列了一些我能想到的用法。主要分 3 种用途:1. 字节处理; 2. 规定长度的帧处理; 3. 不规定长度整帧处理。
    如果你的通信协议能得到一帧数据长度的,为什么非得强求 fifo 或 dma 的 TimeOut 的方式呢?如果你本身就只有那么点内存资源的,为什么不用字节处理要用其它方式呢?当然,无论如何,为了程序健壮性,设置一个超时还是必要的。没有其他办法就用定时器做。

    补充一下,结合实际情况,STM32L151 也有 USART 的 RX idle 中断触发,可以使用 STM32L 的 USART(idle interrupt)+DMA 方式来处理不定长度的数据。

    https://www.amobbs.com/thread-5486343-1-1.html 很好的一个帖子

    前言:开始学 USART+DMA 的时候看到帖子《STM32 UART DMA 实现未知数据长度接收》,觉得方法妙极了。此下出自此帖子——(整体的思路是这样的,一开始设置好 DMA 接收,可以把缓冲区长度设置为帧最大长度,我们可以把 RX 连接到定时器的管脚输入端,并且一开始设置输入并且使能引脚下降沿中断,当帧的第一个字节发送时,因为起始位为低电平,空闲时 UART 为高电平,满足条件,进入中断,禁止中断,并且在中断中开启定时器,该定时器工作在复位模式,上升沿复位,并且设置好定时器输出比较值为超时时间,比如 20ms,这样,在传输后面字节时,肯定会有高低电平出现,即便是传输的是 0x00,0xFF,虽然 UART 数据区不变,但是都为 1,或都为 0,但是因为起始位为低电平,停止位是高电平,所以肯定会有上升沿,定时器会一直复位,输出定时器的计数器一直到达不了输出比较值,当一帧传输结束后,定时在最后一个字节复位后,由于没有数据继续到达,无法复位,则计数器就能计到输出比较值,这时发出中断,在定时器中断中可以计算出接收数据的长度,并且通知外部数据已经接收完毕。)
    今天我在工作中调通了另一种 USART+DMA 接收未知数据长度的接收,使用的是 USRAT 空闲总线中断接收,这种方法也在网站上比较多见,以前没试过,今天才知道如此的爽,另外我使用 DMA 发送 USART 数据替代了以前的查询法发送,发现更加爽了。其速度快了很多,尤其是在大量数据传输与发送的时候其优势更加明显。

    我举个例子:1、后台数据 ->USART1-> USART2-> 其它设备,其它设备数据 ->USART2-> USART1-> 后台,这两个数据过程也可能同时进行。
    2、由于硬件的限制,USART1 和 USART2 的传输波特率不一样,比如 USART1 使用 GPRS 通信,USART2 使用短距离无线通信;或者 USART1 使用以太网通信,USART2 使用 485 总线通信。
    由于在寝室只有笔记本电脑,只有一个串口转 USB,没办法实现两个串口之间的数据转发了,只好实现串口各自的数据转发。
    现在我把我实现的过程简单描述一下:
    1、 初始化设置:USART1RX+DMA1 Channel5,USART2RX+DMA1 Channel6,USART1TX+DMA1 Channel4,USART2TX+DMA1 Channel7(具体设置请看程序包)。
    2、 当数据发送给 USART1 接收完毕时候会引起 USART1 的串口总线中断,计算 DMA1 Channel5 内存数组剩余容量,得到接收的字符长度。将接收的字符复制给 DMA1 Channel4 内存数组,启动 DMA1 Channel4 通道传输数据,(传输完成需要关闭。)下一次数据接收可以在启动 DMA1 Channel4 时候就开始,不需要等待 DMA1 Channel4 数据传输完成。但是上一次 DMA1 Channel4 完成之前,不可以将数据复制给 DMA1_ Channel4 内存数组,会冲掉以前数据。
    3、 USART2 类同 USART1。
    呵呵,下面贴程序:

    1. IO 口定义:

    2. void GPIO_Configuration(void)

    3. {

    4. GPIO_InitTypeDef GPIO_InitStructure;

    5. / 第 1 步:打开 GPIO 和 USART 部件的时钟 /

    6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

    7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    8. / 第 2 步:将 USART Tx 的 GPIO 配置为推挽复用模式 /

    9. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;

    10. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    11. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    12. GPIO_Init(GPIOA, &GPIO_InitStructure);

    13. /* 第 3 步:将 USART Rx 的 GPIO 配置为浮空输入模式

    14. 由于 CPU 复位后,GPIO 缺省都是浮空输入模式,因此下面这个步骤不是必须的

    15. 但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数

    16. */

    17. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

    18. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    19. GPIO_Init(GPIOA, &GPIO_InitStructure);

    20. / 第 1 步:打开 GPIO 和 USART2 部件的时钟 /

    21. //RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

    22. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    23. / 第 2 步:将 USART2 Tx 的 GPIO 配置为推挽复用模式 /

    24. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;

    25. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    26. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    27. GPIO_Init(GPIOA, &GPIO_InitStructure);

    28. /* 第 3 步:将 USART2 Rx 的 GPIO 配置为浮空输入模式

    29. 由于 CPU 复位后,GPIO 缺省都是浮空输入模式,因此下面这个步骤不是必须的

    30. 但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数

    31. */

    32. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;

    33. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    34. GPIO_Init(GPIOA, &GPIO_InitStructure);

    35. /* 第 3 步已经做了,因此这步可以不做

    36. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    37. */

    38. GPIO_Init(GPIOA, &GPIO_InitStructure);

    39. }

    40. 串口初始化:

    41. void USART_Configuration(void)

    42. {

    43. USART_InitTypeDef USART_InitStructure;

    44. /* 第 4 步:配置 USART 参数

    • BaudRate = 115200 baud
    • Word Length = 8 Bits
    • One Stop Bit
    • No parity
    • Hardware flow control disabled (RTS and CTS signals)
    • Receive and transmit enabled
    1. */

    2. USART_InitStructure.USART_BaudRate = 19200;

    3. USART_InitStructure.USART_WordLength = USART_WordLength_8b;

    4. USART_InitStructure.USART_StopBits = USART_StopBits_1;

    5. USART_InitStructure.USART_Parity = USART_Parity_No;

    6. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    7. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    8. USART_Init(USART1, &USART_InitStructure);

    9. // 空闲中断

    10. USART_ITConfig(USART1, USART_IT_IDLE , ENABLE);

    11. / 第 5 步:使能 USART, 配置完毕 /

    12. USART_Cmd(USART1, ENABLE);

    13. /* CPU 的小缺陷:串口配置好,如果直接 Send,则第 1 个字节发送不出去

    14. 如下语句解决第 1 个字节无法正确发送出去的问题 */

    15. USART_ClearFlag(USART1, USART_FLAG_TC); / 清发送外城标志,Transmission Complete flag /

    16. USART_InitStructure.USART_BaudRate = 9600;

    17. USART_InitStructure.USART_WordLength = USART_WordLength_8b;

    18. USART_InitStructure.USART_StopBits = USART_StopBits_1;

    19. USART_InitStructure.USART_Parity = USART_Parity_No;

    20. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    21. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    22. USART_Init(USART2, &USART_InitStructure);

    23. USART_ITConfig(USART2, USART_IT_IDLE , ENABLE);// 开启空闲, 帧错, 噪声, 校验错中断

    24. USART_Cmd(USART2, ENABLE);

    25. /* CPU 的小缺陷:串口配置好,如果直接 Send,则第 1 个字节发送不出去

    26. 如下语句解决第 1 个字节无法正确发送出去的问题 */

    27. USART_ClearFlag(USART2, USART_FLAG_TC); / 清发送外城标志,Transmission Complete flag /

    28. }

    29. DMA 配置:

    30. void DMA_Configuration(void)

    31. {

    32. DMA_InitTypeDef DMA_InitStructure;

    33. / DMA clock enable /

    34. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1

    35. / DMA1 Channel4 (triggered by USART1 Tx event) Config /

    36. DMA_DeInit(DMA1_Channel4);

    37. DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40013804;

    38. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_SEND_DATA;

    39. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

    40. DMA_InitStructure.DMA_BufferSize = 512;

    41. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

    42. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

    43. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

    44. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

    45. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

    46. DMA_InitStructure.DMA_Priority = DMA_Priority_High;

    47. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

    48. DMA_Init(DMA1_Channel4, &DMA_InitStructure);

    49. DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);

    50. DMA_ITConfig(DMA1_Channel4, DMA_IT_TE, ENABLE);

    51. / Enable USART1 DMA TX request /

    52. USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);

    53. DMA_Cmd(DMA1_Channel4, DISABLE);

    54. / DMA1 Channel5 (triggered by USART2 Tx event) Config /

    55. DMA_DeInit(DMA1_Channel7);

    56. DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40004404;

    57. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART2_SEND_DATA;

    58. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

    59. DMA_InitStructure.DMA_BufferSize = 512;

    60. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

    61. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

    62. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

    63. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

    64. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

    65. DMA_InitStructure.DMA_Priority = DMA_Priority_High;

    66. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

    67. DMA_Init(DMA1_Channel7, &DMA_InitStructure);

    68. DMA_ITConfig(DMA1_Channel7, DMA_IT_TC, ENABLE);

    69. DMA_ITConfig(DMA1_Channel7, DMA_IT_TE, ENABLE);

    70. / Enable USART1 DMA TX request /

    71. USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);

    72. DMA_Cmd(DMA1_Channel7, DISABLE);

    73. / DMA1 Channel5 (triggered by USART1 Rx event) Config /

    74. DMA_DeInit(DMA1_Channel5);

    75. DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40013804;

    76. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_RECEIVE_DATA;

    77. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

    78. DMA_InitStructure.DMA_BufferSize = 512;

    79. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

    80. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

    81. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

    82. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

    83. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

    84. DMA_InitStructure.DMA_Priority = DMA_Priority_High;

    85. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

    86. DMA_Init(DMA1_Channel5, &DMA_InitStructure);

    87. DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);

    88. DMA_ITConfig(DMA1_Channel5, DMA_IT_TE, ENABLE);

    89. / Enable USART1 DMA RX request /

    90. USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);

    91. DMA_Cmd(DMA1_Channel5, ENABLE);

    92. / DMA1 Channel6 (triggered by USART1 Rx event) Config /

    93. DMA_DeInit(DMA1_Channel6);

    94. DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40004404;

    95. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART2_RECEIVE_DATA;

    96. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

    97. DMA_InitStructure.DMA_BufferSize = 512;

    98. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

    99. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

    100. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

    101. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

    102. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

    103. DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;

    104. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

    105. DMA_Init(DMA1_Channel6, &DMA_InitStructure);

    106. DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);

    107. DMA_ITConfig(DMA1_Channel6, DMA_IT_TE, ENABLE);

    108. / Enable USART2 DMA RX request /

    109. USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);

    110. DMA_Cmd(DMA1_Channel6, ENABLE);

    111. }

    112. 中断优先级配置:

    113. void NVIC_Configuration(void)

    114. {

    115. NVIC_InitTypeDef NVIC_InitStructure;

    116. / Configure one bit for preemption priority /

    117. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    118. / Enable the USART1 Interrupt /

    119. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

    120. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;

    121. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

    122. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    123. NVIC_Init(&NVIC_InitStructure);

    124. / Enable the USART2 Interrupt /

    125. NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;

    126. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;

    127. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;

    128. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    129. NVIC_Init(&NVIC_InitStructure);

    130. //Enable DMA Channel4 Interrupt

    131. NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;

    132. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

    133. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

    134. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    135. NVIC_Init(&NVIC_InitStructure);

    136. //Enable DMA Channel7 Interrupt

    137. NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;

    138. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

    139. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;

    140. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    141. NVIC_Init(&NVIC_InitStructure);

    142. /Enable DMA Channel5 Interrupt /

    143. NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;

    144. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;

    145. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

    146. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    147. NVIC_Init(&NVIC_InitStructure);

    148. /Enable DMA Channel6 Interrupt /

    149. NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;

    150. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;

    151. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

    152. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    153. NVIC_Init(&NVIC_InitStructure);

    154. }

    155. 数组定义,含义如题名:

    156. u8 USART1_SEND_DATA[512];

    157. u8 USART2_SEND_DATA[512];

    158. u8 USART1_RECEIVE_DATA[512];

    159. u8 USART2_RECEIVE_DATA[512];

    160. u8 USART1_TX_Finish=1;// USART1 发送完成标志量

    161. u8 USART2_TX_Finish=1; // USART2 发送完成标志量

    162. USART1 中断服务函数

    163. void USART1_IRQHandler(void)

    164. {

    165. u16 DATA_LEN;

    166. u16 i;

    167. if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)// 如果为空闲总线中断

    168. {

    169. DMA_Cmd(DMA1_Channel5, DISABLE);// 关闭 DMA, 防止处理其间有数据

    170. //USART_RX_STA = USART1->SR;// 先读 SR,然后读 DR 才能**

    171. //USART_RX_STA = USART1->DR;

    172. DATA_LEN=512-DMA_GetCurrDataCounter(DMA1_Channel5);

    173. if(DATA_LEN > 0)

    174. {

    175. while(USART1_TX_Finish==0)// 等待数据传输完成才下一次

    176. {

    177. ;

    178. }

    179. // 将数据送 DMA 存储地址

    180. for(i=0;i<DATA_LEN;i++)

    181. {

    182. USART1_SEND_DATA[i]=USART1_RECEIVE_DATA[i];

    183. }

    184. //USART 用 DMA 传输替代查询方式发送,克服被高优先级中断而产生丢帧现象。

    185. DMA_Cmd(DMA1_Channel4, DISABLE); // 改变 datasize 前先要禁止通道工作

    186. DMA1_Channel4->CNDTR=DATA_LEN; //DMA1, 传输数据量

    187. USART1_TX_Finish=0;//DMA 传输开始标志量

    188. DMA_Cmd(DMA1_Channel4, ENABLE);

    189. }

    190. //DMA_Cmd(DMA1_Channel5, DISABLE);// 关闭 DMA, 防止处理其间有数据

    191. DMA_ClearFlag(DMA1_FLAG_GL5 | DMA1_FLAG_TC5 | DMA1_FLAG_TE5 | DMA1_FLAG_HT5);// 清标志

    192. DMA1_Channel5->CNDTR = 512;// 重装填

    193. DMA_Cmd(DMA1_Channel5, ENABLE);// 处理完, 重开 DMA

    194. // 读 SR 后读 DR**Idle

    195. i = USART1->SR;

    196. i = USART1->DR;

    197. }

    198. if(USART_GetITStatus(USART1, USART_IT_PE | USART_IT_FE | USART_IT_NE) != RESET)// 出错

    199. {

    200. USART_ClearITPendingBit(USART1, USART_IT_PE | USART_IT_FE | USART_IT_NE);

    201. }

    202. USART_ClearITPendingBit(USART1, USART_IT_TC);

    203. USART_ClearITPendingBit(USART1, USART_IT_IDLE);

    204. }

    205. USART2 中断服务函数

    206. void USART2_IRQHandler(void)

    207. {

    208. u16 DATA_LEN;

    209. u16 i;

    210. if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) // 如果为空闲总线中断

    211. {

    212. DMA_Cmd(DMA1_Channel6, DISABLE);// 关闭 DMA, 防止处理其间有数据

    213. //USART_RX_STA = USART1->SR;// 先读 SR,然后读 DR 才能**

    214. //USART_RX_STA = USART1->DR;

    215. DATA_LEN=512-DMA_GetCurrDataCounter(DMA1_Channel6);

    216. if(DATA_LEN > 0)

    217. {

    218. while(USART2_TX_Finish==0)// 等待数据完成才下一次

    219. {

    220. ;

    221. }

    222. // 将数据送 DMA 存储地址

    223. for(i=0;i<DATA_LEN;i++)

    224. {

    225. USART2_SEND_DATA[i]=USART2_RECEIVE_DATA[i];

    226. }

    227. //USART 用 DMA 传输替代查询方式发送,克服被高优先级中断而产生丢帧现象。

    228. DMA_Cmd(DMA1_Channel7, DISABLE); // 改变 datasize 前先要禁止通道工作

    229. DMA1_Channel7->CNDTR=DATA_LEN; //DMA1, 传输数据量

    230. USART2_TX_Finish=0;//DMA 传输开始标志量

    231. DMA_Cmd(DMA1_Channel7, ENABLE);

    232. }

    233. //DMA_Cmd(DMA1_Channel5, DISABLE);// 关闭 DMA, 防止处理其间有数据

    234. DMA_ClearFlag(DMA1_FLAG_GL6 | DMA1_FLAG_TC6 | DMA1_FLAG_TE6 | DMA1_FLAG_HT6);// 清标志

    235. DMA1_Channel6->CNDTR = 512;// 重装填

    236. DMA_Cmd(DMA1_Channel6, ENABLE);// 处理完, 重开 DMA

    237. // 读 SR 后读 DR**Idle

    238. i = USART2->SR;

    239. i = USART2->DR;

    240. }

    241. if(USART_GetITStatus(USART2, USART_IT_PE | USART_IT_FE | USART_IT_NE) != RESET)// 出错

    242. {

    243. USART_ClearITPendingBit(USART2, USART_IT_PE | USART_IT_FE | USART_IT_NE);

    244. }

    245. USART_ClearITPendingBit(USART2, USART_IT_TC);

    246. USART_ClearITPendingBit(USART2, USART_IT_IDLE);

    247. }

    248. DMA1_Channel5 中断服务函数

    249. void DMA1_Channel5_IRQHandler(void)

    250. {

    251. DMA_ClearITPendingBit(DMA1_IT_TC5);

    252. DMA_ClearITPendingBit(DMA1_IT_TE5);

    253. DMA_Cmd(DMA1_Channel5, DISABLE);// 关闭 DMA, 防止处理其间有数据

    254. DMA1_Channel5->CNDTR = 580;// 重装填

    255. DMA_Cmd(DMA1_Channel5, ENABLE);// 处理完, 重开 DMA

    256. }

    257. DMA1_Channel6 中断服务函数

    258. void DMA1_Channel6_IRQHandler(void)

    259. {

    260. DMA_ClearITPendingBit(DMA1_IT_TC6);

    261. DMA_ClearITPendingBit(DMA1_IT_TE6);

    262. DMA_Cmd(DMA1_Channel6, DISABLE);// 关闭 DMA, 防止处理其间有数据

    263. DMA1_Channel6->CNDTR = 580;// 重装填

    264. DMA_Cmd(DMA1_Channel6, ENABLE);// 处理完, 重开 DMA

    265. }

    266. DMA1_Channel4 中断服务函数

    267. //USART1 使用 DMA 发数据中断服务程序

    268. void DMA1_Channel4_IRQHandler(void)

    269. {

    270. DMA_ClearITPendingBit(DMA1_IT_TC4);

    271. DMA_ClearITPendingBit(DMA1_IT_TE4);

    272. DMA_Cmd(DMA1_Channel4, DISABLE);// 关闭 DMA

    273. USART1_TX_Finish=1;// 置 DMA 传输完成

    274. }

    275. DMA1_Channel7 中断服务函数

    276. //USART2 使用 DMA 发数据中断服务程序

    277. void DMA1_Channel7_IRQHandler(void)

    278. {

    279. DMA_ClearITPendingBit(DMA1_IT_TC7);

    280. DMA_ClearITPendingBit(DMA1_IT_TE7);

    281. DMA_Cmd(DMA1_Channel7, DISABLE);// 关闭 DMA

    282. USART2_TX_Finish=1;// 置 DMA 传输完成

    283. }

    复制代码

    呵呵,全部完,但是程序在开始启动时会出现自己发几个不知道什么字符,之后一切正常。如有什么问题,请大神指教。个人认为问题不大,因为在工作的时候通过 STM32 访问后台或者后台访问 STM32 大量的间隔密的数据时没有出现问题。而如果没有使用 DMA,单帧数据发收可以,多帧数据经过 USART1 转 USART2,就收不到从 USART2 反馈的第二帧数据了。不一定是速度上的问题,可能是我处理顺序的问题,但是不管是巧合,还是瞎撞的,总归解决办法的就是好办法。
    工程下载地址:https://115.com/file/dpj82l3j#
    USART_DMA_Interrupt.rar
    说明文档下载地址:https://115.com/file/dpj8i8ov#
    STM32 实现 USART+DMA 接收未知长度的数据和发送. doc

    实战:

    static void usart1_config(void)

    {

    USART_InitTypeDef usart1_usart_initstructure;

    NVIC_InitTypeDef NVIC_InitStructure;

    usart1_usart_initstructure.USART_BaudRate = 115200;

    usart1_usart_initstructure.USART_WordLength = USART_WordLength_8b;

    usart1_usart_initstructure.USART_StopBits = USART_StopBits_1;

    usart1_usart_initstructure.USART_Parity = USART_Parity_No;

    usart1_usart_initstructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    usart1_usart_initstructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    //configuration

    USART_Init(USART1, &usart1_usart_initstructure);

    //enable

    USART_Cmd(USART1, ENABLE);

    USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); // RX_Idle interrupt enable

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //IRQÖжÏ

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //Ï&Egre;Õ¼ÓÅÏ&Egre;¼¶0¼¶

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //´ÓÓÅÏ&Egr**e;¼¶2¼¶

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQͨµ&Agr**e;±»Ê¹ÄÜ

    NVIC_Init(&NVIC_InitStructure); //¸&ugre;¾ÝNVIC_InitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯ÍâÉ&egre;NVIC¼Ä´æÆ÷

    USART_ClearFlag(USART1,USART_FLAG_IDLE);

    USART_ClearFlag(USART1,USART_FLAG_TC); 注意此处,需要** flag

    }

    void USART1_IRQHandler(void)

    {

    uint16_t dma_len;

    uint32_t i;

    if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET)

    {

    DMA_Cmd(DMA1_Channel5,DISABLE);

    dma_len = DMA_GetCurrDataCounter(DMA1_Channel5);

    if(dma_len>0)

    {

    usart1_rxbuff.len = USART1_DMA_BUF_NUM - dma_len;

    memcpy(usart1_rxbuff.usart1_rxdata,Ch5DMABuffer,usart1_rxbuff.len);

    memset(Ch5DMABuffer,0,usart1_rxbuff.len);

    usart1_rxbuff.rxend = 1;

    }

    DMA_ClearFlag(DMA1_FLAG_GL5 | DMA1_FLAG_TC5 | DMA1_FLAG_TE5 | DMA1_FLAG_HT5);//

    DMA_SetCurrDataCounter(DMA1_Channel5,sizeof(Ch5DMABuffer));

    DMA_Cmd(DMA1_Channel5,ENABLE);

    }

    (an read to theUSART_SR register followed by a read to the USART_DR register **idle flag 在 reference manual Datasheet 中)

    i = USART1->SR; //**flag 先读取 sr 读取 DR

    i = USART1->DR;

    //USART_ClearITPendingBit(USART1,USART_IT_TC);

    //USART_ClearITPendingBit(USART1,USART_IT_IDLE);

    }
    https://www.eefocus.com/hugo01chen/blog/16-04/3788916aeff.html?_mobile=1
    https://www.eefocus.com/hugo01chen/blog/16-04/3788916aeff.html?_mobile=1