STM32
直播中

李华瑞

7年用户 959经验值
私信 关注
[问答]

USART串口通信该如何去使用呢

什么是串行通信?串行通信的实现方式有哪几种?
USART串口通信该如何去使用呢?


回帖(1)

黄剑屏

2021-12-8 10:59:29
今天介绍USART串口通信的使用,目的在于会用串口传送和接收数据。内容包含了NVIC中断的知识,下一篇着重讲NVIC中断。ADC在下下一篇。主要还是先看懂USART的相关代码。
什么是串行通信

我们对通信的字面意思理解就是信息的传输与交换。通信按照基本类型可以分为并行通信和串行通信。并行通信时数据的各个位同时传送,可以实现字节为单位通信,但是因为通信线多占用资源多,成本高。而串口通信传送数据是一帧一帧发送,传送距离相对较远,占用资源少,成本低,但是串行通信传输速度相对于并行通信传输速度较慢。单片机上大多使用串行通信。            
串行通信是单片机最常用的一种通信技术,通常用于单片机和电脑之间以及单片机和单片机之间的通信。单片机的串口是全双工异步串口通信方式,通过TXD引脚发送,RXD引脚接收输入。





串行通信的实现方式--数据传输方式

同步通信:接收时钟与发送进钟严格同步,通常要有同步时钟,日常应用在大批量数据传输上。例(SPI、IIC通信接口)
异步通信:接收时钟与发送进钟不需要严格同步,但是必须设置相同的波特率,否则传输数据会产生异常。例(单总线,uart)
串行通信的实现方式--数据传输方向

根据串行通信的数据传输方向,串行通信方式分为三种:单工、单双工、全双工。
单工: 信息只可以从一方传向另一方。
半双工: 任意时刻信息都可以从一方传向另一方,但不能同时发生。即A-->B或B-->A。
全双工:任意时刻信息都可以双向的传输,即A-->B且B-->A。
串口通信的基础概念

串口通讯在协议层中规定了数据包的内容,具体包括起始位、主体数据(8位或9位)、校验位以及停止位,通讯的双方必须将数据包的格式约定一致才能正常收发数据。 1、波特率:由于异步通信中没有时钟信号,所以接收双方要约定好波特率,即每秒传输的码元个数,以便对信号进行解码,常见的波特率有4800、9600、115200等。 2、起始和停止信号:数据包的首尾分别是起始位和停止位,数据包的起始信号由一个逻辑0的数据位表示,停止位信号可由0.5、1、1.5、2个逻辑1的数据位表示,双方需约定一致。 3、有效数据:有效数据规定了主题数据的长度,一般为8或9位 4、数据校验;在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0校验(space)、1校验(mark)以及无(noparity)。
USART工作过程(usart比uart多了同步的功能)

UART传输数据依靠的是UART总线,数据总线用于通过微控制器等其他设备将数据发送到UART。数据以并行形式从数据总线传输发送到UART。UART从数据总线获得并行数据之后,它将添加起始位,奇偶校验位和停止位,从而创建数据包。接下来,数据包在Tx引脚上逐位串行输出。UART接收端则在其Rx引脚上逐位读取数据包。然后,接收UART将数据转换回并行形式,并删除起始位,奇偶校验位和停止位。最后,接收UART将数据包并行传输到接收端的数据总线。
介绍了其工作过程,我们现在看看其关键代码实现,下面是对USART串口配置的初始化代码,基本没有什么难的,请自动忽略NVIC的部分下一篇主要讲他:

1) 串口时钟使能,GPIO 时钟使能
2) 串口复位
3) GPIO 端口模式设置
4) 串口参数初始化
5) 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
6) 使能串口

void uart_init(u32 bound){

//定义需要用到的结构体
GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
//使能GPIOA与USART1的时钟         
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);       
  
//配置GPIO PA9为输出端  PA10为输入端
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);
   
  
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);

//NVIC配置    下个主要讲
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;               
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                       
        NVIC_Init(&NVIC_InitStructure);       
  
//USART的初始化配置,根据上面USART的基础知识来配

        USART_InitStructure.USART_BaudRate = bound;  //波特率
        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);    //串口1初始化
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);  //开启串口1中断
  USART_Cmd(USART1, ENABLE);                      //串口1使能

}



加上重定义代码段就可以直接用printf()来给上位机传数据了


int fputc(int ch, FILE *f)
{      
        while((USART1->SR&0X40)==0);
    USART1->DR = (u8) ch;      
        return ch;
}
那上位机如何给单片机传输数据呢?请看下面的代码:


void USART1_IRQHandler(void)                       
        {
        u8 Res;
        if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
                {
                Res =USART_ReceiveData(USART1);  //保存当前传送来的数据
               
                if((USART_RX_STA&0x8000)==0)   //如果接收未完成
                        {
                        if(USART_RX_STA&0x4000)//如果次高位为1

                                {
                                if(Res!=0x0a)USART_RX_STA=0;//次高位已经为1,所以本次接收结果一定为0x0a,不为0x0a的话就是接收过程错误,重新接受。
                                else USART_RX_STA|=0x8000;//此时的数据为0x0a,则将最高位改为1代表接受已经完成
                                }
                        else
                                {       
                                if(Res==0x0d)USART_RX_STA|=0x4000;       //次高位不为1,但是本次数据为0x0d,则将次高位设置为1
                                else
                                        {
                                        USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;//次高位不为1,本次数据也不是为0x0d,缓冲区加1
                                        USART_RX_STA++;                                                                                                       
                                        if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//超出最大数据容量,接受错误,重新接收
                                        }                 
                                }
                        }                    
     }

}
想信许多没有基础的同学看这这些代码很蒙,没关系下来我给大家介绍一下代码的意思。


上位机给串口发送数据时,都会给数据末尾加上0x0d,0x0a。0x0d是回车的意思,0x0a是换行的意思。由此我们就可以判断数据何时发送结束了。


USART_RX_STA是接收状态标记。是16位的数据,他的最高位可以判断接受是否完成,次高位为判断数据是否为回车,回车的ASCLL是0X0d。其他14位用来存储数据的长度。USART_RX_BUF[]是接收缓冲区,他被下面代码定义为最大可以接收200个字节(这是usart.h里的代码)。


#define USART_REC_LEN                          200
extern u8  USART_RX_BUF[USART_REC_LEN];
上面代码其实就是为了上位机给的数据被成功的接收,很难表达出来,详细注释都给了,基本没很大难度,有需要私聊。


主函数如下,目的是将上位机给单片机的数据又返回给上位机,便于观察是否传输正确:


while(1)
        {
                if(USART_RX_STA&0x8000)  //最高位为1,接收完成
                {                                          
                        len=USART_RX_STA&0x3fff;   //取出数据长度到len
        //下面为函数重定义,将单片机收到的数据再返回给上位机,主要起了提示作用
for(t=0;t                         {
                                USART1->DR=USART_RX_BUF[t];
                                while((USART1->SR&0X40)==0);
                        }
    //传输完成清空标志位
                        USART_RX_STA=0;
                }
    //此时没有数据可以输出给上位机,请再发送数据
                else
                {
                        times++;
                        if(times%200==0)printf("请输入数据:rn");  
                        delay_ms(10);   
                }
        }         
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分