基於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.0\Libraries\STM32F10x_StdPeriph_Driver\src”文件夾中。一般調用該文件的函數有:

  • 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.0\Libraries\STM32F10x_StdPeriph_Driver\src”文件夾中。一般調用該文件的函數有:

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

    在調用該函數前,通常需要先聲明並配置好結構體GPIO_InitTypeDef。該結構體具體定義在stm32f10x_gpio.h(文件位於“...\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\inc”)文件中,需要配置的一般有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.0\Libraries\STM32F10x_StdPeriph_Driver\src”文件夾中。一般調用該文件的函數有:

  • 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.0\Libraries\STM32F10x_StdPeriph_Driver\src”文件夾中。一般調用該文件的函數有:

  • 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.0\Libraries\STM32F10x_StdPeriph_Driver\src”文件夾中。一般調用該文件的函數有:

  • 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.0\Libraries\STM32F10x_StdPeriph_Driver\src”文件夾中。一般調用該文件的函數有:

  • 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 自定義接收函數

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

示例:以回車符(\r\n)作為結束標志的接收函數

 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] == '\r') && 
20                           (Buffer[Counter - 1] == '\n') )
21     {
22       Buffer[0] = Buffer[Counter];
23       Counter = 0;
24       continue;
25     }
26   }while((Buffer[Counter - 2] != '\r') || (Buffer[Counter - 1] != '\n') );
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中斷函數接收數據

    通過中斷函數來接收函數,可以做到不影響主程序的流向,從而實現對串口的接收。同樣可以對中斷程序進行編寫達到自定義接收的效果。

示例:以回車符(\r\n)作為結束標志的接收函數

 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] == '\r' ) &&
15                                (RxBuffer2[USART2_Counter-1] == '\n' )   ) )
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頁。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM