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

STM32-USART串口协议

一、USART与UART

1、区别

  • 同步通信‌:USART在同步通信时需要时钟来触发数据传输,能够提供主动时钟,这使得通信双方可以共享一个时钟信号来采样数据线。
  • ‌异步通信‌:在异步通信中,USART与UART没有区别,因为两者都不需要共享时钟信号,设备间的数据传输速

2、特点

UART

  • 全双工通信
  • 简单易用
  • 异步通信
  • 传输速率可调    

USART:

  • 全双工通信
  • 支持同步和异步通信
  • 支持多种高级功能,如硬件流控制、DMA
  • 自带波特率发生器,最高达4.5Mbps/s
  • 具有多种错误检测标志:包括帧错误、溢出错误、噪声检测、奇偶校验错误

二、USART

一、概述

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

  • USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
  • 自带波特率发生器,最高达4.5Mbits/s
  • 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2) 可选校验位(无校验/奇校验/偶校验)
  • 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN

STM32F103C8T6 USART资源: USART1、 USART2、 USART3。

硬件流控制,就是防止接收设备处理数据速度慢,而导致数据丢失的问题,比如A设备Tx向B设备的Rx发送数据,A设备一直在发,发的太快了,B设备处理不过来,如果没有硬件流控制,那B设备就只能抛弃新数据或者覆盖原数据了。如果有硬件流控制,在硬件电路上,会多出一根线,如果B没有准备好接收,就置高电平,如果准备好了,就置低电平。

二、USART框图

如下:

发送器控制者发送移位寄存器,接收器控制接收移位寄存器。

硬件数据流控制:nRTS(Require to Send,n表示低电平,发送),nCTS(Clear to Send,n表示低电平,接收)。

如下,设备A向设备B发送数据,设备A需要根据设备B上nRTS发送来的电平进行判断,如果是低电平设备A可以发送数据,检测到高电平就停止发送数据。

当我们向TDR写数据时,等待发送移位寄存器移完数据后,TDR向发送移位寄存器转移数据,这时TXE(TX Empty)标志位会置1,我们可以写入新的数据。

接收寄存器类似,当我们接收数据时,数据会从高位往低位这个方向移动,也就是向右移,当接收一个字节后,会向RDR寄存器转运,同时,RXNE(RX Not empty,接收数据寄存器非空)会置1,者样我们就可以读走数据了,

SCLK(同步时钟)用于同步通信,只支持发送,不支持接收。

唤醒单元就是挂载多个设备。

USART1挂载在APB2总线上,所以就是PCLK2的时钟,一般是72MHz。其他USART挂载在APB1,就是PCLK1的时钟,一般36MHz。

TE(TX Enable)置1就是发送器使能,发送部分的波特率就有效了。RE(RX Enable)置1就是接收器使能,接收部分的波特率就有效了。

 三、USART基本结构

 四、数据帧

停止位就是控制高平时间。

五、起始位侦测

 每个码元都要侦测。

 手册上写的说明:

 六、波特率发生器

发送器和接收器的波特率由波特率寄存器BRR里的DIV确定 ,计算公式:波特率 = fPCLK2/1 / (16 bote。DIV就是分频器。

三、串口发送代码

可以使用下面的函数可以重定向到串口,第一个函数可以用printf();第二个直接调用函数。

//重定向到串口
int fputc(int ch,FILE *f)
{Serial_SendByte(ch);return ch;
}//重定向到串口
void Serial_Printf(char *format,...)
{char String[100];va_list arg;va_start(arg,format);vsprintf(String,format,arg);//都添加到string字符串里va_end(arg);Serial_SendString(String);}

发送汉字时,要加一点东西,在魔法棒——>C/C++——>Misc Controls上加--no-multibyte-chars

 

可以在如下所示修改适合的编码形式。 

 

Serial.c:

#include "stm32f10x.h"                  // Device header
#include "stdio.h"
#include "stdarg.h"
void Serial_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;  //如果有错误,就把这个函数放在最前面USART_InitTypeDef  USART_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);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);
//	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //推挽输出
//	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//Polarity  极性USART_InitStruct.USART_Parity=USART_Parity_No ;               //奇偶USART_InitStruct.USART_BaudRate=9600;USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;USART_InitStruct.USART_Mode=USART_Mode_Tx;   //作为发送器,同时收发或上USART_InitStruct.USART_StopBits=USART_StopBits_1 ;USART_InitStruct.USART_WordLength=USART_WordLength_8b ;USART_Init(USART1,&USART_InitStruct);USART_Cmd(USART1,ENABLE);}void Serial_SendByte(uint8_t Byte)
{//每次写入数据时,硬件会自动将TXE标志位清零USART_SendData(USART1, Byte);	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);}void Serial_SendArray(uint8_t *Array,uint8_t length)
{uint16_t i;for(i=0;i<length;i++){Serial_SendByte(Array[i]);}
}void Serial_SendString(char *Array)
{while(*Array!='\0'){Serial_SendByte(*Array);Array++;}
}uint32_t Serial_Pow(uint16_t x,uint16_t y)
{uint32_t Result=1;while(y--){Result*=x;}return  Result;
}void Serial_SendNumber(uint32_t Number,uint8_t length)
{uint16_t i;for(i=0;i<length;i++){Serial_SendByte(Number/Serial_Pow(10,length-1-i)%10+0x30);}}//重定向到串口
int fputc(int ch,FILE *f)
{Serial_SendByte(ch);return ch;
}void Serial_Printf(char *format,...)
{char String[100];va_list arg;va_start(arg,format);vsprintf(String,format,arg);va_end(arg);Serial_SendString(String);}

Serial.h:

#ifndef _SERIAL_H
#define _SERIAL_Hvoid Serial_Init(void);void Serial_SendByte(uint8_t Byte);
void Serial_SendString(char *Array);void Serial_SendArray(uint8_t *Array,uint8_t length);void Serial_SendNumber(uint32_t Number,uint8_t length);
void Serial_Printf(char *format,...);#endif

main.c:

#include  "stm32f10x.h"                  // Device header
#include  "OLED.h"
#include  "delay.h"
#include  "Serial.h"
#include  "stdio.h"int main(void)
{//   uint8_t MyArray[]={0x41,0x43,0x44,0x45};//uint8_t MyArray[]="ABCDE11111111111";OLED_Init();Serial_Init();Serial_SendByte(0x41);//Serial_SendString(MyArray);//Serial_SendArray(MyArray,4);//Serial_SendNumber(12345,5);//printf("Num=%d\r\n",666);	//Serial_Printf("Num=%d\r\n",666);Serial_Printf("你好,世界");while(1) {}}

四、串口接收

Serial.c:

#include "stm32f10x.h"                  // Device header
#include "stdio.h"
#include "stdarg.h"uint16_t Rxdata;
char String[16]={0};
uint16_t Flag;
uint16_t Flag_String;
void Serial_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;  //如果有错误,就把这个函数放在最前面USART_InitTypeDef  USART_InitStruct;NVIC_InitTypeDef NVIC_InitStructure ;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);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);GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; //推挽输出GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;GPIO_Init(GPIOA,&GPIO_InitStructure);//Polarity  极性USART_InitStruct.USART_Parity=USART_Parity_No ;               //奇偶USART_InitStruct.USART_BaudRate=9600;USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;USART_InitStruct.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;   //作为发送器,同时收发或上USART_InitStruct.USART_StopBits=USART_StopBits_1 ;USART_InitStruct.USART_WordLength=USART_WordLength_8b ;USART_Init(USART1,&USART_InitStruct);USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn ;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStructure);USART_Cmd(USART1,ENABLE);}void Serial_SendByte(uint8_t Byte)
{//每次写入数据时,硬件会自动将TXE标志位清零USART_SendData(USART1, Byte);	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);}void Serial_SendArray(uint8_t *Array,uint8_t length)
{uint16_t i;for(i=0;i<length;i++){Serial_SendByte(Array[i]);}
}void Serial_SendString(char *Array)
{while(*Array!='\0'){Serial_SendByte(*Array);Array++;}
}uint32_t Serial_Pow(uint16_t x,uint16_t y)
{uint32_t Result=1;while(y--){Result*=x;}return  Result;
}void Serial_SendNumber(uint32_t Number,uint8_t length)
{uint16_t i;for(i=0;i<length;i++){Serial_SendByte(Number/Serial_Pow(10,length-1-i)%10+0x30);}}//重定向到串口
int fputc(int ch,FILE *f)
{Serial_SendByte(ch);return ch;
}void Serial_Printf(char *format,...)
{char String[100];va_list arg;va_start(arg,format);vsprintf(String,format,arg);va_end(arg);Serial_SendString(String);}uint8_t Serial_GetRxFlag(void)
{if(Flag==1){Flag=0;return 1;}		 return 0;}uint8_t Serial_GetRxData(void)
{return Rxdata;}void  USART1_IRQHandler(void)
{if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET){Rxdata=USART_ReceiveData(USART1);Flag=1;Flag_String++;USART_ClearITPendingBit(USART1,USART_IT_RXNE);}}

Serial.h:

#ifndef _SERIAL_H
#define _SERIAL_Hvoid Serial_Init(void);void Serial_SendByte(uint8_t Byte);
void Serial_SendString(char *Array);void Serial_SendArray(uint8_t *Array,uint8_t length);void Serial_SendNumber(uint32_t Number,uint8_t length);
void Serial_Printf(char *format,...);
uint8_t Serial_GetRxData(void);
uint8_t Serial_GetRxFlag(void);#endif

main.c:

#include  "stm32f10x.h"                  // Device header
#include  "OLED.h"
#include  "delay.h"
#include  "Serial.h"
#include  "stdio.h"extern char String[16];
uint16_t Serial_Rxdata;
int main(void)
{OLED_Init();Serial_Init();OLED_ShowString(1,1,"RXData:");while(1) {if(Serial_GetRxFlag()==1){
//				Serial_Rxdata=Serial_GetRxData();
//				Serial_SendByte(Serial_Rxdata);
//				OLED_ShowHexNum(1,8,Serial_Rxdata,2);OLED_ShowString(1,8,String);}}}

五、串口收发-数据包

1、数据模式

显示汉字时/字符时,需要两端的编码相同,否则会产生错误。

 2、HEX数据包

数据的包头包尾可以自己随便设置,但最好不要设置与传输的数据一样,否则会出现判错。


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

相关文章:

  • 奖金——Topsort
  • 《RECONX: RECONSTRUCT ANY SCENE FROM SPARSEVIEWS WITH VIDEO DIFFUSION MODEL》论文阅读
  • 记录 ruoyi-vue-plus在linux 部署遇到的问题
  • 【Python爬虫实战】XPath与lxml实现高效XML/HTML数据解析
  • MySQL索引优化
  • 【Next.js 项目实战系列】03-查看 Issue
  • jmeter响应断言放进csv文件遇到的问题
  • 第二课:Python入门学习之开发工具的安装
  • 专题:数组(已完结)
  • 2024全国大数据与计算智能挑战赛火热报名中!
  • 【优选算法】(第四十四篇)
  • 数据结构之红黑树的实现
  • 1 -《本地部署开源大模型》如何选择合适的硬件配置
  • AI全栈大模型项目实战,人工智能,多模态大模型,微调技术训练营,大模型多场景实战
  • 算力基础篇:从零开始了解算力
  • 张驰咨询:假如国人都成为六西格玛黑带,中国将会怎样?
  • C++之《剑指offer》学习记录(4):赋值运算符函数
  • 视频分割软件哪个好?无损分割视频片段就靠它
  • 某电子元器件企业人力资源管理体系搭建咨询项目
  • 【观点】机器学习与神经网络荣膺诺贝尔物理学奖的启示:科技的未来与物理学的转变