• 基于STM32F10x的串口(USART)输入输出编程


    1 前言

        STM32有强大的固件库,绝大部分函数都可以有库里面的函数组合编写。固件库可以到ST官网(www.st.com)上下载,也可以搜索“STM32 固件库 v3.5”下载到固件库。本文章就是基于固件库来编写有关串口的输入输出函数。由于博主的知识水平有限,目前仅仅是将程序的思路和实现给出,具体到函数的执行效率、代码的简化方面未进行深入探讨。如果有兴趣的同学可以联系我,我们可以一起探讨一下。

    2 STM32固件库

        有关固件库的详细介绍,在emouse 思·睿 技术博客里有较为详细的解释(详见http://www.cnblogs.com/emouse/archive/2011/11/29/2268441.html),在本文中就不再讨论。这里仅仅将使用到的库函数列举出来,并不作深入的分析。

    2.1 stm32f10x_rcc.c

        该文件位于“...STM32F10x_StdPeriph_Lib_V3.5.0LibrariesSTM32F10x_StdPeriph_Driversrc”文件夹中。一般调用该文件的函数有:

    • void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState)
    • void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
    • void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)

    注意:不同的时钟所处的总线是不一样的,最常见的就是USART1和USART2分别位于APB2和APB1中,故写程序时必须分开写。具体函数的参数可以参考“stm32f10x_rcc.c”。

    举例:

    1   /* Enable GPIOA, GPIOD and USART1 clocks */
    2   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  | RCC_APB2Periph_GPIOD |
    3                          RCC_APB2Periph_USART1 , ENABLE);
    4 
    5   /* Enable USART2 clocks */
    6   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

     

    图1 互联型的系统结构

    2.2 stm32f10x_gpio.c

        该文件位于“...STM32F10x_StdPeriph_Lib_V3.5.0LibrariesSTM32F10x_StdPeriph_Driversrc”文件夹中。一般调用该文件的函数有:

    • void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

        在调用该函数前,通常需要先声明并配置好结构体GPIO_InitTypeDef。该结构体具体定义在stm32f10x_gpio.h(文件位于“...STM32F10x_StdPeriph_Lib_V3.5.0LibrariesSTM32F10x_StdPeriph_Driverinc”)文件中,需要配置的一般有GPIO_Pin(引脚)、GPIO_Speed(速率)和GPIO_Mode(模式)。

    举例:

    1   GPIO_InitTypeDef         GPIO_InitStructure;
    2   
    3   /* Configure PA.09 as alternate function push-pull */
    4   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                   
    5   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    6   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    7   GPIO_Init(GPIOA, &GPIO_InitStructure);

    2.3 misc.c

        该文件位于“...STM32F10x_StdPeriph_Lib_V3.5.0LibrariesSTM32F10x_StdPeriph_Driversrc”文件夹中。一般调用该文件的函数有:

    • void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
    • void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)

        对于中断函数来说,最关键的莫过于优先级排序。函数NVIC_PriorityGroupConfig提供了非常丰富的优先级排序,并通过结构体NVIC_InitTypeDef(位于misc.h文件中)的配置可以完成不同优先级的设置。同样,在使用函数NVIC_Init前,需要配置相应的NVIC_InitTypeDef结构体。

    举例:

     1   NVIC_InitTypeDef         NVIC_InitStructure;
     2   
     3   /* Configure two bit for preemptive priority */
     4   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
     5   
     6   /* Enable DMA Channel6 Interrupt */ 
     7   NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;
     8   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
     9   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    10   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    11   NVIC_Init(&NVIC_InitStructure);

    注意:语句“NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;”一般不能省略。除非在第一个中断配置中配置好后,后面的配置方可省略该条语句(不代表不配置,只是省略了而已)。

     2.4 stm32f10x_dma.c

        该文件位于“...STM32F10x_StdPeriph_Lib_V3.5.0LibrariesSTM32F10x_StdPeriph_Driversrc”文件夹中。一般调用该文件的函数有:

    • void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
    • void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState)
    • void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState)

        配置方式与2.2和2.3的方式相同:先定义一个结构体,并配置相应的值(赋值),然后装载。其中,ITConfig是中断配置,如不需要写中断函数,可以不配置。

    举例:

     1   DMA_InitTypeDef          DMA_InitStructure;
     2   
     3   /* DMA1 Channel6 (triggered by USART1 Rx event) Config */
     4   DMA_DeInit(DMA1_Channel6);  
     5   DMA_InitStructure.DMA_PeripheralBaseAddr =(u32)(&USART1->DR);
     6   DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer1;
     7   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
     8   DMA_InitStructure.DMA_BufferSize = RxBufferSize2;
     9   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    10   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    11   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    12   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;    
    13   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    14   DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    15   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    16   DMA_Init(DMA1_Channel6, &DMA_InitStructure);
    17   
    18   DMA_Cmd(DMA1_Channel6, ENABLE);

    说明:其中比较关键的是前4条配置(5-8行)。

    2.5 stm32f10x_tim.c

        该文件位于“...STM32F10x_StdPeriph_Lib_V3.5.0LibrariesSTM32F10x_StdPeriph_Driversrc”文件夹中。一般调用该文件的函数有:

    • void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
    • void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
    • void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)

        配置方法同2.4。该文件的函数很多,可以编写出非常丰富的定时与中断函数。在本文中只是利用其中最简单的一部分:定时功能。

    举例:

     1   TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
     2   
     3   /* Time base configuration */
     4   TIM_TimeBaseStructure.TIM_Period = (10000 - 1);
     5   TIM_TimeBaseStructure.TIM_Prescaler = (192 - 1);
     6   TIM_TimeBaseStructure.TIM_ClockDivision = 0;    
     7   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
     8   TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
     9 
    10   TIM_Cmd(TIM5, DISABLE);

    注意:请留心是否需要打开定时器,否则当定时器计数并触发中断时,程序很可能一直卡在中断程序里,导致程序无法正常走下去。

    2.6 stm32f10x_usart.c

        该文件位于“...STM32F10x_StdPeriph_Lib_V3.5.0LibrariesSTM32F10x_StdPeriph_Driversrc”文件夹中。一般调用该文件的函数有:

    • void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
    • void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
    • void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
    • void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState)

        配置方法同2.4-2.5。其中USART_DMACmd是配置DMA与USART通道,如不适用DMA,可以不配置。

    举例:

      USART_InitTypeDef        USART_InitStructure;
      
    /*****************************************************************************
    *USART1 configured as follow:                                                *
    *     - BaudRate =  9600 baud                                                *
    *     - Word Length = 8 Bits                                                 *
    *     - One Stop Bit                                                         *
    *     - No parity                                                            *
    *     - Hardware flow control disabled (RTS and CTS signals)                 *
    *     - Receive and transmit enabled                                         *
    *****************************************************************************/
      USART_InitStructure.USART_BaudRate = 9600;
      USART_InitStructure.USART_WordLength = USART_WordLength_8b;
      USART_InitStructure.USART_StopBits = USART_StopBits_1;
      USART_InitStructure.USART_Parity = USART_Parity_No ;
      USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
      USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
      USART_Init(USART1, &USART_InitStructure);
    
      //STM_EVAL_COMInit(COM1, &USART_InitStructure);
      USART_Cmd(USART1, ENABLE);

    注意:在博主的开发板上,语句“STM_EVAL_COMInit(COM1, &USART_InitStructure);”是必须的,但在有的开发板上可以不需要。调用该函数时需要调用头文件stm32_eval.h。

    3 串口输出(发送)程序

    3.1 关键库函数

    • void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
    • FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)

        第一条语句是发送函数,它告诉我们很重要的一点,那就是串口是以”位“来传输的。如果能够理解这个概念,那么程序思路就很简单了。第二句语句作用如同其名”Get Flag Status“,可以用它来得知串口的状态。那么,结合这两个函数就可以写出自己风格和要求的串口发送函数。

    3.2 利用printf改写成串口发送函数

        在C编程中,最常用的便是printf,用于观察各种结果。可以利用库函数来编写类似的函数。

     1 /* GUC编译环境 */
     2 #ifdef __GNUC__
     3      With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
     4      set to 'Yes') calls __io_putchar() 
     5   #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
     6 #else
     7   #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
     8 #endif    __GNUC__
     9 
    10 /*************************************************
    11 * Function Name  : PUTCHAR_PROTOTYPE
    12 * Description    : Retargets the C library printf function to the USART
    13 * Input          : NONE
    14 * Output         : NONE
    15 * Return         : NONE
    16 *************************************************/
    17 PUTCHAR_PROTOTYPE
    18 {
    19   /* Place your implementation of fputc here 
    20      e.g. write a character to the USART */
    21   USART_SendData(USART1, (uint8_t) ch);
    22 
    23   /* Loop until the end of transmission */
    24   while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
    25   return ch;
    26 }

        函数的调用形式与c里面的printf一样。

        优点:方便。

        缺点:只能利用一个串口(上面程序中利用的是USART1)。当遇到多串口的时候需要对其他串口编写其他程序,在形式上就会不一致,导致程序维护起来不方便。

    3.3 参考printf函数编写串口发送函数

        可以参考库函数中printf函数的编写方法来自己编写一个串口的printf函数。

     1 #include <stdio.h>
     2 #include <stdarg.h>
     3 
     4 /*************************************************
     5 * Function Name  : USART1_printf
     6 * Description    : 
     7 * Input          : 
     8 * Output         : NONE
     9 * Return         : NONE
    10 *************************************************/
    11 void USART1_printf (char *fmt, ...) 
    12 { 
    13   char buffer[CMD_BUFFER_LEN+1];  
    14   u8 i = 0; 
    15   
    16   va_list arg_ptr; 
    17   va_start(arg_ptr, fmt);   
    18   vsnprintf(buffer, CMD_BUFFER_LEN+1, fmt, arg_ptr); 
    19   while ((i < CMD_BUFFER_LEN) && buffer[i]) 
    20   { 
    21     USART_SendData(USART1, (u8) buffer[i++]); 
    22     while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);  
    23   } 
    24   va_end(arg_ptr); 
    25 }

        函数的调用形式与c里面的printf一样。

        优点:格式与printf一致,使用方便。

        缺点:编写比较复杂。

    3.4 自定义编写发送函数

        根据固件库函数,可以编写出满足自己使用条件的各种发送函数,这样可以根据不同串口的特点编写出对应的函数,具有很强的针对性。

     1 /*************************************************
     2 * Function Name  : USART1_SendData
     3 * Description    : 串口1发送
     4 * Input          : char *Buffer
     5 * Output         : NONE
     6 * Return         : NONE
     7 *************************************************/
     8 void USART1_SendData(char *Buffer)
     9 {
    10   u8 Counter = 0;
    11   while( (Counter == 0) || (Buffer[Counter] != 0) ) //条件...
    12   {
    13     USART_SendData(USART1, Buffer[Counter++]);
    14     while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);          
    15   }
    16 }

        优点:自定制,编写简单。

        缺点:printf字符串的时候语句不够精炼。

    3.5 利用DMA发送串口数据

        上述3.2-3.4的函数都需要具体在程序中调用,对CPU占用的比较厉害,而DMA的优点是不会占用CPU。

     示例:

      1 #include "stm32f10x.h"
      2 #include "stm32_eval.h"
      3 
      4 #define TxBufferSize1      10
      5 
      6 u8 TxBuffer1[TxBufferSize1];
      7 
      8 int main(void)
      9 {
     10   Configuration();
     11   
     12   while(1);
     13 }
     14 
     15 void Configuration(void)
     16 {
     17   GPIO_InitTypeDef         GPIO_InitStructure;
     18   NVIC_InitTypeDef         NVIC_InitStructure;
     19   DMA_InitTypeDef          DMA_InitStructure;
     20   USART_InitTypeDef        USART_InitStructure;
     21   
     22   
     23   /* Enable GPIOA and USART1 clocks */
     24   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  | RCC_APB2Periph_USART1, ENABLE);
     25   
     26   /* DMA clock enable */
     27   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
     28   
     29   
     30   /* Configure USART1 Rx (PA.10) as input floating */
     31   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
     32   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
     33   GPIO_Init(GPIOA, &GPIO_InitStructure);
     34   
     35   /* Configure USART1 Tx (PA.09) as alternate function push-pull */
     36   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
     37   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     38   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
     39   GPIO_Init(GPIOA, &GPIO_InitStructure);
     40   
     41   
     42   /* Configure one bit for preemptive priority */
     43   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 
     44   
     45   /* Enable DMA Channel6 Interrupt */ 
     46   NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;
     47   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
     48   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
     49   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     50   NVIC_Init(&NVIC_InitStructure);
     51   
     52   
     53   /* DMA1 Channel6 (triggered by USART1 Tx event) Config */ 
     54   DMA_InitStructure.DMA_PeripheralBaseAddr =(u32)(&USART1->DR);
     55   DMA_InitStructure.DMA_MemoryBaseAddr = (u32)TxBuffer1;
     56   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
     57   DMA_InitStructure.DMA_BufferSize = TxBufferSize1;
     58   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
     59   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
     60   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
     61   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;    
     62   DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                    
     63   DMA_InitStructure.DMA_Priority = DMA_Priority_High;
     64   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
     65   DMA_Init(DMA1_Channel6, &DMA_InitStructure);
     66   
     67   DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);
     68   DMA_Cmd(DMA1_Channel6, ENABLE);
     69   
     70   
     71 /*****************************************************************************
     72 *USART1 configured as follow:                                                *
     73 *     - BaudRate =  9600 baud                                               *
     74 *     - Word Length = 8 Bits                                                 *
     75 *     - One Stop Bit                                                         *
     76 *     - No parity                                                            *
     77 *     - Hardware flow control disabled (RTS and CTS signals)                 *
     78 *     - Receive and transmit enabled                                         *
     79 *****************************************************************************/
     80   USART_InitStructure.USART_BaudRate = 9600;
     81   USART_InitStructure.USART_WordLength = USART_WordLength_8b;
     82   USART_InitStructure.USART_StopBits = USART_StopBits_1;
     83   USART_InitStructure.USART_Parity = USART_Parity_No ;
     84   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
     85   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
     86   USART_Init(USART1, &USART_InitStructure);
     87   
     88   //STM_EVAL_COMInit(COM1, &USART_InitStructure);
     89   USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
     90   USART_Cmd(USART1, ENABLE);
     91 
     92 }
     93 
     94 /*************************************************
     95 * Function Name  : DMA1_Channel6_IRQHandler
     96 * Description    : DMA1_Channel6中断服务函数
     97 * Input          : NONE
     98 * Output         : NONE
     99 * Return         : NONE
    100 *************************************************/
    101 void DMA1_Channel6_IRQHandler(void)
    102 {
    103   DMA_ClearITPendingBit(DMA1_IT_TC6);
    104   DMA_Cmd(DMA1_Channel6, DISABLE);
    105   
    106   /* 中断程序 */
    107   
    108   DMA_Cmd(DMA1_Channel6, ENABLE);
    109 }
    example10

    注意:以上事例仅仅作为利用DMA进行串口发送的一个简要步骤。如果工程比较复杂时建议分类进行配置,否则更改起来十分费劲。其中,中断函数DMA1_Channel6_IRQHandler可以放在main.c文件中,也可以放在stm32f10x_it.c文件中,但注意拼写一定要对(中断函数的具体名称可以查看startup_stm32f10x_XX.s,“XX”表示类型)。

        优点:不占用DMA,中断函数使用方便。

        缺点:若考虑传输出错时的错误处理时,编程比较繁琐。

    3.6 利用USART中断进行发送数据

        不使用DMA进行发送,而是利用USART的中断事件进行发送,也能实现各种自定义发送。

        程序待补

    4 串口输入(接收)程序

    4.1 关键库函数

    • uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
    • FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)

        同理,第一条是逐个接收数据;第二条是获取状态。

    4.2 自定义接收函数

        利用上述函数可以自己定制接收函数。

    示例:以回车符( )作为结束标志的接收函数

     1 /*************************************************
     2 * Function Name  : USART1_ReceiveData
     3 * Description    : 串口1接收
     4 * Input          : char *Buffer, u8 BufferSize
     5 * Output         : NONE
     6 * Return         : NONE
     7 *************************************************/
     8 void USART1_ReceiveData(char *Buffer, u8 BufferSize)
     9 {
    10   u8 Counter;
    11   for(Counter = 0 ; Counter < BufferSize ; Counter++)
    12     Buffer[Counter] = 0;
    13   Counter = 0;
    14   do
    15   { 
    16     if(  (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET)
    17        &&(Counter < BufferSize) )
    18        Buffer[Counter++] = USART_ReceiveData(USART1);
    19     if( (Counter == 2) && (Buffer[Counter - 2] == '
    ') && 
    20                           (Buffer[Counter - 1] == '
    ') )
    21     {
    22       Buffer[0] = Buffer[Counter];
    23       Counter = 0;
    24       continue;
    25     }
    26   }while((Buffer[Counter - 2] != '
    ') || (Buffer[Counter - 1] != '
    ') );
    27   for( ; Counter < BufferSize ; Counter++)
    28     Buffer[Counter] = 0;
    29 }

    说明:该函数接收的最大字长为BufferSize,遇到回车符接收完毕,且不接收空字符。关于回车符可见文章http://www.crifan.com/detailed_carriage_return_0x0d_0x0a_cr_lf__r__n_the_context/

        优点:编写简单,可定制。

        缺点:接收时需要调用函数,即不能作为“监听”串口是否有数据输入。

    4.3 利用USART中断函数接收数据

        通过中断函数来接收函数,可以做到不影响主程序的流向,从而实现对串口的接收。同样可以对中断程序进行编写达到自定义接收的效果。

    示例:以回车符( )作为结束标志的接收函数

     1 /*************************************************
     2 * Function Name  : USART2_IRQHeader
     3 * Description    : USART2中断服务函数
     4 * Input          : NONE
     5 * Output         : NONE
     6 * Return         : NONE
     7 *************************************************/
     8 void USART2_IRQHandler(void)
     9 {
    10   u8 i;
    11   if( USART2_Counter < RxBufferSize2 )
    12     RxBuffer2[USART2_Counter++] = USART_ReceiveData(USART2);
    13   if( (USART2_Counter == RxBufferSize2) || 
    14       ((USART2_Counter > 1) && (RxBuffer2[USART2_Counter-2] == '
    ' ) &&
    15                                (RxBuffer2[USART2_Counter-1] == '
    ' )   ) )
    16   {
    17     USART2_Counter = 0;
    18     
    19     /* 中断程序 */
    20     
    21   }
    22   USART_ClearITPendingBit(USART2, USART_IT_RXNE);
    23 }

        优点:可定制,占用CPU少,不影响主程序的走向。

        缺点:需要配置中断函数。

    4.4 利用DMA接收定长数据

        详见3.5。可以利用DMA接收定长的数据,对于定长数据传输时能做到不占用CPU,高效率的传输。

        优点:速度快,适合与定长传输、海量数据传输。

        缺点:定长

    4.5 利用DMA接收不定长数据

        其思路有两种:一种是外部时钟加内部编程,即Rx端外加一个时钟引脚,利用时钟来判定是否停止接收。其优点是“软硬结合,编程简单”,缺点是对于不熟悉时钟操作的人来说操作起来有一定难度。

        另一种方法是利用STM32内部的定时器编程的方法触发另一个中断程序。通过波特率计算出传输一位(8bit)的时间,定时器设置约为传输一位(8bit)的时间,通过判断DMA内数组长度(指针位置)是否改变从而触发事件。

    示例:

     1 u8   TIM4_Counter   = 0;
     2 bool TIM4_i         = 0;
     3 bool TIM4_j         = 0;
     4 
     5 
     6 /*************************************************
     7 * Function Name  : TIM4_IRQHandler
     8 * Description    : TIM4中断服务函数
     9 * Input          : NONE
    10 * Output         : NONE
    11 * Return         : NONE
    12 *************************************************/
    13 void TIM4_IRQHandler(void)
    14 {
    15   TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    16   TIM_Cmd(TIM4, DISABLE);
    17 
    18   if(!TIM4_i)
    19   {
    20     TIM4_Counter = RxBufferSize2 - DMA_GetCurrDataCounter(DMA1_Channel6);
    21     TIM4_i = 1;
    22   }
    23   else if(TIM4_Counter != (RxBufferSize2 - DMA_GetCurrDataCounter(DMA1_Channel6)) )
    24     TIM4_i = 0;
    25   else if( (!TIM4_j) && (TIM4_Counter != 0) && (TIM4_Counter != RxBufferSize2) )
    26   {
    27     TIM4_i = 0;
    28     DMA_Cmd(DMA1_Channel6, DISABLE);
    29     DMA1_Channel6->CNDTR = RxBufferSize2;   //重装初值
    30     
    31     /* 中断程序 */
    32     
    33     DMA_Cmd(DMA1_Channel6, ENABLE);
    34   }
    35   else if(TIM4_j)
    36   {
    37     TIM4_i = 0;
    38     TIM4_j = 0;
    39   }
    40   
    41   TIM_Cmd(TIM4, ENABLE);
    42 }
    43 /*************************************************
    44 * Function Name  : DMA1_Channel6_IRQHandler
    45 * Description    : DMA1_Channel6中断服务函数
    46 * Input          : NONE
    47 * Output         : NONE
    48 * Return         : NONE
    49 *************************************************/
    50 void DMA1_Channel6_IRQHandler(void)
    51 {
    52   DMA_ClearITPendingBit(DMA1_IT_TC6);
    53   DMA_Cmd(DMA1_Channel6, DISABLE);
    54   DMA1_Channel6->CNDTR = RxBufferSize2;   //重装初值
    55   TIM4_j = 1;
    56   
    57   /* 中断程序 */
    58   
    59   DMA_Cmd(DMA1_Channel6, ENABLE);
    60 }

        优点:不定长DMA接收数据

        缺点:程序复杂。

    5 结语

        以上便是基于STM32F10x的串口输入输出编程,其程序都经过测试可行。但由于编写文章时难免有些疏漏,程序如有问题,可在下方留言,我会尽快修改。如果有地方说的不正确的也请指明,我也会积极修改并给出回复。如果有其他意见和建议,也可以在后面留言,我会尽快答复。

        最后声明一点,其中代码所蕴含的思想,或者说程序本身,并非博主所有,博主所做的仅仅是一种经验上的总结,如果有那些部分是私用了您的版权,请私下联系我并出示证明,我会尽快处理。如果此文章对您有所启发,也欢迎转载,转载时请注明文章出处,谢谢!

    6 附件

        图1出处为《STM32F系列ARM内核32位高性能微控制器参考手册V14.pdf》. 48页。

  • 相关阅读:
    poj 3253 Fence Repair (优先队列,哈弗曼)
    容斥原理 (转载)
    poj 1088 滑雪 DP(dfs的记忆化搜索)
    饭卡 01背包 + 贪心
    N分之一 竖式除法模拟
    poj2325 大数除法+贪心
    优先队列重载运算符< 以及初始化列表
    POJ 2718 Smallest Difference(贪心 or next_permutation暴力枚举)
    HASH算法
    字符串匹配算法——KMP算法
  • 原文地址:https://www.cnblogs.com/mocet/p/stm32f10x_usart_InputOutout_Function_Design_1.html
Copyright © 2020-2023  润新知