2021/10/15 智能家具 嵌入式實訓 第五天 串口通信 (1)


   

通信的兩種方式:

並行通信

  -傳輸原理:數據各個位同時傳輸。
  -優點:速度快
   -缺點:占用引腳資源多

串行通信

  -傳輸原理:數據按位順序傳輸。
  -優點:占用引腳資源少
  -缺點:速度相對較慢

串行通信分類(按照數據傳送方向)

  單工(a):
     數據傳輸只支持數據在一個方向上傳輸

  半雙工(b):
     允許數據在兩個方向上傳輸,但是,在某一時刻,只允許數據在一個方向上傳輸,它實際上是一種切換方向的單工通信;

  全雙工(c):
     允許數據同時在兩個方向上傳輸,因此,全雙工通信是兩個單工通信方式的結合,它要求發送設備和接收設備都有獨立的接收和發送能力。

 

 

串行通信的通信方式

同步通信(帶時鍾同步信號傳輸):

     SPI,IIC通信接口

異步通信(不帶時鍾同步信號傳輸):

    UART(通用異步收發器),單總線 約定傳輸速度

 

 

STM32的串口通信接口

  UART:通用異步收發器。
  USART:通用同步異步收發器。
    大容量STM32F10x系列芯片,包含3個USART和2個UART

UART異步通信方式引腳連接方法:

  -RXD:數據輸入引腳。數據接受。
  -TXD:數據發送引腳。數據發送。

 

 

 

 

 

 

UART異步通信方式特點:

  全雙工異步通信。

  分數波特率發生器系統,提供精確的波特率。
 -發送和接受共用的可編程波特率,最高可達4.5Mbits/s
  可編程的數據字長度(8位或者9位);
  可配置的停止位(支持1或者2位停止位);
  可配置的使用DMA多緩沖器通信。
  單獨的發送器和接收器使能位。
  檢測標志:① 接受緩沖器 ②發送緩沖器空 ③傳輸結束標志
  多個帶標志的中斷源。觸發中斷。
  其他:校驗控制,四個錯誤檢測標志。

 串口通信過程

 

 

 

 

 

 

 

 發送數據:通過總線往USARTx控制器中的DR寄存器(TDR),寫入數據,USARTx控制器會自動通過Tx管腳發送出去。

 接收數據:USARTx控制通過Rx管腳接收到位數據,組合成8位數據,存在DR寄存器(RDR),直接讀取DR寄存器。

 

STM32串口異步通信需要定義的參數

 

1 起始位
2 數據位(8位或者9位)
3 奇偶校驗位(第9位)
4 停止位(1,15,2位)
5 波特率設置

 

協議規定了什么?硬件層、電平標准、通信數據格式、傳輸速率

                     開始位   +數據位 +奇偶校驗位 +停止位

位數      1       5~8       0~1        1

電平      0       0/1        0/1        1

開始位:低電平 -- 設備檢測下降沿,代表開始

數據位:5 -- 0000 0101   5~8位 -- 8

奇偶校驗位:校驗一幀數據是否完整

奇偶校驗:數據位中1的個數+奇偶位中1的個數之和

如果是奇校驗:個數之和必須為奇數。

如果是偶校驗:個數之和必須為偶數。

例如:發送方:0110 0011 -- 0x63   采用奇校驗     奇偶校驗位為1

接收方:0110 0011    1   --- 正確  

                0100 0011    1   --- 錯誤

               0000 0011    1   --- 奇偶校驗正確,數據錯誤    -- 現在采用CRC校驗。

停止位:高電平 -- 總線空閑狀態為高電平

常用的幀格式:1+8+0+1 -- 1個開始位+8個數據位+0個奇偶校驗位+1個停止位

通信速率:波特率   bps  每秒鍾發送的位數,常見的波特率:9600  115200等等

進行通信的兩個設備,波特率必須一樣

 

常用的串口相關寄存器

  USART_SR狀態寄存器
  USART_DR數據寄存器
  USART_BRR波特率寄存器 (填寫下面計算后的數值)
  USART_CR1控制寄存器
根據該圖可了解串行通信的相關配置。根據下半圖,可得到波特率的計算方法:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

串口操作相關庫函數

void USART_Init(); //串口初始化:波特率,數據字長,奇偶校驗,硬件流控以及收發使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相關中斷

void USART_SendData();//發送數據到串口,DR
uint16_t USART_ReceiveData();//接受數據,從DR讀取接受到的數據

FlagStatus USART_GetFlagStatus();//獲取狀態標志位,SR
void USART_ClearFlag();//清除狀態標志位,SR
ITStatus USART_GetITStatus();//獲取中斷狀態標志位,SR
void USART_ClearITPendingBit();//清除中斷狀態標志位,SR

實驗4串口實驗的代碼中FWLib文件夾下stm32f10x_usart.c下的stm32f10x_usart.h下,可找到相關函數的聲明與定義,然后可再進行追溯。

串口配置一般步驟

 ①串口時鍾使能,GPIO時鍾使能:RCC_APB2PeriphClockCmd();
  ②串口復位:USART_DeInit(); 這一步不是必須的
  ③GPIO端口模式設置:GPIO_Init(); 模式設置為GPIO_Mode_AF_PP
  ④串口參數初始化:USART_Init();
  ⑤開啟中斷並且初始化NVIC(如果需要開啟中斷才需要這個步驟)
    NVIC_Init();
    USART_ITConfig();
  ⑥使能串口:USART_Cmd();
  ⑦編寫中斷處理函數:USARTx_IRQHandler();
 ⑧串口數據收發:
    void USART_SendData();//發送數據到串口,DR
    uint16_t USART_ReceiveData();//接受數據,從DR讀取接受到的數據
⑨串口傳輸狀態獲取:
    FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
    void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

 具體實現

第一步:新建模版,並使能串口時鍾和GPIO時鍾。

跟之前一樣,先建立一個簡單的模版。
  在USER文件夾下找到system_stm32f10x.c下的stm32f10x_rcc.h.中找到void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);之前就知道這個函數是用來使能的,然后先Go To xxx找到函數的定義后,在函數中assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));

查看函數中的第一個參數RCC_APB2PeriphGo To xxx后我們可以看到,該函數即可使能GPIOA又可使能USART1。故使能語句可這么編寫

void My_USART1_Init(){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);
}

串口復位非必須,故可省略。

 

 

 

第二步:GPIO端口模式設置

在USER文件夾下找到system_stm32f10x.c下的stm32f10x_gpio.h.中找到void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);該函數之前已經講解過了,程序可修改為:


 

 

 

 

 

 

 

 

 

 另外一個USARTX_RX是輸入 ,根據上面的圖知道設為上拉輸入或者浮空

 

 

 

    //2. 配置模式
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//推挽輸出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
void My_USART1_Init(){
    GPIO_InitTypeDef GPIO_InitStructure; //定義結構體
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);    //使能GPIO時鍾
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);    //使能串口時鍾
    
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;    //設置為復用推挽輸出
    GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;    //引腳9
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;    //速度10MHz
    GPIO_Init(GPIOA,&GPIO_InitStructure);    //GPIOA模式設置
    
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空輸入
    GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;    //引腳10
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;    //速度10MHz
    GPIO_Init(GPIOA,&GPIO_InitStructure);    //GPIOA模式設置
    
}

 

 

第三步:串口參數初始化和使能串口

在USER文件夾下找到system_stm32f10x.c下的stm32f10x_usart.h.中找到void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);、void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);,程序可修改為:

 

 

 

/* 配置USART模式 */
    //1. 時鍾使能
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    //2. 模式配置
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = brr;                                        //波特率   (數據傳輸速度)
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //非硬件流 (如何決定收發的時機)
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 //收發模式 (全雙工)
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;                     //RS232協議(數據格式):數據位     8個
    USART_InitStructure.USART_Parity = USART_Parity_No;                                //RS232協議(數據格式):奇偶校驗位 0個
    USART_InitStructure.USART_StopBits = USART_StopBits_1;                            //RS232協議(數據格式):停止位     1個
    USART_Init(USART1, &USART_InitStructure);

 

void My_USART1_Init(){
    GPIO_InitTypeDef GPIO_InitStructure; //定義GPIO結構體
    USART_InitTypeDef USART_InitStructure;//定義USART結構體
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);    //使能GPIO時鍾
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);    //使能串口時鍾
    
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;    //設置為復用推挽輸出
    GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;    //引腳9
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;    //速度10MHz
    GPIO_Init(GPIOA,&GPIO_InitStructure);    //GPIOA模式設置
    
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空輸入
    GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;    //引腳10
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;    //速度10MHz
    GPIO_Init(GPIOA,&GPIO_InitStructure);    //GPIOA模式設置
    
    
    USART_InitStructure.USART_BaudRate=115200;//波特率
    USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;//硬件流:無
    USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//TX和RX都使能故使用|
    USART_InitStructure.USART_Parity=USART_Parity_No;//奇偶校驗位:無
    USART_InitStructure.USART_StopBits=USART_StopBits_1;//停止位:1
    USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字長:8位
    
    
    USART_Init(USART1,&USART_InitStructure);//串口參數初始化
    USART_Cmd(USART1,ENABLE);    //使能串口
}

第四步:開啟中斷並且初始化NVIC

  在FWLIB文件夾下找到misc下找到void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup),函數中找到;assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));,在Go To xxx,查看填寫格式,選擇NVIC_PriorityGroup_2,即:兩位響應優先級和兩位搶占優先級。

  在USER文件夾下找到system_stm32f10x.c下的stm32f10x_usart.h.中找到void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)

 

 

 

寫NVIC函數

  //3. 配置接收中斷(串口回顯時注釋掉)
    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);            //接收中斷打開
    NVIC_InitTypeDef NVIC_InitStruct = {0};
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStruct);
//4. 初始狀態 -- 使能
    USART_Cmd(USART1, ENABLE);

第五步:編寫中斷處理函數

在文件stm32f10x_usart.h文件下打開:ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);

 

 

 

void USART1_IRQHandler(void){
    u8 res;
    if(USART_GetITStatus(USART1,USART_IT_RXNE)){//若是接收到中斷
        res=USART_ReceiveData(USART1);//接收數據
        USART_SendData(USART1,res);//接收到在發送數據,才可以在串口監視器中看到數據
        
    }
}

最后編譯就通過了,注意要把模版中文件夾SYSTEM中的uart刪除,因為定義重復了。

 

之后就可以上傳程序,然后利用串口調試器,注意調試器中的設置要跟程序一樣,波特率為115200,停止位1,等等。最終的實驗現象就是發送什么,最后在調試軟件中就會看到什么。

 

SYSTEM文件夾下,usart.c文件中,可看到以下代碼(實驗4串口實驗):

 

main函數中調用函數

 

 

void USART1_IRQHandler(void)                    //串口1中斷服務程序
    {
    u8 Res;
#if SYSTEM_SUPPORT_OS         //如果SYSTEM_SUPPORT_OS為真,則需要支持OS.
    OSIntEnter();    
#endif
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中斷(接收到的數據必須是0x0d 0x0a結尾)
        {
        Res =USART_ReceiveData(USART1);    //讀取接收到的數據
        
        if((USART_RX_STA&0x8000)==0)//接收未完成,判斷最高位是不是0,是0表示接收未完成,則往下執行。若為1則不往下執行(上一次接收沒清空)
            {
            if(USART_RX_STA&0x4000)//接收到了0x0d,若第二位為1則表示接收到0x0d,在往下判斷下一位是不是0x0a,若不是重新開始,是的話則則給第一位置1
                {
                if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
                else USART_RX_STA|=0x8000;    //接收完成了 
                }
            else //還沒收到0X0D,第二位不是1,則判斷什么時候接收到0x0d時,將第二位置為1
                {    
                if(Res==0x0d)USART_RX_STA|=0x4000;
                else//若一直沒收到的話,則一直講Res變量中的數值給變量,且通過位與來判斷該位是否溢出(若第1、2位有數據給他清零)
                    {
                    USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
                    USART_RX_STA++;
                    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,重新開始接收      
                    }         
                }
            }            
     } 
#if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS為真,則需要支持OS.
    OSIntExit();                                               
#endif
} 
#endif    

 

實驗4代碼詳細解釋

usart.h

 

 

#ifndef __USART_H
#define __USART_H
#include "stdio.h"    
#include "sys.h" 
//////////////////////////////////////////////////////////////////////////////////     
//本程序只供學習使用,未經作者許可,不得用於其它任何用途
//ALIENTEK STM32開發板
//串口1初始化           
//正點原子@ALIENTEK
//技術論壇:www.openedv.com
//修改日期:2012/8/18
//版本:V1.5
//版權所有,盜版必究。
//Copyright(C) 廣州市星翼電子科技有限公司 2009-2019
//All rights reserved
//********************************************************************************
//V1.3修改說明 
//支持適應不同頻率下的串口波特率設置.
//加入了對printf的支持
//增加了串口接收命令功能.
//修正了printf第一個字符丟失的bug
//V1.4修改說明
//1,修改串口初始化IO的bug
//2,修改了USART_RX_STA,使得串口最大接收字節數為2的14次方
//3,增加了USART_REC_LEN,用於定義串口最大允許接收的字節數(不大於2的14次方)
//4,修改了EN_USART1_RX的使能方式
//V1.5修改說明
//1,增加了對UCOSII的支持
#define USART_REC_LEN              200      //定義最大接收字節數 200
#define EN_USART1_RX             1        //使能(1)/禁止(0)串口1接收
          
extern u8  USART_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節.末字節為換行符 
extern u16 USART_RX_STA;                 //接收狀態標記    
//如果想串口中斷接收,請不要注釋以下宏定義
void uart_init(u32 bound);
#endif

 

bound 是波特率

extern 定義了一些變量 外部變量

 

 

 

 

 

接收從電腦傳來的數據存入buf

 

 

 0x0D=回車  0x0A=換行     二個結束符

接收完成 bit15-1  bit14-1  bit13~0 ----接收的位數

void USART1_IRQHandler(void)                    //串口1中斷服務程序
{
    u8 Res; 
#if SYSTEM_SUPPORT_OS         //如果SYSTEM_SUPPORT_OS為真,則需要支持OS.
    OSIntEnter();    
#endif
    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 ;//=0x3fff最多2^13-1數據
                    USART_RX_STA++;//有效數據個數++
                    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,重新開始接收      
                    }         //有效的數據個數不能大於定義的數據個數
                }
            }            
     } 
#if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS為真,則需要支持OS.
    OSIntExit();                                               
#endif
} 
#endif    

 

 

工程文件可供參考:

https://wwa.lanzoui.com/iG84Jvh1loj

 


免責聲明!

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



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