1. 程式人生 > >利用定時中斷接收不定長度的串列埠資料

利用定時中斷接收不定長度的串列埠資料

在使用串列埠時,通常會遇到一些功能,如在TFT螢幕上顯示串列埠收到的字串,這些字串直接是對方printf過來的,沒有任何協議,此時為了保證顯示內容是一整個句子(通常句子傳送會有間隔),這是我們可以用定時器進行判斷是否接收完成。

以stm32f4為例,程式碼基於正點原子的例程,為了閱讀方便,刪除了部分註釋。
我們需要用到定時器和串列埠兩部分:

timer.h進行定時器初始化函式的宣告。

#ifndef _TIMER_H
#define _TIMER_H
#include "sys.h"

void TIM7_Int_Init(u16 arr,u16 psc);

#endif

timer.c進行定時器的定義及中斷函式說明,其中中斷函式內容的意思大致就是,當定時器溢位(計時到了),強制標記接收完成,然後清除中斷標誌位,禁用定時器,等待使用者處理串列埠接收buffer。

#include "timer.h"
#include "led.h"


extern vu16 USART3_RX_STA;

//定時器7中斷服務程式		    
void TIM7_IRQHandler(void)
{ 	
	if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中斷
	{	 			   
		USART3_RX_STA|
=1<<15; //標記接收完成 TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中斷標誌 TIM_Cmd(TIM7, DISABLE); //關閉TIM7 } } //通用定時器中斷初始化 //這裡始終選擇為APB1的2倍,而APB1為36M //arr:自動重灌值。 //psc:時鐘預分頻數 void TIM7_Int_Init(u16 arr,u16 psc) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7時鐘使能 //定時器TIM7初始化 TIM_TimeBaseStructure.TIM_Period = arr; //設定在下一個更新事件裝入活動的自動重灌載暫存器週期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //設定用來作為TIMx時鐘頻率除數的預分頻值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設定時鐘分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式 TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根據指定的引數初始化TIMx的時間基數單位 TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中斷,允許更新中斷 TIM_Cmd(TIM7,ENABLE);//使能定時器7 NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//搶佔優先順序0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子優先順序1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根據指定的引數初始化VIC暫存器 }

usart3.h中是對buffer長度的定義,及相關函式的宣告。

#ifndef __USART3_H
#define __USART3_H	 
#include "sys.h"  
#include "timer.h"

#define USART3_MAX_RECV_LEN		400					//最大接收快取位元組數
#define USART3_MAX_SEND_LEN		400					//最大發送快取位元組數

extern u8  USART3_RX_BUF[USART3_MAX_RECV_LEN]; 		//接收緩衝,最大USART3_MAX_RECV_LEN位元組
extern u8  USART3_TX_BUF[USART3_MAX_SEND_LEN]; 		//傳送緩衝,最大USART3_MAX_SEND_LEN位元組
extern u16 USART3_RX_STA;   						//接收資料狀態

void usart3_init(u32 bound);				//串列埠3初始化 
void u3_printf(char* fmt, ...);
void u3_putchar(u8 data);
#endif

usart3.c中是中斷服務函式和配置函式。
其中__align關鍵字用於記憶體對齊,具體可以參考原子的解釋:
FATFS一個小BUG搞了我2天才解決.特此發帖,希望大家不要重蹈我的覆轍.

#include "delay.h"
#include "usart3.h"
#include "stdarg.h"
#include "stdio.h"
#include "string.h"
#include "timer.h"

//串列埠傳送快取區
__align(8) u8 USART3_TX_BUF[USART3_MAX_SEND_LEN]; 	//傳送緩衝,最大USART3_MAX_SEND_LEN位元組
//串列埠接收快取區
__align(8) u8 USART3_RX_BUF[USART3_MAX_RECV_LEN]; 	//接收緩衝,最大USART3_MAX_RECV_LEN個位元組.


//通過判斷接收連續2個字元之間的時間差不大於100ms來決定是不是一次連續的資料.
//如果2個字元接收間隔超過100ms,則認為不是1次連續資料.也就是超過100ms沒有接收到
//任何資料,則表示此次接收完畢.
//接收到的資料狀態
//[15]:0,沒有接收到資料;1,接收到了一批資料.
//[14:0]:接收到的資料長度
u16 USART3_RX_STA=0;
void USART3_IRQHandler(void)
{
    u8 res;
    if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)//接收到資料
    {

        res =USART_ReceiveData(USART3);
        if((USART3_RX_STA&(1<<15))==0)//接收完的一批資料,還沒有被處理,則不再接收其他資料
        {
            if(USART3_RX_STA<USART3_MAX_RECV_LEN)		//還可以接收資料
            {
                TIM_SetCounter(TIM7,0);//計數器清空
                if(USART3_RX_STA==0)
                    TIM_Cmd(TIM7, ENABLE);  //使能定時器7
                USART3_RX_BUF[USART3_RX_STA++]=res;		//記錄接收到的值
            } else
            {
                USART3_RX_STA|=1<<15;					//強制標記接收完成
            }
        }
    }
}

//初始化IO 串列埠3
//bound:波特率
void usart3_init(u32 bound)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    USART_DeInit(USART3);  //復位串列埠3

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB時鐘
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//使能USART3時鐘


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10; //GPIOB11和GPIOB10初始化
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//複用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度50MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推輓複用輸出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
    GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB11,和GPIOB10


    GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3); //GPIOB11複用為USART3
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3); //GPIOB10複用為USART3

    USART_InitStructure.USART_BaudRate = bound;//波特率一般設定為9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位資料格式
    USART_InitStructure.USART_StopBits = USART_StopBits_2;//一個停止位
    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(USART3, &USART_InitStructure); //初始化串列埠3

    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//開啟中斷

    USART_Cmd(USART3, ENABLE);                    //使能串列埠


    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//搶佔優先順序2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子優先順序3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);	//根據指定的引數初始化VIC暫存器

    TIM7_Int_Init(100-1,8400-1);	//10ms中斷一次

    TIM_Cmd(TIM7, DISABLE); //關閉定時器7

    USART3_RX_STA=0;				//清零
}

//串列埠3,printf 函式
//確保一次傳送資料不超過USART3_MAX_SEND_LEN位元組
void u3_printf(char* fmt,...)
{
    u16 i,j;
    va_list ap;
    va_start(ap,fmt);
    vsprintf((char*)USART3_TX_BUF,fmt,ap);
    va_end(ap);
    i=strlen((const char*)USART3_TX_BUF);//此次傳送資料的長度
    for(j=0; j<i; j++) //迴圈傳送資料
    {
        while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET);  //等待上次傳輸完成
        USART_SendData(USART3,(uint8_t)USART3_TX_BUF[j]); 	 //傳送資料到串列埠3
    }

}

void u3_putchar(u8 data)
{
    while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET);  //等待上次傳輸完成
    USART_SendData(USART3,data); 	 //傳送資料到串列埠3
}

在中斷服務函式中有這麼一個過程,在正常的串列埠資料儲存過程中,多了個計數器清零和啟動定時器,因為進中斷的時候是當前收到的最後一個字元,那具體有沒有接收完呢?這個時候我們開始計時了(10ms),如果10ms內有資料接收(一般以9600的波特率是1ms發一個數據),那麼這裡會把計數值清空,繼續接收,如果沒有資料來了,定時器溢位會進定時器中斷,強制標記結束標誌。

		if(USART3_RX_STA<USART3_MAX_RECV_LEN)		//還可以接收資料
            {
                TIM_SetCounter(TIM7,0);//計數器清空
                if(USART3_RX_STA==0)
                    TIM_Cmd(TIM7, ENABLE);  //使能定時器7
                USART3_RX_BUF[USART3_RX_STA++]=res;		//記錄接收到的值
            }

這種方法就不需要以\r\n作為字串的結束符了。