速通STM32-基础篇-UART
阅读小tips!(Look at my eyes!)
本篇基于电子类大学生自主学习嵌入式的学习总结,知识点理解输出较于浅显,只适用于深入学习stm32前的一些理解和引导,或是大学期末前一周速通,若要深入学习嵌入式,可借助阅读本篇来搭建一个入门嵌入式的框架,而后想深入可转战b站等学习网站,有更多的大牛在等待你!
一.串口(USART/UART)
1.什么叫做串口呢?
与上篇内容相同,简洁明了的话,USART就是:

UART:就是通用异步接收器发射器。在这里,我们通常不使用时钟线,采用异步模式。
2.串口究竟是用来做什么的呢?
我们可以来这样理解,日常生活中,人与人之间需要交流,那么设备与设备之间,单片机与单片机之间同样需要通信交流,那么串口,就是单片机间交流的桥梁,TX(transmit)如同我们的嘴巴,用来发送信息,RX(receive)如同我们的耳朵,用来接受信息。

3.串口之间通过什么来传递数据呢?
串口是一种通信接口,是MCU上的一种片上外设,串口依据通信协议来发送接收数据。
在这里可以通过一个简单的例子,来便于我们理解,如同人与人的交流一般,像不同的人有不同的语言格式,串口通信间依然有不同的数据帧格式。
数据帧格式:

空闲状态下,单片机保持高电平,当有数据要发送/接收时,发送方/接收方用一起始位拉低电平,表示通信开始,接着传输数据位,从低位(2^0)开始1bit1bit写入/读入,数据位通常为8~9位,最后一位为校验位,完整的数据帧格式还包括停止位,通常有0.5,1,1.5,2位组成,数据发送/接收完整后,单片机保持高电平,若下一次数据传输,起始位再次发起低电平。
通常情况下:
采用八位数据位,无校验位,一个停止位,来传输一个字节。
采用九位数据位,有校验位,一个停止位,来传输一个字节。
校验位:
通信过程中,可能发生数据位传输错误,通过校验位可明细数据位是否发生错误。
奇校验:要求数据位中有奇数个1。
偶校验:要求数据位中有偶数个1。
二.USART模块中的数据传输:

波特率:每秒钟传输多少位,在波特率寄存器中设置。 常用波特率:9600,115200。
TDR:(transmit data register)发送数据寄存器
TxE:(transmit data register empty) 当TxE为1时,标志发送数据寄存器为空。
TC:(transmit complete) 当TC为1时,标志发送完成,TDR和发送数据移位寄存器同时为空。
RxNE : (receive data register none empty ) 当RxNE标志为1,接收数据寄存器非空。
错误标志位:
PE:(parity error)若接收到的数据有校验错误,则PE=1,否则为0。
FE:(frame error)接收到了无效数据帧,则FE =1,否则为0。
NE:(noise error)接收的数据中检测到了噪声,则NE =1,否则为0。
ORE:(overrun error)由于过载造成了数据丢失,则ORE=1,否则为0。
如图所示,在控制电路中,我们初始化数据帧格式。设置数据位,停止位,校验方式。
串口的初始化:
//1.开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
结构体定义,串口初始化。
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 115200;//设置波特率
USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//设置收发双向
USART_InitStruct.USART_WordLength = USART_WordLength_8b;//设置八位数据位
USART_InitStruct.USART_Parity = USART_Parity_No;//设置无校验
USART_InitStruct.USART_StopBit = USART_StopBit_1;//设置一位停止位
USART_Init(USART,&USART_Init);//串口初始化
发送与接收数据:

串行: 一个通道,数据位排队走。一个一个bit位依次传输。
并行: 多个通道,数据位并肩走。一个字节形式8个bit位传输。以8个数据位,无校验位格式。
数据以二进制形式从发送数据寄存器中以并行输入的模式进入发送数据移位寄存器,并以串行输出的模式打包成数据帧,从TX的引脚发送出去。接收数据同理。
USART模块的引脚初始化:
查找芯片手册,可以看到:



半双工:两设备间的通信传输仅靠一条线,一边发送时,一边只能接收。
全双工:有两条数据传输线,可以同时发送同时接收。
同步模式下,需要连接时钟线,这里不考虑,硬件流量控制简单概括一下,如同一个反馈量,当发送数据时,发送数据寄存器与发送数据移位寄存器中数据还没传输完成,下一位数据来临时,硬件流量控制简称硬件流控会返回一个信息,让下一位数据等待寄存器清空。
Rx:全双工模式中,GPIO尽量配置为输入上拉模式,避免RX接口断线,若此时为输入浮空模式下,IO口如同天线,接收外界不定电磁波,产生电磁干扰,若为输入上拉模式,RX接口断线时,上拉电阻给IO口提供默认高电平。
USART引脚初始化:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
//初始化PA9 TX
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
//初始化PA10 RX
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA,&GPIO_InitStruct);
重映射功能:
在一块STM32F103C8T6中,我们知道这块芯片有48个引脚,除去特殊功能引脚外,提供使用的普通IO引脚并不多,但一个单片机那么多功能,引脚不够用了怎么办,这时并不用担心,单片机在设计时,已经帮我们解决了这个问题,引脚有复用功能,一个引脚包含有多功能,但当需要的功能同时占用一个引脚时,又该怎么办呢,当然这时候就引出了重映射的概念。
重映射就是当一个引脚复用功能起冲突时,单片机就会将留存的另一组引脚拿来使用。

比如:当PA9 PA10被用来定时器初始化时,启用引脚重映射功能,就能将PB6 PB7拿来做串口引脚初始化。其中重映射用到了AFIO模块,后话会详细讲解。
//1.开启AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remp_USART1,ENABLE);//USART1_REMAP=1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//初始化PB6 TX
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
//初始化PB7 RX
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_17;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB,&GPIO_InitStruct);
三.串口发送数据
利用串口发连续发送数据,此处默认串口,IO口已初始化完毕。串口助手GPIO章节发布过。
//闭合总开关
USART_Cmd(USART1,ENABLE);
定义串口连续发送数据函数:
//函数声明
void My_USART_SendBytes(USART_TypeDef *USARTx,uint8_t *pData,uint16_t Size);
//函数实现
void My_USART_SendBytes(USART_TypeDef *USARTx,uint8_t *pData,uint16_t Size)
{
for(uint32_t i = 0;i < Size;i++ )
{
//等待发送数据寄存器为空
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
//写入发送数据寄存器
USART_SendData(USART1,pData[i]);
//等待数据发送完成
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
}
}
uint8_t SendData[]={1,2,3,4,5};
My_USART_SendBytes(USART1,SendData,5);
串口格式化打印数据:

标准库中提前写好了一些函数,_weak称为弱方法,此处不做深层讲解,可以简单理解为,标准库为方便开发,提供了一些默认函数,开发者可以通过覆盖函数名重写来定义自己的函数。
# include <stdio.h>
int fputc (int ch, FILE *f)
{
//等待发送数据寄存器为空
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
//写入发送数据寄存器中
USART_SendData(USART1,(uint8_t)ch);
return ch;
}
int main (void)
{
printf("hello word\r\n");
while(1)
{
}
}
串口接收数据:
// 等待接收数据寄存器非空
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==RESET);
//接收数据
uint8_t bytercvd = USART_ReceiveData(USART1);
//默认PC13已初始化
if(bytercvd == '0')
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//亮灯
else if(bytercvd == '1')
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//灭灯
创作不易,请多多点赞,收藏,转发,您的支持,是我创作最大的动力!
内容若有误,在所难免,还望指出,定会积极订正,不胜感激,还望海涵!
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)