当前位置: 首页 > news >正文

USART串口(发送和接收)

目录

一. USART串口协议

二. USART串口外设

三. 串口发送+接收

四. 效果展示


一. USART串口协议

        USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器。

        通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统。

        通信的协议:制定通信的规则,通信双方按照协议规则进行数据收发。

单片机有了通信功能,就能与众多别的模块互联。总的来说,通信的目的就是进行信息传递,双方约定的规则就是通信协议。通信协议不止一种,在STM32中,就有诸如USART,I2C,SPI,CAN,USB等通信。今天我们主要介绍USART串口通信。

        单片机的串口可以使单片机与单片机,单片机与电脑,单片机与各种各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。

其中,单片机和电脑通信,是串口的一大优势,可以接电脑屏幕,非常适合调试程序,打印信息。

        串口的硬件电路,需要其中一个设备的发送端TX连接另外一个设备的接收端RX,接收端RX连接另一个设备的发送端TX。当只需单向的数据传输时,可以只接一根通信线。

TX和RX都是单端信号,它们的高低电平都是相对于GND的。所以串口通信的TX,RX,GND都是必须要连接的。

        串口的参数及时序:

1)波特率:串口通信的速率。由于串口一般使用异步通信,所以需要双方约定一个通信速率。它决定了每隔多久发送一位。

2)起始位:它是标志一个数据帧的开始,固定为低电平。首先串口的空闲状态是高电平,也就是没有数据传输的时候,引脚必须要置高电平,作为空闲状态。然后需要传输的时候,就必须要先发送一个起始位,这个起始位必须是低电平,来打破空闲状态的高电平,产生一个下降沿。这个下降沿就告诉接收设备,这一帧数据要开始了。

3)停止位:在一个字节数据发送完成后,必须要有一个停止位。这个停止位的作用就是用于数据帧间隔,固定为高电平。同时,这个停止位,也是为下一个起始位做准备的。

4)数据位:表示数据帧的有效载荷,1为高电平,0为低电平,低位先行。低位先行的意思就是数据先从低位发送。

5)校验位:用于数据验证,根据数据位计算得来。校验可以选择3种方式。无校验,奇校验和偶校验。

二. USART串口外设

        一般我们串口很少使用同步功能,所以USART和UART使用起来,并没有太大的区别。所以我们学习串口,主要还是异步通信。

        USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里。

        USART外设就是串口通信的硬件支持电路。当我们配置好了USART电路,直接读写数据寄存器,就能自动发送和接收数据了。

        举个案例,我们STM32F103C8T6的USART资源就有如下:USART1,USART2,USART3.总共3个独立的USART外设,可以挂载很多串口设备。其中,USART1是APB2总线上的设备,剩下的两个都是APB1总线的设备。

下面我们来了解一下整个USART的框图和结构:

如上所示, 串口的数据寄存器就包括上面的右边部分,发送或接收的字节数据就存在这里。一个是发送数据寄存器TDR(Transmit DR)和接收数据寄存器RDR(Receive DR)。

但其实,这两个数据寄存器都占用一个地址,在程序上只表现为一个寄存器,那就是数据寄存器DR(Data Register)。

当数据从TDR移动到移位寄存器时,会置一个标志位,叫TXE(TX Empty),发送寄存器空。我们检查这个标志位,如果置1了,我们就可以在TDR写入下一个数据了。然后发送移位器就会在下面的发送器控制的驱动下,向右移位,然后一位一位地,把数据输出到TX引脚。向右移位,正好和串口协议规定的低位先行,是一致的。

同样的,接收数据的时候,也会置一个标志位,叫RXNE(RX Not Empty),接收数据寄存器非空。当我们检测到RXNE置1之后,就可以把数据读走了。当数据从移位寄存器转移到RDR时,就可以直接移位接收下一帧数据了。

如上所示的中间位置,有一个唤醒单元,而这个唤醒单元的作用就是用来实现多设备的功能。

串口一般是点对点模式。

 

上面的时钟输入一般是fPCLKx(x=1或2)。

USART1挂载在APB2,所以就是PCLK2的时钟,一般是72M.其他的USART都挂载在APB1,所以是PCLK1的时钟,一般是32M.

之后这个时钟进行一个分频,除以一个USARTDIV的分频系数。之后分频完后,还要再除个16,得到发送器时钟和接收器时钟,通向控制部分。

然后右边这里,如果TE(TX Enable)为1,就是发送器使能了,发送部分的波特率就有效。

如果RE(RX Enable)为1,就是接收器使能了,接收部分的波特率就有效。

到这里USART的框图就看完了,剩下的就是一些寄存器的指示,比如各个CR控制寄存器的哪一位控制那一部分电路。这些都可以自己看看手册里的寄存器描述。

最后我们看下USART基本结构:

上图中的最左边这里就是波特率发生器,用于产生约定的通信速率。

时钟来源是PCLK2或1.

三. 串口发送+接收

        通过观察引脚定义表,我们可以发现USART1的TX是PA9,RX是PA10.

如下所示:

 所以根据产品手册和上文中所介绍的USART串口结构,我们可以总结除如下串口的相关代码配置流程:

1)开启时钟,把需要用到的USART和GPIO的时钟打开

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启对应的GPIO口

2)GPIO初始化,把TX配置成复用功能(因为只有复用功能才能让片上外设模块的复用功能生效),RX配置成输入模式。

    /*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA9引脚初始化为复用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;           //将PA10引脚初始化为上拉输入模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					

3)配置USART,可以直接使用一个结构体,就可以把相关的所有参数配置好了。

    /*USART初始化*/USART_InitTypeDef USART_InitStructure;					//定义结构体变量USART_InitStructure.USART_BaudRate = 9600;				//波特率USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_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);		

4)如果你只需要发送的功能,就直接开启USART,初始化就完成了。如下所示:

USART_Cmd(USART1, ENABLE);

如果你需要接收的功能,可能还需要配置中断,那就在开启USART之前,再加上ITConfig和NVIC的代码就行了。如下所示:

    /*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//选择配置NVIC的USART1线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);						

在上面进行好了USART相关初始化后,就需要编写串口函数了,在启动文件startup_stm32f10x_md.s中找到串口的名称,如下所示:

void USART1_IRQHandler(void)
{if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断{Serial_RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量Serial_RxFlag = 1;										//置接收标志位变量为1USART_ClearITPendingBit(USART1, USART_IT_RXNE);			//清除USART1的RXNE标志位//读取数据寄存器会自动清除此标志位//如果已经读取了数据寄存器,也可以不执行此代码}
}

我们就可以编写程序来测试下了。这里例举两个测试案例:

#include "Serial.h"uint8_t RxData;			//定义用于接收串口数据的变量int main(void)
{/*模块初始化*/OLED_Init();		//OLED初始化/*显示静态字符串*/OLED_ShowString(1, 1, "RxData:");/*串口初始化*/Serial_Init();		//串口初始化while (1){if (Serial_GetRxFlag() == 1)			//检查串口接收数据的标志位{RxData = Serial_GetRxData();		//获取串口接收的数据Serial_SendByte(RxData);			//串口将收到的数据回传回去,用于测试OLED_ShowHexNum(1, 8, RxData, 2);	//显示串口接收的数据}}
}

四. 效果展示

        在这里我们使用某一位b站博主自己做的串口助手,并对它设置好波特率为9600,数据位为8,停止位为1等参数,然后我们发送一个数23,如下所示:

 然后我们再来看下我们的OLED调试屏幕:

如上所示,屏幕上也显示出了所接收到数据23.

好了,到这里所有的内容差不多已经全部讲完,如果有任何疑问或者问题都可以在下面进行留言或者私信。 

         

 

 

 

        

        


http://www.mrgr.cn/news/54159.html

相关文章:

  • Golang 并发编程:通道(Channel)的详细用法
  • OpenSEMBA :一个用于电磁场模拟的开源软件框架
  • IDEA如何查看所有的断点(Breakpoints)并关闭
  • Android 内存优化——常见内存泄露及优化方案
  • <项目代码>YOLOv8路面病害识别<目标检测>
  • 微信小程序中的文件查看方法
  • 【MySQL】多表查询——内连接,左/右连接
  • MyBatisPlus
  • 【算法】将单向链表按某值分成左边小、中间相等、右边大的形式
  • 在各大媒体报纸上刊登自己的文章用什么投稿方法发表快?
  • RDK X5 目标跟踪核心代码Deepsort
  • Flux.concat 使用说明书
  • 注塑机机械手升降传送机程序
  • 现代大数据架构Kappa
  • 【Java】—JavaBean转换方法详解
  • 数据结构练习题4(链表)
  • 网络空间安全之一个WH的超前沿全栈技术深入学习之路(二:渗透测试行业术语扫盲)作者——LJS
  • 单位评职称需要在指定媒体上投稿发表文章看我如何轻松应对
  • CGAL概述
  • 缓冲区类QBuffer
  • python-库
  • 【OD】【E卷】【真题】【100分】光伏场地建设规划(PythonJavajavaScriptC++C)
  • Chapter 2 - 7. Understanding Congestion in Fibre Channel Fabrics
  • mysql数据量分库分表
  • SOCKET与底层TCP协议的关系
  • 数据库产品中传输中的数据加密(Encryption in Transit)方法简介