[STM32F1] USART实现串口通讯

[复制链接]
1523|7
 楼主| gaoyang9992006 发表于 2016-3-28 19:42 | 显示全部楼层 |阅读模式

STM32F10x 系列单片机中都包含了USART 模块,所谓USART,就是通用同步异步收发器。通用同步异步收发器(USART)提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。它支持同步单向通信和半双工单线通信,也支持LIN(局部互连网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。


从前面的介绍可知USART模块功能非常的强大。这里我只简单讲讲如何用USART模块来实现标准EIA-232 串口通讯。


用过单片机的人肯定都接触过串口,设置串口无非就是设置波特率、数据位、停止位、奇偶校验位。发送接收也就三种基本方式,轮询、中断和DMA。STM32F10x 的USART 模块也不过如此。所以我重点讲讲我在调试代码时犯得各种错误,那些很容易得到的代码就不详细的讲解了。


首先说说我的硬件环境。还是那块神舟4号开发板,用的是串口2,对应的是USART2。默认情况下USART2是连接到IO端口A的,但是我这里需要将USART的管腿重定向到IO端口D上。具体的管腿的关系参见下表。这个表是从STM32参考手册上拷下来的。

初始化USART的代码很简单。USART2 连接到APB1 总线上了,先要打开USART2的时钟,然后设置波特率一类的参数。

  1. USART_InitTypeDef USART_InitStructure;

  2. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  3. USART_InitStructure.USART_BaudRate = 9600;
  4. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  5. USART_InitStructure.USART_StopBits = USART_StopBits_1;
  6. USART_InitStructure.USART_Parity = USART_Parity_No;
  7. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  8. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  9. USART_Init(USART2, &USART_InitStructure );

这样设置了还不能使用。因为我们将USART2 重定向了。重定向操作需要写复用重映射和调试I/O配置寄存器(AFIO_MAPR)。GPIO_PinRemapConfig() 可以完成这项任务。

GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);

这样操作还不够。STM32参考手册上有这么一段话:

对寄存器AFIO_EVCR,AFIO_MAPR和AFIO_EXTICRX进行读写操作前,应当首先打开AFIO的时钟。参考第6.3.7节APB2外设时钟使能寄存器(RCC_APB2ENR)。

所以需要先打开AFIO的时钟。因此,USART2的重定向需要两步操作:

  • RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  
  • GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);  

我原以为这样就能工作了,可是结果还是什么都没有输出。没办法只能继续研究。在读GPIO的相关章节时看到下图让我恍然大悟。


USART2的输入输出都是借用PD口管腿,PD 口的时钟却还没给。用到的几个IO 端口也没有设置相应的输入输出状态。在读到8.1.9 复用功能配置这一小节时发现了如下的表格。


按照上面给出的配置,写好程序:

  1. GPIO_InitTypeDef GPIO_InitStructure;
  2. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD , ENABLE);
  3. /* Configure USART Tx as alternate function push-pull */
  4. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  5. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  6. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  7. GPIO_Init(GPIOD, &GPIO_InitStructure);
  8.        
  9. /* Configure USART Rx as input floating */
  10. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  11. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  12. GPIO_Init(GPIOD, &GPIO_InitStructure);

再次测试,一切正常。

发送一个字符的函数可以这么写:

void UART_PutChar(USART_TypeDef* USARTx, uint8_t Data)
{
        while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET ) {};
        USART_SendData (USARTx, Data);
}
这个函数可以手工优化一下,里面的两个函数调用都可以去掉,甚至于这个函数可以用汇编来实现或者写成inline 函数。不过这里只是个示例代码,没有考虑这些。


发送字符串的函数如下:

void UART_PutStr (USART_TypeDef* USARTx, uint8_t *str)
{
    while (0 != *str)
    {
        UART_PutChar(USARTx, *str);
        str++;
    }
}
上面串口初始化的代码可以放到一个函数中:
  1. void USART2_init(void)
  2. {
  3.         GPIO_InitTypeDef GPIO_InitStructure;
  4.         USART_InitTypeDef USART_InitStructure;

  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
  6.        
  7.         /* Configure USART Tx as alternate function push-pull */
  8.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  9.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  10.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  11.         GPIO_Init(GPIOD, &GPIO_InitStructure);
  12.        
  13.         /* Configure USART Rx as input floating */
  14.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  15.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  16.         GPIO_Init(GPIOD, &GPIO_InitStructure);
  17.        
  18.         GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
  19.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

  20.         USART_InitStructure.USART_BaudRate = 9600;
  21.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  22.         USART_InitStructure.USART_StopBits = USART_StopBits_1;
  23.         USART_InitStructure.USART_Parity = USART_Parity_No;
  24.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  25.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  26.         USART_Init(USART2, &USART_InitStructure );

  27.     USART_Cmd(USART2, ENABLE);
  28. }
接收字符的函数与发送字符的函数差不多,但是这种轮询方式效率很低,不建议使用。下次写一篇介绍如何用中断方式发送接收串口数据,中断方式的效率会高很多。


 楼主| gaoyang9992006 发表于 2016-3-28 19:43 | 显示全部楼层

这次讲讲利用串口收发中断来进行串口通讯。STM32 上为每个串口分配了一个中断。也就是说无论是发送完成还是收到数据或是数据溢出都产生同一个中断。程序需在中断处理函数中读取状态寄存器(USART_SR)来判断当前的是什么中断。下面的中断映像图给出了这些中断源是如何汇合成最终的中断信号的。图中也给出了如何控制每一个单独的中断源是否起作用。


另外,Cortex-M3 内核中还有个NVIC,可以控制这里的中断信号是否触发中断处理函数的执行,还有这些外部中断的级别。关于NVIC 可以参考《ARM CortexM3 权威指南》,里面讲解的非常详细。

简单的说,为了开启中断,我们需要如下的代码:

  1. NVIC_InitTypeDef NVIC_InitStructure;
  2. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  3. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  4. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  5. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  6. NVIC_Init(&NVIC_InitStructure);

  7. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启接收中断
  8. USART_ITConfig(USART1, USART_IT_TXE, ENABLE); // 开启发送中断

这里多说一句,串口的发送中断有两个,分别是:

  • l发送数据寄存器空中断(TXE
  • l发送完成中断(TC

一般来说我们会使用发送数据寄存器空中断,用这个中断发送的效率会高一些。

中断处理函数的框架如下,如果检测到错误就清除错误,收到数了就处理。发完当前数据了就发下一个。

  1. void USART1_IRQHandler(void)
  2. {
  3.     unsigned int data;

  4.     if(USART1->SR & 0x0F)
  5.     {
  6.         // See if we have some kind of error, Clear interrupt   
  7.         data = USART1->DR;
  8.     }
  9.     else if(USART1->SR & USART_FLAG_RXNE) //Receive Data Reg Full Flag
  10.     {  
  11.         data = USART1->DR;
  12.         // 对收到的数据进行处理,或者干些其他的事  
  13.     }
  14.     else if(USART1->SR & USART_FLAG_TXE)
  15.     {
  16.         { // 可以发送数据了,如果没有数据需要发送,就在这里关闭发送中断
  17.             USART1->DR =  something;        // Yes, Send character                     
  18.         }                                          
  19.     }  
  20. }   


 楼主| gaoyang9992006 发表于 2016-3-28 19:48 | 显示全部楼层
下面给一个利用环形缓冲区的串口驱动程序。
  1. #ifndef _COM_BUFFERED_H_
  2. #define _COM_BUFFERED_H_

  3. #define  COM1                   0
  4. #define  COM2                   1

  5. #define  COM_RX_BUF_SIZE        64                /* Number of characters in Rx ring buffer             */
  6. #define  COM_TX_BUF_SIZE        64                /* Number of characters in Tx ring buffer             */

  7. #define  COM_NO_ERR             0                /* Function call was successful                       */
  8. #define  COM_BAD_CH             1                /* Invalid communications port channel                */
  9. #define  COM_RX_EMPTY           2                /* Rx buffer is empty, no character available         */
  10. #define  COM_TX_FULL            3                /* Tx buffer is full, could not deposit character     */
  11. #define  COM_TX_EMPTY           4                /* If the Tx buffer is empty.                         */


  12. /************************************************************
  13. * function : COMGetCharB
  14. * parameter: char port, port can be COM1 / COM2
  15. * parameter: char* err   is a pointer to where an error code will be placed:
  16. *                   *err is set to COM_NO_ERR   if a character is available
  17. *                   *err is set to COM_RX_EMPTY if the Rx buffer is empty
  18. *                   *err is set to COM_BAD_CH   if you have specified an invalid channel
  19. * return   : char
  20. * usage    : This function is called by your application to obtain a character from the communications
  21. *               channel.
  22. * changelog:
  23. *************************************************************/
  24. unsigned char  COMGetCharB (unsigned char ch, unsigned char *err);

  25. /************************************************************
  26. * function : COMPutCharB
  27. * parameter: char port, port can be COM1 / COM2
  28. * return   :    COMM_NO_ERR   if the function was successful (the buffer was not full)
  29. *               COMM_TX_FULL  if the buffer was full
  30. *               COMM_BAD_CH   if you have specified an incorrect channel

  31. * usage    : This function is called by your application to send a character on the communications
  32. *               channel.  The character to send is first inserted into the Tx buffer and will be sent by
  33. *               the Tx ISR.  If this is the first character placed into the buffer, the Tx ISR will be
  34. *               enabled.  If the Tx buffer is full, the character will not be sent (i.e. it will be lost)
  35. * changelog:
  36. *************************************************************/
  37. unsigned char COMPutCharB (unsigned char port, unsigned char c);

  38. /************************************************************
  39. * function : COMBufferInit
  40. * parameter:
  41. * return   :   
  42. * usage    : This function is called by your application to initialize the communications module.  You
  43. *             must call this function before calling any other functions.
  44. * changelog:
  45. *************************************************************/
  46. void  COMBufferInit (void);

  47. /************************************************************
  48. * function : COMBufferIsEmpty
  49. * parameter: char port, port can be COM1 / COM2
  50. * return   : char
  51. * usage    : This function is called by your application to see
  52. *            if any character is available from the communications channel.
  53. *            If at least one character is available, the function returns
  54. *            FALSE(0) otherwise, the function returns TRUE(1).
  55. * changelog:
  56. *************************************************************/
  57. unsigned char  COMBufferIsEmpty (unsigned char port);

  58. /************************************************************
  59. * function : COMBufferIsFull
  60. * parameter: char port, port can be COM1 / COM2
  61. * return   : char
  62. * usage    : This function is called by your application to see if any more characters can be placed
  63. *             in the Tx buffer.  In other words, this function check to see if the Tx buffer is full.
  64. *             If the buffer is full, the function returns TRUE otherwise, the function returns FALSE.
  65. * changelog:
  66. *************************************************************/
  67. unsigned char COMBufferIsFull (unsigned char port);

  68. #endif
  1. /*
  2. * file: com_buffered.c
  3. * author: Li Yuan
  4. * platform: STM32F107
  5. * date: 2013-5-5
  6. * version: 0.0.1
  7. * description: UART Ring Buffer                           
  8. **/

  9. #include "stm32f10x_usart.h"
  10. #include "com_buffered.h"

  11. #define OS_ENTER_CRITICAL()     __set_PRIMASK(1)
  12. #define OS_EXIT_CRITICAL()      __set_PRIMASK(0)   
  13.    
  14. /**
  15. *        Enables Transmiter interrupt.
  16. **/
  17. static void COMEnableTxInt(unsigned char port)
  18. {
  19.         static USART_TypeDef* map[2] = {USART1, USART2};
  20.         USART_ITConfig(map[port], USART_IT_TXE, ENABLE);       
  21. }
  22. /*
  23. *********************************************************************************************************
  24. *                                               DATA TYPES
  25. *********************************************************************************************************
  26. */
  27. typedef struct {
  28.     short  RingBufRxCtr;                   /* Number of characters in the Rx ring buffer              */
  29.     unsigned char  *RingBufRxInPtr;                 /* Pointer to where next character will be inserted        */
  30.     unsigned char  *RingBufRxOutPtr;                /* Pointer from where next character will be extracted     */
  31.     unsigned char   RingBufRx[COM_RX_BUF_SIZE];     /* Ring buffer character storage (Rx)                      */
  32.     short  RingBufTxCtr;                   /* Number of characters in the Tx ring buffer              */
  33.     unsigned char  *RingBufTxInPtr;                 /* Pointer to where next character will be inserted        */
  34.     unsigned char  *RingBufTxOutPtr;                /* Pointer from where next character will be extracted     */
  35.     unsigned char   RingBufTx[COM_TX_BUF_SIZE];     /* Ring buffer character storage (Tx)                      */
  36. } COM_RING_BUF;

  37. /*
  38. *********************************************************************************************************
  39. *                                            GLOBAL VARIABLES
  40. *********************************************************************************************************
  41. */

  42. COM_RING_BUF  COM1Buf;
  43. COM_RING_BUF  COM2Buf;


  44. /************************************************************
  45. * function : COMGetCharB
  46. * parameter: char port, port can be COM1 / COM2
  47. * parameter: char* err   is a pointer to where an error code will be placed:
  48. *                   *err is set to COM_NO_ERR   if a character is available
  49. *                   *err is set to COM_RX_EMPTY if the Rx buffer is empty
  50. *                   *err is set to COM_BAD_CH   if you have specified an invalid channel
  51. * return   : char
  52. * usage    : This function is called by your application to obtain a character from the communications
  53. *               channel.
  54. * changelog:
  55. *************************************************************/
  56. unsigned char  COMGetCharB (unsigned char port, unsigned char *err)
  57. {
  58. //    unsigned char cpu_sr;
  59.    
  60.     unsigned char c;
  61.     COM_RING_BUF *pbuf;

  62.     switch (port)
  63.     {                                          /* Obtain pointer to communications channel */
  64.         case COM1:
  65.              pbuf = &COM1Buf;
  66.              break;

  67.         case COM2:
  68.              pbuf = &COM2Buf;
  69.              break;

  70.         default:
  71.              *err = COM_BAD_CH;
  72.              return (0);
  73.     }
  74.     OS_ENTER_CRITICAL();
  75.     if (pbuf->RingBufRxCtr > 0)                            /* See if buffer is empty                   */
  76.     {                                                      
  77.         pbuf->RingBufRxCtr--;                              /* No, decrement character count            */
  78.         c = *pbuf->RingBufRxOutPtr++;                      /* Get character from buffer                */
  79.         if (pbuf->RingBufRxOutPtr == &pbuf->RingBufRx[COM_RX_BUF_SIZE])
  80.         {      
  81.             pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];   /* Wrap OUT pointer     */
  82.         }
  83.         OS_EXIT_CRITICAL();
  84.         *err = COM_NO_ERR;
  85.         return (c);
  86.     } else {
  87.         OS_EXIT_CRITICAL();
  88.         *err = COM_RX_EMPTY;
  89.         c    = 0;                                        /* Buffer is empty, return 0              */
  90.         return (c);
  91.     }
  92. }


  93. /************************************************************
  94. * function : COMPutCharB
  95. * parameter: char port, port can be COM1 / COM2
  96. * return   :    COMM_NO_ERR   if the function was successful (the buffer was not full)
  97. *               COMM_TX_FULL  if the buffer was full
  98. *               COMM_BAD_CH   if you have specified an incorrect channel

  99. * usage    : This function is called by your application to send a character on the communications
  100. *               channel.  The character to send is first inserted into the Tx buffer and will be sent by
  101. *               the Tx ISR.  If this is the first character placed into the buffer, the Tx ISR will be
  102. *               enabled.  If the Tx buffer is full, the character will not be sent (i.e. it will be lost)
  103. * changelog:
  104. *            1.first implimented by liyuan 2010.11.5
  105. *************************************************************/
  106. unsigned char COMPutCharB (unsigned char port, unsigned char c)
  107. {
  108. //    unsigned char cpu_sr;
  109.    
  110.     COM_RING_BUF *pbuf;
  111.     switch (port)
  112.     {                                                     /* Obtain pointer to communications channel */
  113.         case COM1:
  114.              pbuf = &COM1Buf;
  115.              break;

  116.         case COM2:
  117.              pbuf = &COM2Buf;
  118.              break;

  119.         default:
  120.              return (COM_BAD_CH);
  121.     }

  122.     OS_ENTER_CRITICAL();
  123.     if (pbuf->RingBufTxCtr < COM_TX_BUF_SIZE) {           /* See if buffer is full                    */
  124.         pbuf->RingBufTxCtr++;                              /* No, increment character count            */
  125.         *pbuf->RingBufTxInPtr++ = c;                       /* Put character into buffer                */
  126.         if (pbuf->RingBufTxInPtr == &pbuf->RingBufTx[COM_TX_BUF_SIZE]) { /* Wrap IN pointer           */
  127.             pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0];
  128.         }
  129.         if (pbuf->RingBufTxCtr == 1) {                     /* See if this is the first character       */
  130.             COMEnableTxInt(port);                          /* Yes, Enable Tx interrupts                */
  131.             OS_EXIT_CRITICAL();
  132.         } else {
  133.             OS_EXIT_CRITICAL();
  134.         }
  135.         return (COM_NO_ERR);
  136.     } else {
  137.         OS_EXIT_CRITICAL();
  138.         return (COM_TX_FULL);
  139.     }
  140. }

  141. /************************************************************
  142. * function : COMBufferInit
  143. * parameter:
  144. * return   :   
  145. * usage    : This function is called by your application to initialize the communications module.  You
  146. *             must call this function before calling any other functions.
  147. * changelog:
  148. *************************************************************/
  149. void  COMBufferInit (void)
  150. {
  151.     COM_RING_BUF *pbuf;

  152.     pbuf                  = &COM1Buf;                     /* Initialize the ring buffer for COM0     */
  153.     pbuf->RingBufRxCtr    = 0;
  154.     pbuf->RingBufRxInPtr  = &pbuf->RingBufRx[0];
  155.     pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];
  156.     pbuf->RingBufTxCtr    = 0;
  157.     pbuf->RingBufTxInPtr  = &pbuf->RingBufTx[0];
  158.     pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];

  159.     pbuf                  = &COM2Buf;                     /* Initialize the ring buffer for COM1     */
  160.     pbuf->RingBufRxCtr    = 0;
  161.     pbuf->RingBufRxInPtr  = &pbuf->RingBufRx[0];
  162.     pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];
  163.     pbuf->RingBufTxCtr    = 0;
  164.     pbuf->RingBufTxInPtr  = &pbuf->RingBufTx[0];
  165.     pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];
  166. }

  167. /************************************************************
  168. * function : COMBufferIsEmpty
  169. * parameter: char port, port can be COM1 / COM2
  170. * return   : char
  171. * usage    : This function is called by your application to see
  172. *            if any character is available from the communications channel.
  173. *            If at least one character is available, the function returns
  174. *            FALSE(0) otherwise, the function returns TRUE(1).
  175. * changelog:
  176. *************************************************************/
  177. unsigned char  COMBufferIsEmpty (unsigned char port)
  178. {
  179. //    unsigned char cpu_sr;
  180.    
  181.     unsigned char empty;
  182.     COM_RING_BUF *pbuf;
  183.     switch (port)
  184.     {                                                     /* Obtain pointer to communications channel */
  185.         case COM1:
  186.              pbuf = &COM1Buf;
  187.              break;

  188.         case COM2:
  189.              pbuf = &COM2Buf;
  190.              break;

  191.         default:
  192.              return (1);
  193.     }
  194.     OS_ENTER_CRITICAL();
  195.     if (pbuf->RingBufRxCtr > 0)
  196.     {                                                      /* See if buffer is empty                   */
  197.         empty = 0;                                         /* Buffer is NOT empty                      */
  198.     }
  199.     else
  200.     {
  201.         empty = 1;                                         /* Buffer is empty                          */
  202.     }
  203.     OS_EXIT_CRITICAL();
  204.     return (empty);
  205. }


  206. /************************************************************
  207. * function : COMBufferIsFull
  208. * parameter: char port, port can be COM1 / COM2
  209. * return   : char
  210. * usage    : This function is called by your application to see if any more characters can be placed
  211. *             in the Tx buffer.  In other words, this function check to see if the Tx buffer is full.
  212. *             If the buffer is full, the function returns TRUE otherwise, the function returns FALSE.
  213. * changelog:
  214. *************************************************************/
  215. unsigned char COMBufferIsFull (unsigned char port)
  216. {
  217. //    unsigned char cpu_sr;
  218.    
  219.     char full;
  220.     COM_RING_BUF *pbuf;
  221.     switch (port)
  222.     {                                                     /* Obtain pointer to communications channel */
  223.         case COM1:
  224.              pbuf = &COM1Buf;
  225.              break;

  226.         case COM2:
  227.              pbuf = &COM2Buf;
  228.              break;

  229.         default:
  230.              return (1);
  231.     }
  232.     OS_ENTER_CRITICAL();
  233.     if (pbuf->RingBufTxCtr < COM_TX_BUF_SIZE) {           /* See if buffer is full                    */
  234.         full = 0;                                      /* Buffer is NOT full                       */
  235.     } else {
  236.         full = 1;                                       /* Buffer is full                           */
  237.     }
  238.     OS_EXIT_CRITICAL();
  239.     return (full);
  240. }


  241. // This function is called by the Rx ISR to insert a character into the receive ring buffer.
  242. static void  COMPutRxChar (unsigned char port, unsigned char c)
  243. {
  244.     COM_RING_BUF *pbuf;

  245.     switch (port)
  246.     {                                                     /* Obtain pointer to communications channel */
  247.         case COM1:
  248.              pbuf = &COM1Buf;
  249.              break;

  250.         case COM2:
  251.              pbuf = &COM2Buf;
  252.              break;

  253.         default:
  254.              return;
  255.     }
  256.     if (pbuf->RingBufRxCtr < COM_RX_BUF_SIZE) {           /* See if buffer is full                    */
  257.         pbuf->RingBufRxCtr++;                              /* No, increment character count            */
  258.         *pbuf->RingBufRxInPtr++ = c;                       /* Put character into buffer                */
  259.         if (pbuf->RingBufRxInPtr == &pbuf->RingBufRx[COM_RX_BUF_SIZE]) { /* Wrap IN pointer           */
  260.             pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0];
  261.         }
  262.     }
  263. }


  264. // This function is called by the Tx ISR to extract the next character from the Tx buffer.
  265. //    The function returns FALSE if the buffer is empty after the character is extracted from
  266. //    the buffer.  This is done to signal the Tx ISR to disable interrupts because this is the
  267. //    last character to send.
  268. static unsigned char COMGetTxChar (unsigned char port, unsigned char *err)
  269. {
  270.     unsigned char c;
  271.     COM_RING_BUF *pbuf;

  272.     switch (port)
  273.     {                                          /* Obtain pointer to communications channel */
  274.         case COM1:
  275.              pbuf = &COM1Buf;
  276.              break;

  277.         case COM2:
  278.              pbuf = &COM2Buf;
  279.              break;

  280.         default:
  281.              *err = COM_BAD_CH;
  282.              return (0);
  283.     }
  284.     if (pbuf->RingBufTxCtr > 0) {                          /* See if buffer is empty                   */
  285.         pbuf->RingBufTxCtr--;                              /* No, decrement character count            */
  286.         c = *pbuf->RingBufTxOutPtr++;                      /* Get character from buffer                */
  287.         if (pbuf->RingBufTxOutPtr == &pbuf->RingBufTx[COM_TX_BUF_SIZE])
  288.         {     
  289.             pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];   /* Wrap OUT pointer     */
  290.         }
  291.         *err = COM_NO_ERR;
  292.         return (c);                                        /* Characters are still available           */
  293.     } else {
  294.         *err = COM_TX_EMPTY;
  295.         return (0);                                      /* Buffer is empty                          */
  296.     }
  297. }


  298. void USART1_IRQHandler(void)
  299. {
  300.     unsigned int data;
  301.     unsigned char err;

  302.     if(USART1->SR & 0x0F)
  303.     {
  304.         // See if we have some kind of error   
  305.         // Clear interrupt (do nothing about it!)   
  306.         data = USART1->DR;
  307.     }
  308.     else if(USART1->SR & USART_FLAG_RXNE) //Receive Data Reg Full Flag
  309.     {  
  310.         data = USART1->DR;
  311.         COMPutRxChar(COM1, data);                    // Insert received character into buffer     
  312.     }
  313.     else if(USART1->SR & USART_FLAG_TXE)
  314.     {
  315.         data = COMGetTxChar(COM1, &err);             // Get next character to send.               
  316.         if (err == COM_TX_EMPTY)
  317.         {                                            // Do we have anymore characters to send ?   
  318.                                                      // No,  Disable Tx interrupts               
  319.             //USART_ITConfig(USART1, USART_IT_TXE| USART_IT_TC, ENABLE);
  320.                         USART1->CR1 &= ~USART_FLAG_TXE | USART_FLAG_TC;
  321.         }
  322.         else
  323.         {
  324.             USART1->DR = data;        // Yes, Send character                     
  325.         }                                          
  326.     }  
  327. }   

  328. void USART2_IRQHandler(void)
  329. {
  330.     unsigned int data;
  331.     unsigned char err;

  332.     if(USART2->SR & 0x0F)
  333.     {
  334.         // See if we have some kind of error   
  335.         // Clear interrupt (do nothing about it!)   
  336.         data = USART2->DR;
  337.     }
  338.     else if(USART2->SR & USART_FLAG_RXNE) //Receive Data Reg Full Flag
  339.     {  
  340.         data = USART2->DR;
  341.         COMPutRxChar(COM2, data);                    // Insert received character into buffer     
  342.     }
  343.     else if(USART2->SR & USART_FLAG_TXE)
  344.     {
  345.         data = COMGetTxChar(COM2, &err);             // Get next character to send.               
  346.         if (err == COM_TX_EMPTY)
  347.         {                                            // Do we have anymore characters to send ?   
  348.                                                      // No,  Disable Tx interrupts               
  349.             //USART_ITConfig(USART2, USART_IT_TXE| USART_IT_TC, ENABLE);
  350.                         USART2->CR1 &= ~USART_FLAG_TXE | USART_FLAG_TC;
  351.         }
  352.         else
  353.         {
  354.             USART2->DR = data;        // Yes, Send character                     
  355.         }                                          
  356.     }  
  357. }


 楼主| gaoyang9992006 发表于 2016-3-28 19:50 | 显示全部楼层
下面给个例子主程序,来演示如何使用上面的串口驱动代码。
  1. #include "misc.h"
  2. #include "stm32f10x.h"
  3. #include "com_buffered.h"

  4. void UART_PutStrB (unsigned char port, uint8_t *str)
  5. {
  6.     while (0 != *str)
  7.     {
  8.         COMPutCharB(port, *str);
  9.         str++;
  10.     }
  11. }

  12. void USART1_Init(void)
  13. {
  14.         GPIO_InitTypeDef GPIO_InitStructure;
  15.         USART_InitTypeDef USART_InitStructure;
  16.         NVIC_InitTypeDef NVIC_InitStructure;

  17.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
  18.        
  19.         /* Configure USART Tx as alternate function push-pull */
  20.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  21.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  22.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  23.         GPIO_Init(GPIOA, &GPIO_InitStructure);
  24.        
  25.         /* Configure USART Rx as input floating */
  26.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  27.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  28.         GPIO_Init(GPIOA, &GPIO_InitStructure);

  29.         USART_InitStructure.USART_BaudRate = 9600;
  30.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  31.         USART_InitStructure.USART_StopBits = USART_StopBits_1;
  32.         USART_InitStructure.USART_Parity = USART_Parity_No;
  33.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  34.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  35.         USART_Init(USART1, &USART_InitStructure );

  36.         USART_Cmd(USART1, ENABLE);

  37.         NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  38.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  39.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  40.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  41.         NVIC_Init(&NVIC_InitStructure);
  42. }

  43. void USART2_Init(void)
  44. {
  45.         GPIO_InitTypeDef GPIO_InitStructure;
  46.         USART_InitTypeDef USART_InitStructure;
  47.         NVIC_InitTypeDef NVIC_InitStructure;

  48.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
  49.        
  50.         /* Configure USART Tx as alternate function push-pull */
  51.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  52.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  53.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  54.         GPIO_Init(GPIOD, &GPIO_InitStructure);
  55.        
  56.         /* Configure USART Rx as input floating */
  57.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  58.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  59.         GPIO_Init(GPIOD, &GPIO_InitStructure);
  60.        
  61.         GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
  62.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

  63.         USART_InitStructure.USART_BaudRate = 9600;
  64.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  65.         USART_InitStructure.USART_StopBits = USART_StopBits_1;
  66.         USART_InitStructure.USART_Parity = USART_Parity_No;
  67.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  68.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  69.         USART_Init(USART2, &USART_InitStructure );

  70.         USART_Cmd(USART2, ENABLE);

  71.         NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  72.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  73.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  74.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  75.         NVIC_Init(&NVIC_InitStructure);
  76. }


  77. int main(void)
  78. {
  79.         unsigned char c;
  80.         unsigned char err;
  81.        
  82.         USART1_Init();
  83.         USART2_Init();       
  84.         COMBufferInit();
  85.         USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  86.         USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);

  87.         UART_PutStrB(COM1, "Hello World!\n");
  88.         for(;;)
  89.         {
  90.                 c = COMGetCharB(COM1, &err);
  91.                 if(err == COM_NO_ERR)
  92.                 {
  93.                         COMPutCharB(COM1, c);
  94.                 }
  95.         }       
  96. }


稳稳の幸福 发表于 2016-3-28 21:17 | 显示全部楼层
NVIC 依照优先级处理所有支持的异常,所有异常在“处理器模式”处理。NVIC 结构支持32(IRQ[31:0]) 个离散中断,每个中断可以支持 4 级离散中断优先级。所有的中断和大多数系统异常可以配置为不同优先级。当中断发生时,NVIC 将比较新中断与当前中断的优先级,如果新中断优先级高,则立即处理新中断。当接受任何中断时,ISR的开始地址可从内存的向量表中取得。不需要确定哪个中断被响应,也不要软件分配相关中断服务程序(ISR)的开始地址。当获取中断入口地址时,NVIC 将自动保存处理状态到栈中,包括以下寄存器“PC, PSR, LR, R0~R3, R12” 的值。在ISR结束时,NVIC 将从栈中恢复相关寄存器的值,进行正常操作,因此花费少量且确定的时间处理中断请求。NVIC 支持末尾连锁 ”TailChaining”,有效处理背对背中断 ”back-to-back interrupts”,即无需保存和恢复当前状态从而减少在切换当前ISR时的延迟时间。NVIC 还支持迟到 “Late Arrival”,改善同时发生的ISR的效率。当较高优先级中断请求发生在当前ISR开始执行之前(保持处理器状态和获取起始地址阶段),NVIC 将立即处理更高优先级的中断,从而提高了实时性。
豆腐块 发表于 2016-3-28 21:36 | 显示全部楼层
一般来说我们会使用发送数据寄存器空中断,用这个中断发送的效率会高一些
 楼主| gaoyang9992006 发表于 2016-3-28 22:37 | 显示全部楼层
本文主要 讨论了其中的一种方法,大家都知道实现串口的方法,以及形式太多了。
yklstudent 发表于 2016-3-29 09:19 | 显示全部楼层
楼主的代码结构挺好的,学习下
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:如果你觉得我的分享或者答复还可以,请给我点赞,谢谢。

2052

主题

16403

帖子

222

粉丝
快速回复 在线客服 返回列表 返回顶部