• DSP_TMS320F28335_优秀的串口通信框架


    本文介绍一种嵌入式程序中,一种比较优秀的串口通信框架,除了DSP我认为其他单核的嵌入式平台也可以借鉴本文的思路

    在文章DSP_控制程序框架与优化-CSDN博客这篇文章中,我提到了用“高频小队列”的方式去处理与上位机的通信程序,最小的队列,就是一个节点只存储一个字节。用串口和上位机通信的方式是非常普遍的,本文讲描述一种串口通信的“高频小队列”框架。本框架其实核心的思想就是将串口的接收中断配置成1个字节就触发一次,中断处理程序只做一件事,就是把接收到的1个字节push到接收队列当中,然后在主程序 while(1)中去处理接收到的数据,对数据进行帧解析,为了让这个框架更普适,我们通过帧头和帧尾来截取一帧完整的数据,这样就能普适不定长的通信协议。 但帧解析的方式也很精细,我们是以10kHz的频率来处理,一次只处理一个字节,而不是直接一次把队列里面所有的数据都处理了。 同样的,我们要把数据通过串口发送给上位机,首先也是把需要发送的数据先一个字节一个字节的push到发送队列中,然后同样以10kHz的频率,一次只发送一字节出去,而不是一次性发完一帧(特别是一帧数据很多字节的情况,万不可如此)。

    无论是接收数据、帧解析还是发送数据都是1字节1字节的处理,这样做的好处是,能够减少主程序对CPU时间片的需求。 进一步避免由于中断程序占用CPU时间片过长的问题。 如果中断占用CPU时间过长, 而主程序又是一次性处理很多数据(包括帧解析和发送),那么很有可能出现CPU一直被中断占用,而主程序又无法正常的执行的情况,最终呈现出来就是你上位机给DSP发信息,DSP直接无响应, DSP也无法发信息给上位机。

    鲁棒的程序写法是,中断程序对CPU占用要尽量的减少,甚至一些不那么重要的但需要实时运行的程序比如ADC数据采集也可以放在while(1)之下,而不要全部放在中断程序中。  比如你ADC数据采集花了特别多的时间,导致中断处理程序的运行时间基本上快达到中断触发周期了,那留给其他中断和主程序的CPU时间片已经不多了,这就会导致程序异常,特别是主程序如果帧解析不是1个字节1个字节的解析的,发送数据也不是1个字节一个字节发送的。 这时候主程序的运行肯定就不正常了。 

    总得来说,中断程序一定是尽量的用时短而美,主程序while(1)里面的数据处理程序也是尽量的用时短而美,而不是一次性做很复杂的操作。

    目录

    串口配置和单字节队列处理 

    主程序和主中断


    串口配置和单字节队列处理 

    1. /*
    2. * scib.h
    3. *
    4. */
    5. #ifndef USERPROGRAM_PC_SCIB_H_
    6. #define USERPROGRAM_PC_SCIB_H_
    7. #include
    8. // Select the Baud rate of SCI
    9. // #define _BAUD_9600_
    10. // #define _BAUD_38400_
    11. #define _BAUD_115200_
    12. //#define _BAUD_256000_
    13. //#define _BAUD_187500_
    14. // #define _BAUD_460800_
    15. //#define _BAUD_921600_
    16. void para_init(void);
    17. void configScib(void);
    18. void configScibGpio(void);
    19. void configScibRegister(void);
    20. void ScibInterruptInit(void);
    21. #define MAX_RX_FRAME_SIZE 100
    22. extern Uint16 u_data_frame_array[MAX_RX_FRAME_SIZE];
    23. extern Uint16 u_data_frame_length;
    24. extern Uint16 is_data_frame_available;
    25. interrupt void getScib(void);
    26. void sendScib(void);
    27. void get_data_frame(void);
    28. #endif /* USERPROGRAM_PC_SCIB_H_ */

    1. /*
    2. * scib.c
    3. *
    4. */
    5. #include
    6. // The communication port for display and control debugging is generally serial port
    7. // SCI, Serial Communications Interface
    8. void configScib(void)
    9. {
    10. para_init();
    11. configScibGpio();
    12. configScibRegister();
    13. ScibInterruptInit();
    14. }
    15. // Configure the multiplexing function of GPIO pin used by Scic
    16. void configScibGpio(void)
    17. {
    18. EALLOW;
    19. GpioCtrlRegs.GPAPUD.bit.GPIO18 = 0; // Enable pull-up for GPIO18 (SCITXDB)
    20. GpioCtrlRegs.GPAPUD.bit.GPIO19 = 0; // Enable pull-up for GPIO19 (SCIRXDB)
    21. GpioCtrlRegs.GPAQSEL2.bit.GPIO19 = 3; // Asynch input GPIO19 (SCIRXDB)
    22. GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 2; // Configure GPIO18 for SCITXDB operation
    23. GpioCtrlRegs.GPAMUX2.bit.GPIO19 = 2; // Configure GPIO19 for SCIRXDB operation
    24. EDIS;
    25. }
    26. // Configure relevant registers of Scic
    27. void configScibRegister(void)
    28. {
    29. /*---------------------------------------------SCICCR-------------------------------------------------------------*/
    30. // SCI number of stop bits
    31. ScibRegs.SCICCR.bit.STOPBITS = 0; // 0: One stop bit ▲
    32. // 1: Two stop bit
    33. // SCI parity odd/even selection
    34. ScibRegs.SCICCR.bit.PARITY = 0; // 0: Odd parity
    35. // 1: Even parity
    36. // SCI parity enable
    37. ScibRegs.SCICCR.bit.PARITYENA = 0; // 0: Parity disabled ▲
    38. // 1: Parity is enabled
    39. // Loop Back test mode enable
    40. ScibRegs.SCICCR.bit.LOOPBKENA = 0; // 0: Loop Back test mode disabled
    41. // 1: Loop Back test mode enabled
    42. // SCI multiprocessor mode control bit
    43. ScibRegs.SCICCR.bit.ADDRIDLE_MODE = 0; // 0: Idle-line mode protocol selected
    44. // 1: Address-bit mode protocol selected
    45. // Character-length control bits 2-0
    46. ScibRegs.SCICCR.bit.SCICHAR = 7; // 0: SCICHAR_LENGTH_1 ▲
    47. // 1: SCICHAR_LENGTH_2
    48. // 7: SCICHAR_LENGTH_8
    49. /*---------------------------------------------SCICTL1--------------------------------------------------------------*/
    50. // SCI receive error interrupt enable
    51. ScibRegs.SCICTL1.bit.RXERRINTENA = 0; // 0: Receive error interrupt disabled
    52. // 1: Receive error interrupt enabled
    53. // SCI software reset (active low)
    54. ScibRegs.SCICTL1.bit.SWRESET = 0; // 0: Writing a 0 to this bit initializes the SCI state machines
    55. // and operating flags (registers SCICTL2 and SCIRXST) to the reset condition.
    56. // 1: After a system reset, re-enable the SCI by writing a 1 to this bit.
    57. // SCI transmitter wake-up method select
    58. ScibRegs.SCICTL1.bit.TXWAKE = 0; // 0: Transmit feature is not selected. In idle-line mode: write
    59. // a 1 to TXWAKE, then write data to register SCITXBUF to generate
    60. // an idle period of 11 data bits / In address-bit mode: write a 1 to
    61. // TXWAKE, then write data to SCITXBUF to set the address bit for
    62. // that frame to 1
    63. // SCI sleep.
    64. ScibRegs.SCICTL1.bit.SLEEP = 0; // 0: Sleep mode disabled
    65. // 1: Sleep mode enabled
    66. // SCI Transmitter enable
    67. ScibRegs.SCICTL1.bit.TXENA = 1; // 0: Transmitter disabled ▲
    68. // 1: Transmitter enabled
    69. // SCI receiver enable
    70. ScibRegs.SCICTL1.bit.RXENA = 1; // 0: Prevent received characters from transfer into the ▲
    71. // SCIRXEMU and SCIRXBUF receiver buffers
    72. // 1: Send received characters to SCIRXEMU and SCIRXBUF
    73. /*---------------------------------------------SCICTL2------------------------------------------------------------*/
    74. ScibRegs.SCICTL2.bit.TXINTENA = 1; // SCITXBUF-register interrupt enable ▲
    75. ScibRegs.SCICTL2.bit.RXBKINTENA = 1; // Receiver-buffer/break interrupt enable ▲
    76. /*---------------------------------------------SCIHBAUD & SCILBAUD-------------------------------------------------*/
    77. // BRR = (SCIHBAUD << 8) + (SCILBAUD) = LSPCLK / (SCI Asynchronous Baud * 8) - 1
    78. #ifdef _BAUD_9600_
    79. ScibRegs.SCIHBAUD = 0x0001; // 9600 baud @ LSPCLK = 150/4 = 37.5MHz. ▲
    80. ScibRegs.SCILBAUD = 0x00E7; // 487.28d → 487d
    81. #endif
    82. #ifdef _BAUD_38400_
    83. ScibRegs.SCIHBAUD = 0x0000; // 38400 baud @ LSPCLK = 150/4 = 37.5MHz. ▲
    84. ScibRegs.SCILBAUD = 0x0079; // 121.07d → 121d
    85. #endif
    86. #ifdef _BAUD_115200_
    87. ScibRegs.SCIHBAUD = 0x0000; // 115200 baud @ LSPCLK = 150/4 = 37.5MHz. ▲
    88. ScibRegs.SCILBAUD = 0x0028; // 39.69d → 40d
    89. #endif
    90. #ifdef _BAUD_187500_
    91. ScibRegs.SCIHBAUD = 0x0000; // 115200 baud @ LSPCLK = 150/4 = 37.5MHz. ▲
    92. ScibRegs.SCILBAUD = 0x0018; // 39.69d → 40d
    93. #endif
    94. #ifdef _BAUD_256000_
    95. ScibRegs.SCIHBAUD = 0x0000; // 460800 baud @ LSPCLK = 150/4 = 37.5MHz. ▲
    96. ScibRegs.SCILBAUD = 0x0011; // 39.69d → 40d
    97. #endif
    98. #ifdef _BAUD_460800_
    99. ScibRegs.SCIHBAUD = 0x0000; // 460800 baud @ LSPCLK = 150/4 = 37.5MHz. ▲
    100. ScibRegs.SCILBAUD = 0x0009; // 39.69d → 40d
    101. #endif
    102. #ifdef _BAUD_921600_
    103. ScibRegs.SCIHBAUD = 0x0000; // 460800 baud @ LSPCLK = 150/4 = 37.5MHz. ▲
    104. ScibRegs.SCILBAUD = 0x0004; // 39.69d → 40d
    105. #endif
    106. // Relinquish SCI from Reset 【迷惑行为】
    107. ScibRegs.SCICTL1.all = 0x0023;
    108. /*---------------------------------------------SCIFFTX & SCIFFRX------------------------------------- ------------*/
    109. ScibRegs.SCIFFTX.bit.SCIRST = 1; // SCI Reset
    110. ScibRegs.SCIFFTX.bit.SCIFFENA = 1; // SCI FIFO enable
    111. ScibRegs.SCIFFTX.bit.TXFIFOXRESET = 1; // Transmit FIFO reset
    112. ScibRegs.SCIFFTX.bit.TXFFST = 0; // FIFO status 0: Transmit FIFO is empty
    113. // 1: Transmit FIFO has 1 words
    114. // 10H: Transmit FIFO has 16 words
    115. ScibRegs.SCIFFTX.bit.TXFFINTCLR = 0; // Transmit FIFO clear
    116. ScibRegs.SCIFFTX.bit.TXFFIENA = 0; // Transmit FIFO interrrupt enable
    117. ScibRegs.SCIFFTX.bit.TXFFIL = 9; // TXFFIL4-0 Transmit FIFO interrupt level bits.
    118. ScibRegs.SCIFFRX.bit.RXFFOVRCLR = 1; // RXFFOVF(Receive FIFO overflow) clear
    119. ScibRegs.SCIFFRX.bit.RXFIFORESET = 1; // Receive FIFO reset
    120. ScibRegs.SCIFFRX.bit.RXFFST = 6; // FIFO status 0: Receive FIFO is empty
    121. // 1: Receive FIFO has 1 words
    122. // 10H: Receive FIFO has 16 words
    123. ScibRegs.SCIFFRX.bit.RXFFINTCLR = 1; // Receive FIFO interrupt clear
    124. ScibRegs.SCIFFRX.bit.RXFFIENA = 1; // Receive FIFO interrupt enable
    125. // ScibRegs.SCIFFRX.bit.RXFFIL = 6; // Receive FIFO interrupt level bits
    126. ScibRegs.SCIFFRX.bit.RXFFIL = 1; // Receive FIFO interrupt level bits
    127. }
    128. void ScibInterruptInit(void){
    129. // SCIBRXINT INT9.3
    130. // SCIBTXINT INT9.4
    131. // SCICRXINT Initial
    132. EALLOW;
    133. PieVectTable.SCIRXINTB = &getScib; // Specify the interrupt service routine
    134. EDIS;
    135. IER |= M_INT9; // Enable CPU Level interrupt
    136. PieCtrlRegs.PIEIER9.bit.INTx3=1; // Enable PIE Level interrupt
    137. // SCICTXINT Initial
    138. // TODO
    139. }
    140. // get one byte and push
    141. interrupt void getScib(void)
    142. {
    143. Uint16 data = ScibRegs.SCIRXBUF.all;
    144. enqueue(&scib_rx_queue, data);
    145. ScibRegs.SCIFFRX.bit.RXFIFORESET = 0;
    146. ScibRegs.SCIFFRX.bit.RXFIFORESET = 1;
    147. ScibRegs.SCIFFRX.bit.RXFFINTCLR = 1;
    148. PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;
    149. }
    150. // pop and send one byte
    151. void sendScib(void)
    152. {
    153. Uint16 data = 0;
    154. int ret = dequeue(&scib_tx_queue, &data);
    155. if(ret == 1){
    156. ScibRegs.SCITXBUF = data;
    157. while (ScibRegs.SCICTL2.bit.TXRDY == 0);
    158. }
    159. }
    160. Uint16 last_data = 0;
    161. Uint16 is_framehead_identified = 0;
    162. Uint16 u_data_frame_effective_cnt = 0;
    163. Uint16 u_data_frame_array[MAX_RX_FRAME_SIZE];
    164. Uint16 is_data_frame_available = 0;
    165. Uint16 u_data_frame_length = 0;
    166. void para_init(void)
    167. {
    168. int i = 0;
    169. queue_init(&scib_rx_queue);
    170. queue_init(&scib_tx_queue);
    171. for(i = 0; i < MAX_RX_FRAME_SIZE; i++){
    172. u_data_frame_array[i] = 0;
    173. }
    174. }
    175. // Indefinite length data frame acquisition
    176. void get_data_frame(void)
    177. {
    178. Uint16 data = 0;
    179. int ret = dequeue(&scib_rx_queue, &data);
    180. // The scib_rx_queue is not empty
    181. if(ret == 1){
    182. // Frame head recognition
    183. if(last_data==0x55 && data==0xAA){
    184. is_framehead_identified = 1;
    185. u_data_frame_array[0] = 0x55;
    186. u_data_frame_array[1] = 0xAA;
    187. u_data_frame_effective_cnt = 2;
    188. // Frame tail recognition
    189. }else if(is_framehead_identified==1 && last_data==0x66 && data==0x88){
    190. u_data_frame_array[u_data_frame_effective_cnt] = data;
    191. u_data_frame_length = u_data_frame_effective_cnt + 1;
    192. is_data_frame_available = 1;
    193. // save Frame data
    194. }else if(is_framehead_identified==1 && u_data_frame_effective_cnt < (MAX_RX_FRAME_SIZE-1)){
    195. u_data_frame_array[u_data_frame_effective_cnt] = data;
    196. u_data_frame_effective_cnt++;
    197. // frame length error
    198. }else if(is_framehead_identified==1 && u_data_frame_effective_cnt >= (MAX_RX_FRAME_SIZE-1)){
    199. last_data = 0;
    200. u_data_frame_effective_cnt = 0;
    201. is_framehead_identified = 0;
    202. }
    203. last_data = data;
    204. // The scib_rx_queue is empty
    205. }else{
    206. return;
    207. }
    208. }
    209. /*
    210. void sendScib(void)
    211. {
    212. if(u_fr_tx_flag==1 && u_fr_tx_index < 16){
    213. ScibRegs.SCITXBUF = u_fr_tx_buf[u_fr_tx_index] & 0x00FF;
    214. u_fr_tx_index++;
    215. while (ScibRegs.SCICTL2.bit.TXRDY == 0);
    216. }else{
    217. u_fr_tx_flag = 0;
    218. u_fr_tx_index = 0;
    219. }
    220. }
    221. */

    主程序和主中断

    1. while(1)
    2. {
    3. // 10kHz
    4. if(iFreq10000Hz == 1){
    5. get_adc(); // ADC如果耗时过多,可以放在主程序里,系统也能实现接近10kHz的性能
    6. // ADC如果耗时不多,放在主程序里,系统肯定是实现10kHz的性能
    7. // Pop and handle 【one byte】
    8. get_data_frame();
    9. // Execute PC Command
    10. if(is_data_frame_available==1){
    11. fr_recv_decode();
    12. is_data_frame_available = 0;
    13. }
    14. // Pop and send 【one byte】
    15. sendScib();
    16. //runtime_stop(&runtime1);
    17. iFreq10000Hz = 0;
    18. }
    19. }
    1. // 用时最多的主中断(可能其他中断用0.几~几us,主中断用 < 50us的运行时间)
    2. // 10kHz的话,相当于留50%的时间片给其他的中断和主程序
    3. // 即使主程序运行一回要超过50us(但超过的不多哈),那整个系统基本上也是10kHz实时运行的。
    4. interrupt void SystemMain(void)
    5. {
    6. // do your main task
    7. // 用时短 而 美
    8. iFreq10000Hz = 1;
    9. PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
    10. }

    最后愿我们共同进步! 感谢您的阅读,欢迎留言讨论、收藏、点赞、分享。 

  • 相关阅读:
    CMake+CLion+Qt配置
    maven打完包运行,测试结果和本地不一致
    【计算机视觉 | 图像模型】常见的计算机视觉 image model(CNNs & Transformers) 的介绍合集(九)
    1.1 Joyvan的矩阵
    HTTP/1.1和HTTP/2的区别
    智慧工地管理系统源码 智慧大屏、手机APP、SaaS模式
    SpringBean生命周期&扩展接口&简化配置
    nanomsg 广播 问题
    PowerCLi 通过自建PXE Server实现vCenter批量部署常规New-VM到所有的esxi主机上
    聊聊Netty那些事儿之从内核角度看IO模型
  • 原文地址:https://blog.csdn.net/heqiunong/article/details/134265712