STM32-USART串口协议

ops/2024/10/20 7:39:19/

一、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.ppmy.cn/ops/126919.html

相关文章

如何在windows下搭建一个gitlab

在Windows上创建一个GitLab实例&#xff0c;最常用的方法是通过Docker来运行GitLab的官方镜像。以下是具体步骤&#xff1a; 前提条件 Windows系统&#xff1a;确保系统已经安装了Windows 10或更高版本。安装Docker Desktop&#xff1a;可以从 Docker官网 下载并安装。安装完…

【JS】哈希(数组)解决赎金信问题

思路 本文采用数组方式&#xff0c;创建长度为26的数组&#xff0c;用字母对应的Unicode编码做索引&#xff0c;先将杂志字符串每个字母存储到对应位置&#xff0c;再遍历赎金信字符串&#xff0c;减去数组中对应字母的计数&#xff0c;如果出现计数不够减的情况&#xff0c;返…

Docker学习笔记(3) - Docker命令

1. 帮助命令 docker version # 显示docker版本信息 docker info # 显示docker系统信息&#xff0c;包括镜像和容器 docker 命令 --help # 帮助命令2. 镜像命令 docker images # 查看本地主机上的镜像# 搜索镜像 docker search 镜像名称 # 搜索镜像 #…

对象的增删改查,数组api,字符串api,Date对象api,Math对象api

一、对象 1.什么是对象&#xff1f; 在js&#xff0c;对象是一组拥有无序的 属性和 方法的集合 生活中&#xff0c;万事万物皆对象&#xff0c;对象中可以抽象处两个概念&#xff1a;特征(属性)和行为&#xff08;方法&#xff09; 人&#xff1a;特征有姓名性别年龄身高体重…

英伟达CEO黄仁勋在BG2播客上做客

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

九、SQL 进阶之路:深入探索数据库查询的艺术

SQL 进阶之路&#xff1a;深入探索数据库查询的艺术 在当今这个数据驱动的时代&#xff0c;数据已经成为了各个行业和领域中至关重要的资源。在这样的大背景下&#xff0c;掌握 SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;已然成为了数据…

初学结构化查询语言的操作

SQL: Structured Query Language 结构化查询语言(Structured Query Language)简称SQL&#xff0c;是一种特殊目的的编程语言&#xff0c;是一种数 据库查询和程序设计语言&#xff0c;用于存取数据以及查询、更新和管理关系数据库系统。 一&#xff0c;SQL的分类 1&#xff…

CCS字体、字号更改+CCS下载官方链接

Step1、 按照图示箭头操作 step2 Step3 点击确定&#xff0c;点击Apply(应用)&#xff0c;点击Apply and close(应用和关闭) 4、历代版本下载链接 CCS下载&#xff1a;官方链接https://www.ti.com/tool/CCSTUDIO The last but not least 如果成功的解决了你的问题&#x…