STM32F103学习笔记三 串口通信

2022-11-07 15:24:05 浏览数 (1)

STM32F103学习笔记三 串口通信

本文参照正点原子STM32F1xx官方资料:《STM32中文参考手册V10》-第25章通用同步异步收发器(USART) 及 【STM32】串口通信基本原理(超基础、详细版) 单片机入门学习十 STM32单片机学习七 串口通讯

1、串口通信背景知识

1.1、 设备之间的通信方式

并行通信

串行通信

传输原理

数据各个位同时传输

数据按位顺序传输

优点

速度快

占用引脚资源少

缺点

占用引脚资源多

速度相对较慢

1.2、 串行通信分类
1.2.1、按照通信方式,分为:

同步通信:带时钟同步信号传输。比如:SPI,I²C通信接口 异步通信:不带时钟同步信号。比如:UART(通用异步收发器),单总线

1.2.2、按照数据传送方向,分为:

单工:数据传输只支持数据在一个方向上传输; 半双工:允许数据在两个方向上传输。但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;它不需要独立的接收端和发送端,两者可以合并一起使用一个端口。 全双工:允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,需要独立的接收端和发送端。

1.3、常见的串行通信接口

2、STM32的串口通信基础

2.1、STM32的串口通信接口有两种,分别是:

UART(通用异步收发器)、 USART(通用同步异步收发器)、 对于大容量STM32F10x系列芯片,分别有3个USART和2个UART。

2.2、UART引脚连接方法

① 单片机连接单片机

RXD:数据输入引脚,数据接受 TXD”数据发送引脚,数据发送

对于两个芯片之间的连接,两个芯片GND共地,同时TXD和RXD交叉连接。这里的交叉连接的意思就是,芯片1的RxD连接芯片2的TXD,芯片2的RXD连接芯片1的TXD。这样,两个芯片之间就可以进行TTL电平通信了

② 单片机连接PC

若是芯片与PC机(或上位机)相连,除了共地之外,就不能这样直接交叉连接了。尽管PC机和芯片都有TXD和RXD引脚,但是通常PC机(或上位机)通常使用的都是RS232接口,因此不能直接交叉连接。RS232接口是9针(或引脚),通常是TxD和RxD经过电平转换得到的。而单片机采用的是 TTL电平,所以需要 连接一个RS232转换器 将TTL电平转换成 PC可以识别的RS232电平,再交叉连接。

经过电平转换后,芯片串口和rs232的电平标准是不一样的:

代码语言:javascript复制
单片机的电平标准(TTL电平): 5V表示1,0V表示0;
Rs232的电平标准: 15/ 13 V表示1,-15/-13表示0。

RS-232通讯协议标准串口的设备间通讯结构图如下:

所以单片机串口与PC串口通信就应该遵循下面的连接方式: 在单片机串口与上位机给出的rs232口之间,通过电平转换电路(如下面图中的Max232芯片) 实现TTL电平与RS232电平之间的转换。

2.3、UART异步通信特点
  • 全双工异步通信;
  • 分数波特率发生器系统,提供精确的波特率。发送和接受共用的可编程波特率,最高可达4.5Mbits/s;
  • 可编程的数据字长度(8位或者9位);
  • 可配置的停止位(支持1或者2位停止位);
  • 可配置的使用DMA多缓冲器通信;
  • 单独的发送器和接收器使能位;
  • 检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志;
  • 多个带标志的中断源,触发中断;
  • 其他:校验控制,四个错误检测标志。
2.4、STM32串口异步通信需要定义的参数

STM32异步通信参数: ①起始位 ②数据位(8位或者9位) ③奇偶校验位(第9位) ④停止位(1,15,2位) ⑤波特率设置 (奇偶校验位分为奇校验和偶校验两种,是一种简单的数据误码校验方法。奇校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为奇数;偶校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为偶数。 校验方法除了奇校验(odd)、偶校验(even)之外,还可以有:0 校验(space)、1 校验(mark)以及无校验(noparity)。 0/1校验:不管有效数据中的内容是什么,校验位总为0或者1) UART串口通信的数据包以帧为单位,常用的帧结构为:1位起始位 8位数据位 1位奇偶校验位(可选) 1位停止位。如下图所示

2.5、 UART(USART)框图

任何USART双向通信至少需要两个脚:接收数据输入(RX)和发送数据输出(TX)。 RX:接收数据串行输。通过过采样技术来区别数据和噪音,从而恢复数据。 TX:发送数据输出。当发送器被禁止时,输出引脚恢复到它的I/O端口配置。当发送器被激活,并且不发送数据时,TX引脚处于高电平。在单线和智能卡模式里,此I/O口被同时用于数据的发送和接收。 ● 总线在发送或接收前应处于空闲状态 ● 一个起始位 ● 一个数据字(8或9位),最低有效位在前 ● 0.5,1.5,2个的停止位,由此表明数据帧的结束 ● 使用分数波特率发生器 —— 12位整数和4位小数的表示方法。 ● 一个状态寄存器(USART_SR) ● 数据寄存器(USART_DR) ● 一个波特率寄存器(USART_BRR),12位的整数和4位小数 ● 一个智能卡模式下的保护时间寄存器(USART_GTPR)

2.6、 串口通信过程

① 数据接收过程 外部设备将数据发送到 串行输入移位寄存器,串行输入移位寄存器在将数据传送到输入数据缓冲器,MCU在从输入数据缓冲器中读出数据

② 数据发送过程 MCU将要发送的数据写入输出数据缓冲器,输出数据缓冲器在将数据写入串行输出移位寄存器,串行移位寄存器在将数据输出到外部设备

3、 串行通信的编程

3.1、串口通信的相关寄存器
3.1.1、 USART_SR状态寄存器

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

3.1.2、 USART_DR数据寄存器
代码语言:javascript复制
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
3.1.3、 USART_BRR波特率寄存器
代码语言:javascript复制
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

波特率计算方法:

3.2、 串口操作相关库函数(省略入口函数):
代码语言:javascript复制
**void USART_Init();** //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
**void USART_Cmd();**//使能串口
**void USART_ITConfig();**//使能相关中断

**void USART_SendData();**//发送数据到串口,DR
**uint16_t USART_ReceiveData();**//接受数据,从DR读取接受到的数据

**FlagStatus USART_GetFlagStatus();**//获取状态标志位
**void USART_ClearFlag();**//清除状态标志位
**ITStatus USART_GetITStatus();**//获取中断状态标志位
**void USART_ClearITPendingBit();**//清除中断状态标志位
3.3、 串口配置一般步骤:
  1. 串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
  2. 串口复位:USART_DeInit(); 这一步不是必须的
  3. GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
  4. 串口参数初始化:USART_Init();
  5. 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤) NVIC_Init(); USART_ITConfig();
  6. 使能串口:USART_Cmd();
  7. 编写中断处理函数:USARTx_IRQHandler();
  8. 串口数据收发: void USART_SendData();//发送数据到串口,DR uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
  9. 串口传输状态获取: FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

4、 串口编程

(参照3.2、3.3的章节,其中有关于代码配置的步骤) usart.c

代码语言:javascript复制
//串口1中断服务程序
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 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 ;
USART_RX_STA  ;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
}		 
}
}   		 
} 

main.c

代码语言:javascript复制
int main(void)
{		
u16 t;  
u16 len;	
u16 times=0;
delay_init();	    	 //延时函数初始化	  
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200);	 //串口初始化为115200
LED_Init();			     //LED端口初始化
KEY_Init();          //初始化与按键连接的硬件接口
while(1)
{
if(USART_RX_STA&0x8000)
{					   
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("rn您发送的消息为:rnrn");
for(t=0;t<len;t  )
{
USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
printf("rnrn");//插入换行
USART_RX_STA=0;
}else
{
times  ;
if(timesP00==0)
{
printf("rn精英STM32开发板 串口实验rn");
printf("正点原子@ALIENTEKrnrn");
}
if(times 0==0)printf("请输入数据,以回车键结束n");  
if(times0==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);   
}
}	 
}

关于代码编写的总结及注意点,以后有空总结

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/184240.html原文链接:https://javaforall.cn

0 人点赞