淺析STM32 中常用的三個函數(sys.c,delay.c,usart.c)之三


 
#include "sys.h"
#include "usart.h"
//////////////////////////////////////////////////////////////////
//加入以下代碼,支持printf函數,而不需要選擇use MicroLIB      
#if 1
#pragma import(__use_no_semihosting)             
//標准庫需要的支持函數                 
struct __FILE 
{ 
    int handle; 

}; 

FILE __stdout;       
//定義_sys_exit()以避免使用半主機模式    
void _sys_exit(int x) 
{ 
    x = x; 
} 
//重定義fputc函數 
int fputc(int ch, FILE *f)
{      
    while((USART1->SR&0X40)==0);//循環發送,直到發送完畢   
    USART1->DR = (u8) ch;      
    return ch;
}
#endif 


//串口1中斷服務程序
//注意,讀取USARTx->SR能避免莫名其妙的錯誤       
u8 USART_RX_BUF[USART_REC_LEN];     //接收緩沖,最大USART_REC_LEN個字節.
//接收狀態
//bit15,    接收完成標志
//bit14,    接收到0x0d
//bit13~0,    接收到的有效字節數目
u16 USART_RX_STA=0;       //接收狀態標記      
  
void uart_init(u32 bound){
  //GPIO端口設置
  GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
     
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);    //使能USART1,GPIOA時鍾
  
    //USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //復用推挽輸出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
  //USART1_RX      GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優先級3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        //子優先級3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);    //根據指定的參數初始化VIC寄存器
  
   //USART 初始化設置

    USART_InitStructure.USART_BaudRate = bound;//串口波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式
    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); //初始化串口1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串口接受中斷
  USART_Cmd(USART1, ENABLE);                    //使能串口1 

}

void USART1_IRQHandler(void)                    //串口1中斷服務程序
    {
    u8 Res;

    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中斷(接收到的數據必須是0x0d 0x0a結尾)
        {
        Res =USART_ReceiveData(USART1);    //讀取接收到的數據
        
        if((USART_RX_STA&0x8000)==0)//接收未完成
            {
            if(USART_RX_STA&0x4000)//接收到了0x0d
                {
                if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
                else USART_RX_STA|=0x8000;    //接收完成了 
                }
            else //還沒收到0X0D
                {    
                if(Res==0x0d)USART_RX_STA|=0x4000;
                else
                    {
                    USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
                    USART_RX_STA++;
                    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,重新開始接收      
                    }         
                }
            }            
     } 

} 

 

usart文件夾內包含了 usart.c 和 usart.h 兩個文件。這兩個文件用於串口的初始化和中斷接收。這里只是針對串口 1 ,

比如你要用串 口 2 或者其他的串口,只要對代碼稍作修改就可以了。usart.c 里面包含了 2 個函數一個是 void USART1_IRQHandler(void)

另外一個是 void uart_init(u32 bound) 里面還有一段對串口 printf 的支持代碼,如果去掉,則會導致 printf 無法使用,雖然軟件編譯不會報錯,

但是硬件上 STM32 是無法啟動的,這段代碼不要去修改。

 下面將貼上main函數,對這個usart串口中斷法如何實現數據的接收和發送的,過程基本如下所述:

main 函數就是開發板將從電腦虛擬串口助手發送過來的數據接收之后,開發板發送這些接收的數據給電腦的電腦虛擬串口助手。

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"

 int main(void)
 {        
     u16 t;  
    u16 len;    
    u16 times=0;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2位搶占優先級,2位響應優先級
    uart_init(9600);     //串口初始化為115200
     LED_Init();                 //LED端口初始化
    delay_init();             //延時函數初始化      
    KEY_Init();          //初始化與按鍵連接的硬件接口
     while(1)
    {
        if(USART_RX_STA&0x8000)
        {                       
            len=USART_RX_STA&0x3fff;//得到此次接收到的數據長度
            printf("\r\n您發送的消息為:\r\n\r\n");
            for(t=0;t<len;t++)
            {
                USART_SendData(USART2, USART_RX_BUF[t]);//向串口1發送數據
                while(USART_GetFlagStatus(USART2,USART_FLAG_TC)!=SET);//等待發送結束
            }
            printf("\r\n\r\n");//插入換行
            USART_RX_STA=0;
        }
        else
        {

            times++;
            if(times%5000==0)
            {
                printf("\r\n精英STM32開發板 串口實驗\r\n");
                printf("正點原子@ALIENTEK\r\n\r\n");
            }
            if(times%200==0)printf("請輸入數據,以回車鍵結束\n");  
            if(times%30==0)LED0=!LED0;//閃爍LED,提示系統正在運行.
            delay_ms(10);   
        }
    }     
 }

 

下面將是關鍵的printf支持的原理以及串口中斷的處理原理的詳解!!!

 

5.3.1 printf函數支持

這段引入 printf 函數支持的代碼在 usart.h 頭文件的最上方,這段代碼加入之后便可以通過
printf 函數向 串口發送我們需要的內容,方便開發過程中查看代碼執行情況以及一些變量值。這
段代碼不需要修改,引入到 usart.h 即可。

//加入以下代碼,支持printf函數,而不需要選擇use MicroLIB      
#if 1
#pragma import(__use_no_semihosting)             
//標准庫需要的支持函數                 
struct __FILE 
{ 
    int handle; 

}; 

FILE __stdout;       
//定義_sys_exit()以避免使用半主機模式    
void _sys_exit(int x) 
{ 
    x = x; 
} 
//重定義fputc函數 
int fputc(int ch, FILE *f)
{      
    while((USART1->SR&0X40)==0);//循環發送,直到發送完畢   
    USART1->DR = (u8) ch;      
    return ch;
}
#endif 

初始化完相關IO口、NVIC中斷、串口等之外,還要開啟串口接收中斷以及串口使能

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串口接受中斷
  USART_Cmd(USART1, ENABLE);                    //使能串口1 

不然,初始化完成之后,串口依然不能正常功能,例如串口通訊,或者通過printf發送數據到串口調試助手。

這里我們設計了一個小小的接收協議:通過這個函數,配合一個數組USART_RX_BUF一個接收狀態寄存器 USART_RX_STA

(此寄存器其實就是一個全局變量,由作者自行添加。由於它起到類似寄存器的功能,這里暫且稱之為寄存器)

實現對串口數據的接收管理。USART_RX_BUF 的大小由 USART_REC_LEN 定義,也就是 一次接收的數據最大不能超過

USART_REC_LEN 個字節。 SART_RX_STA 是一個接收狀態寄存器其各的定義如下圖所示。

 

原理:

當接收到從電腦發過來的數據,把接收到的數據保存在USART_RX_BUF 中,同時在接收狀態寄存器( USART_RX_STA )

中計數接收到的有效數據 個數,當收到回車(回車的表示由 2個字節組成: 0X0D 和 0X0A )的第一個字節 0X0D 時,

計數器將不再增加,等待 0X0A 的到來,而如果 0X0A 沒有來到,則認為這次接收失敗,重新開始下一次接收。

如果順利接收到 0X0A則標記 USART_RX_STA 的第 15 位,這樣完成一次接收,並等待該位被其他程序清除,

從而開始下一次的接收,而如果遲遲沒有收到 0X0D ,那么在接收數據超過 USART_REC_LEN 的時候,

則會丟棄前面的數據,重新接收。中斷相應函數代碼如下:

void USART1_IRQHandler(void)                    //串口1中斷服務程序
    {
    u8 Res;

    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中斷(接收到的數據必須是0x0d 0x0a結尾)
        {
        Res =USART_ReceiveData(USART1);    //讀取接收到的數據
        
        if((USART_RX_STA&0x8000)==0)//接收未完成
            {
            if(USART_RX_STA&0x4000)//接收到了0x0d
                {
                if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
                else USART_RX_STA|=0x8000;    //接收完成了 
                }
            else //還沒收到0X0D
                {    
                if(Res==0x0d)USART_RX_STA|=0x4000;
                else
                    {
                    USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
                    USART_RX_STA++;
                    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,重新開始接收      
                    }         
                }
            }            
     } 

} 

 請各位大俠,覺得筆者寫的不錯的,給個贊!


免責聲明!

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



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