[串口]USART(stm32学习)
USART引脚组成
USARTx:
USARTx_Tx 发送
USARTx_Rx 接受
USARTx_CTS Clear To Send
USARTx_RTS Request To Send
USARTx_CK 同步模式的时钟线
硬件流控(CTS,RTS):就是控制串口数据的收发,通过请求发送允许发送的拉高拉低来防止数据的丢失,保证数据收发的完整性
优势:相比于软件流控,硬件流控不占据传输数据位、响应更快、高速串口、大数据场景下防丢包效果更稳定。
USART默认引脚与重映射引脚分布

USART1的默认Tx,Rx引脚位于PA9,PA10
重映射引脚(备用引脚)位于PB6,PB7
串口引脚的IO配置

串口工作模式
全双工
数据传输双向,两根线互不影响
半双工
数据传输虽然是双向的,既能从左向右发送数据,也能从右向左发数据,但是不能同时进行。
同步模式
在标准的串口基础上增加一根CK时钟线,负责在两个设备之间传输时钟信号,时钟信号可以把两个设备同步起来。
硬件流控
在标准串口基础上增加CTS,RTS这两个引脚,也是交叉连接。

Rx引脚可以选择输入浮空模式或输入上拉模式,但是更推荐使用输入上拉模式,因为Rx引脚负责接收数据的,如果这根线意外断开了,IO悬空,上拉电阻会将它拉到一个高电压(提供一个高电压1),防止IO悬空导致外界的电磁波信号造成干扰。
为什么不选输入下拉?
根据串口通信格式可知:
在空闲状态下,发送一个低电压表示起始位,然后跟着的是8~9位是数据位,数据位之后是停止位,停止位后面是空闲位,我们用高电压表示空闲,Rx断开时,上拉电阻提供一个高电压,表示串口处于空闲状态,这样对程序断线造成的影响最小。
USART内部标志位
标志位反映了USART模块内部工作状态信息。
TXE:发送数据寄存器空。
TDR为空时,TXE=1,否则TXE=0。
TC:发送完成标志。
当TDR为空且移位寄存器为空时,TC=1,否则TC=0。
错误标志位:PE、FE、NE、ORE
PE:Parity Error 奇偶校验错
FE:Frame Error 帧格式错误 接收到的时=是无效数据帧,FE=1,否则FE=0
NE:Noise Error 噪声错 接收的数据中检测到了噪声,NE=1,否则NE=0
ORE:Overrun Error 过载错 由于过载造成了数据丢失,发数据的速度大于收数据的速度,导致部分数据被覆盖,此时ORE由0变为1
串口初始化
1.先初始化串口引脚的IO配置
/*初始化USART1的Tx,Rx引脚*/
//默认 PA9-Tx 复用推挽模式
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct={0};
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
// 默认 PA10-Rx 输入上拉模式或者输入浮空模式
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOA,&GPIO_InitStruct);
使用重映射引脚时:
/*USART1重映射*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//我要使能IO的复用功能
GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);//将USART1的IO引脚重映射
//PB6-Tx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct={0};
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//PB7 Rx
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOB,&GPIO_InitStruct);
2.初始化USART模块,此处我们使用USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
USART_InitTypeDef USART_InitStruct={0};
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_StopBits=USART_StopBits_1;
USART_Init(USART1,&USART_InitStruct);
3.使能USART1外设(一定要记得!)
USART_Cmd(USART1,ENABLE);//闭合总开关,使能USART

串口初始化完成后,调用USART_Cmd编程接口去闭合总开关,使能USART模块,这样才算串口初始化真正完成!
查询USART标志位的值
串口发送数据
USART_GetFlagStatus(串口名称,要查询的标志位)
返回值:SET–>1 RESET–>0
if(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==SET)//判断发送数据寄存器是否为空
{
//需要执行的操作
}
if(USART_GetFlagStatus(USART1,USART_FLAG_TC)==SET)//判断数据是否发送完成
{
//需要执行的操作
}
把要发送的数据写入到发送数据寄存器当中:
USART_SendData(串口名称,uint16_t要发送的数据)
为什么这里使用2个字节?
根据串口通信格式,若我们采用无校验位,那么数据位有9位,要采用2字节存储
//发送0x01
USART_SendData(USART1,0x01);
使用串口一次性发送多个字节:
/*作用:使用串口一次性发送多个字节*/
/*参数:pData-要发送的数据,Size-要发送的字节数*/
/*pData[i] 每次取出的是一个 uint8_t,也就是一个字节。因为串口一次发一个字节*/
void My_USART_SendBytes(USART_TypeDef *USARTx,uint8_t*pData,uint16_t Size)
{
for(uint32_t i=0;i<Size;i++)
{
//1.等待发送数据寄存器空
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
//2.将要发送的数据写入发送数据寄存器
USART_SendData(USART1,pData[i]);
}
//3.等待数据发送完成
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
}
uint8_t bytestosend[]={1,2,3,4,5,6};
My_USART_SendBytes(USART1,bytestosend,6);
串口接收数据
RxNE:接收数据寄存器非空
当RDR非空时,RxNE=1,否则RxNE=0
从接收数据寄存器读取数据:
USART_ReceiveData(串口名称)
返回一个无符号16位整型,可以装下9位数据位的情况
读数据前要判断一下接收数据寄存器里有没有数据,有数据才能读出来
Step:
1.等待接收数据寄存器非空
2.接收数据
3.处理数据
#include "stm32f10x.h"
#include "delay.h"
#include "stdio.h"
void My_USART_SendBytes(USART_TypeDef*USARTx,uint8_t*pData,uint16_t Size);
void My_USART_Init(void);
void OnBoardLED_Init(void);
int main(void)
{
My_USART_Init();
OnBoardLED_Init();
uint16_t Data_Recv;
while(1)
{
//1.等待接收数据寄存器非空
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==RESET);
//2.接收数据
Data_Recv=USART_ReceiveData(USART1);
//3.处理数据
if(Data_Recv=='0')
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
}
else if(Data_Recv=='1')
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
}
}
//@简介:对USART1进行初始化
// PB6-Tx,PB7-Rx
// 115200,8,1,None,收发双向
void My_USART_Init(void)
{
/*USART1重映射*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);
//PB6 Tx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct={0};
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//PB7 Rx
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*初始化USART模块*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
USART_InitTypeDef USART_InitStruct={0};
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_StopBits=USART_StopBits_1;
USART_Init(USART1,&USART_InitStruct);
USART_Cmd(USART1,ENABLE);//闭合总开关,使能USART
}
void OnBoardLED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct={0};
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_Init(GPIOC,&GPIO_InitStruct);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
串口格式化打印
串口打印时间
while(1)
{
uint32_t currentTick=GetTick();
uint32_t ms=currentTick%1000;
currentTick=currentTick/1000;
uint32_t s=currentTick%60;
currentTick=currentTick/60;
uint32_t min=currentTick%60;
currentTick=currentTick/60;
uint32_t hour=currentTick;
printf("%02u:%02u:%2u.%03u\r\n",hour,min,s,ms);
Delay(100);
}
串口打印字符串
int fputc(int ch,FILE*f)
{
//1.等待TDR为空
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
//2.发送
USART_SendData(USART1,(uint8_t)ch);
return ch;
}
printf("Hello,world\r\n");
串口测试
发送一个字母
My_USART_SendByte(USART1,0x5a);
串口调试助手上显示:
发送一串数字
uint8_t bytestosend[]={1,2,3,4,5,6};
My_USART_SendBytes(USART1,bytestosend,6);
串口调试助手显示:
发送一个字符
My_USART_SendChar(USART1,'L');
串口调试助手显示:
发送一个字符串
My_USART_SendString(USART1,"L3zuibangla!");
串口调试助手显示:
打印字符名
const char *strName="miumiu";
My_USART_Printf(USART1,"Hi,%s!",strName);
串口调试助手显示:
串口发送字符控制LED
发送字符‘0’,LED熄灭,发送字符‘1’,LED亮起。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct={0};
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_Init(GPIOC,&GPIO_InitStruct);
char ch;
while(1)
{
ch= My_USART_ReceiveByte(USART1);
if(ch=='0')
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//熄灭
}
else if(ch=='1')
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//亮起
}
现象:
发送0:
板载LED熄灭
发送1:
板载LED亮起
串口返回接收到的字符串
while(1)//放到循环里,可以一直被执行,不然只能执行一次
{
char buffer[100];
if(My_USART_ReceiveLine(USART1,buffer,100,LINE_SEPERATOR_CRLF,-1)==0)// 阻塞/非阻塞读取一行串口数据(以回车换行CRLF结尾)
{
My_USART_SendString(USART1,buffer);// 收到完整一行,立刻把收到的字符串原路发回
}
}
串口调试助手显示:
发送123\r\n,串口接收到并返回123。
更多推荐

所有评论(0)