ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-10-18 09:03:48  阅读:145  来源: 互联网

标签:10 15 USART RX InitStructure 串口 GPIO USART1


   

通信的两种方式:

并行通信

  -传输原理:数据各个位同时传输。
  -优点:速度快
   -缺点:占用引脚资源多

串行通信

  -传输原理:数据按位顺序传输。
  -优点:占用引脚资源少
  -缺点:速度相对较慢

串行通信分类(按照数据传送方向)

  单工(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

 

标签:10,15,USART,RX,InitStructure,串口,GPIO,USART1
来源: https://www.cnblogs.com/halfup/p/15416480.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有