STM32学习笔记4 --- USART

news2024/12/22 18:28:49

目录

通信接口1  USART

串口的通信协议

硬件部分:

软件部分:

字节数据的传递:

stm32内部的USART外设

串口发送

串口发送+接收

Hex数据包

文本数据包

数据包的收发流程

串口收发Hex数据包

串口收发文本数据包


通信接口1  USART

USART----- USB转串口 -----电脑

USART是STM32内部集成的硬件外设,usb转串口是连接stm32和电脑的

usb转串口:usb协议  ch340芯片转换  输出TXD RXD(串口协议)

通信的目的:将一个设备的数据传送到另一个设备

通信协议:制定通信的规则,通信双方按照协议规则进行数据收发

USART  引脚:TX(数据发送脚)、RX(数据接收脚)

这里全双工,就是指通信双方能够同时进行双向通信

有单独时钟线,同步,接收方可在时钟信号的指引下进行采样

无单独时钟线,异步,需双方约定一个采样频率,采样位置对齐

单端通信接GND

差分信号抗干扰

在USART、I2C、SPI、CAN、USB这几个通信协议中,USART、I2C、SPI、USB都属于串口通信,而CAN虽然也用于串行通信,但其通常不被直接归类为传统的串口通信协议,而是作为一种特定的串行通信协议存在。

串口的通信协议

硬件部分:

 串口:单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信

串口接线(硬件电路)

TTL电平:+3.3V或+5V表示1,0V表示0

软件部分:

发送的一个字节的数据,串口中,每一个字节都装载在一个数据帧里面

每个数据帧都由起始位、数据位和停止位组成

字节数据的传递:

TX引脚输出定时翻转的高低电平

RX引脚定时读取引脚的高低电平

每个字节的数据加上起始位、停止位、可选的校验位,打包为数据帧,依次输出在TX引脚,另一端RX引脚依次接收

stm32内部的USART外设

(按照串口协议来产生和接收高低电平信号)

是串口通信的硬件支持电路

一个字节数据 \Leftrightarrow 波形

STM32F103C8T6 USART资源: USART1(APB2)、 USART2(APB1)、 USART3(APB1)

所以这个跳线帽。是用来选择通信电平的,也是给CH340芯片供电的

数据显示

串口发送

接线:共地

1.开启时钟(USART,GPIO)

2.GPIO初始化,TX复用输出,RX输入(这个程序仅输出可以先只初始化一个pa9  把PA9配置为复用推挽输出,供USART1的TX使用)

3.配置USART(发送:直接开启USART并初始化)(接收:配置中断,ITConfig,NVIC,开启USART)

4.发送函数,接收函数,获取发送和接收的状态,就调用获取标志位的函数

引脚定义表,USART1的TX是PA9,RX是PA10(上拉输入,浮空输入)

\r\n实现换行

//Serial.c
void Serial_Init(void)
{
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//2
    //1
	GPIO_InitTypeDef GPIO_InitStructure;
	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);					
	//2
	USART_InitTypeDef USART_InitStructure;	
				
	USART_InitStructure.USART_BaudRate = 9600;//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要
	USART_InitStructure.USART_Mode = USART_Mode_Tx;//模式,选择为发送模式
	USART_InitStructure.USART_Parity = USART_Parity_No;//奇偶校验,不需要
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位,选择1位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	//字长,选择8位
	USART_Init(USART1, &USART_InitStructure);			
	
	
	USART_Cmd(USART1, ENABLE);								//使能USART1,串口开始运行
}

//发送数据(从TX引脚发送一个字节数据)/
void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);//将字节数据写入数据寄存器,写入后USART自动生成时序波形
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//等待发送完成(等着置1)
	//下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位//
}

//发送数组
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)		
	{
		Serial_SendByte(Array[i]);		
	}
}

//发送字符串  
void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
		Serial_SendByte(String[i]);		
	}
}

//返回X的Y次方 
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;	//设置结果初值为1
	while (Y --)			//执行Y次
	{
		Result *= X;		//将X累乘到结果
	}
	return Result;
}

//发送(字符串形式的)数字  
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)		
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');	//依次调用Serial_SendByte发送每位数字
	}
}

/// 
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);//将printf的底层重定向到自己的发送字节函数
	return ch;
}

void Serial_Printf(char *format, ...)
{
	char String[100];				//定义字符数组
	va_list arg;					//定义可变参数列表数据类型的变量arg
	va_start(arg, format);			//从format开始,接收参数列表到arg变量
	vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中
	va_end(arg);					//结束变量arg
	Serial_SendString(String);		//串口发送字符数组(字符串)
}
/// 

串口发送+接收

初始化GPIO  PA9  PA10

上拉输入/浮空输入

USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	//模式,发送模式和接收模式均选择

串口接收来说,可以使用查询和中断两种方法

查询的流程是,在主函数里不断判断RXNE标志位,如果置1了,就说明收到数据了,那再调用ReceiveData,读取DR寄存器

while (1)
	
		if (USART_GetFlagStatus (USART1, USART_FLAG_RXNE) == SET)//检查串口接收数据的标志位
		{
			RxData = USART_ReceiveData (USART1);
            OLED_ShowHexNum(1, 1, RxData, 2);|
		}
	}

中断方法

开启RXNE标志位到NVIC的输出

配置NVIC

	/*中断输出配置*/
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//选择配置NVIC的USART1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设
	

中断函数接收数据

uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)			//如果标志位为1
	{
		Serial_RxFlag = 0;
		return 1;					//则返回1,并自动清零标志位
	}
	return 0;						//如果标志位为0,则返回0
}

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;			//返回接收的数据变量
}

void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断
	{
		Serial_RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量
		Serial_RxFlag = 1;										//置接收标志位变量为1
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);			//清除USART1的RXNE标志位
																//读取数据寄存器会自动清除此标志位
																//如果已经读取了数据寄存器,也可以不执行此代码
	}
}

以上仅能支持一个字节的接收

USART串口数据包

大量数据,以数据包的形式

数据包的作用是,把一个个单独的数据给打包起来,方便我们进行多字节的数据通信

数据包分割方法:额外添加包头包尾 

在连续不断的数据流中分割出数据包(抱团的部分   有特殊联系)

数据转换为字节流

载荷和包头包尾重复

Hex数据包

HEX数据包里面,数据都是以原始的字节数据本身呈现的

文本数据包

文本数据包里面。每个字节就经过了一层编码和译码

每个文本字符背后,还是一个字节的HEX数据

所以这个文本数据包,通常会以换行作为包尾,打印时一行一行显示

数据包的收发流程

发送

接收

状态机:我们需要设计一个能记住不同状态的机制,在不同状态执行不同的操作,同时还要进行状态的合理转移

串口收发Hex数据包

uint8_t Serial_TxPacket[4];	//定义发送数据包数组,数据包格式:FF 01 02 03 04 FE
//需要在.h文件声明一下(extern uint8_t Serial_TxPacket[];因为需要在main文件进行一个内容填充)
uint8_t Serial_RxPacket[4];//定义接收数据包数组(不含包头包尾)
uint8_t Serial_RxFlag;//定义 接收数据包标志位
······

//Serial_TxPacket数组的内容将加上包头(FF)包尾(FE)后,并发送出去
void Serial_SendPacket(void)
{
	Serial_SendByte(0xFF);
	Serial_SendArray(Serial_TxPacket, 4);
	Serial_SendByte(0xFE);
}


//USART1中断函数
void USART1_IRQHandler(void)
{
	static uint8_t RxState = 0;	//定义表示当前状态机状态的静态变量
	static uint8_t pRxPacket = 0;//定义表示当前接收数据位置的静态变量
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//判断是否是USART1的接收事件触发的中断
	{
		uint8_t RxData = USART_ReceiveData(USART1);	//读取数据寄存器,存放在接收的数据变量
		

		/*使用状态机的思路,依次处理数据包的不同部分*/
		//当前状态为0,接收数据包包头
		if (RxState == 0)
		{
			if (RxData == 0xFF)			//如果数据确实是包头
			{
				RxState = 1;			//置下一个状态
				pRxPacket = 0;			//数据包的位置归零
            }
		}
		//当前状态为1,接收数据包数据
		else if (RxState == 1)
		{
			Serial_RxPacket[pRxPacket] = RxData;	//将数据存入数据包数组的指定位置
			pRxPacket ++;				//数据包的位置自增
			if (pRxPacket >= 4)			//如果收够4个数据
			{
				RxState = 2;			//置下一个状态
			}
		}
		//当前状态为2,接收数据包包尾
		else if (RxState == 2)
		{
			if (RxData == 0xFE)			//如果数据确实是包尾部
			{
				RxState = 0;			//状态归0
				Serial_RxFlag = 1;		//接收数据包标志位置1,成功接收一个数据包
			}
		}
		
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);		//清除标志位
	}
}

static uint8_t RxState = 0;    (S)

静态变量类似全局变量,函数进入只会初始化一次0,函数退出后,数据仍有效

串口收发文本数据包

void USART1_IRQHandler(void)
{
	static uint8_t RxState = 0;		//定义表示当前状态机状态的静态变量
	static uint8_t pRxPacket = 0;	//定义表示当前接收数据位置的静态变量
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)	//判断是否是USART1的接收事件触发的中断
	{
		uint8_t RxData = USART_ReceiveData(USART1);			//读取数据寄存器,存放在接收的数据变量
		
		/*使用状态机的思路,依次处理数据包的不同部分*/
		
		/*当前状态为0,接收数据包包头*/
		if (RxState == 0)
		{
			if (RxData == '@' && Serial_RxFlag == 0)		//如果数据确实是包头,并且上一个数据包已处理完毕
			{
				RxState = 1;			//置下一个状态
				pRxPacket = 0;			//数据包的位置归零
			}
		}
		/*当前状态为1,接收数据包数据,同时判断是否接收到了第一个包尾*/
		else if (RxState == 1)
		{
			if (RxData == '\r')			//如果收到第一个包尾
			{
				RxState = 2;			//置下一个状态
			}
			else						//接收到了正常的数据
			{
				Serial_RxPacket[pRxPacket] = RxData;		//将数据存入数据包数组的指定位置
				pRxPacket ++;			//数据包的位置自增
			}
		}
		/*当前状态为2,接收数据包第二个包尾*/
		else if (RxState == 2)
		{
			if (RxData == '\n')			//如果收到第二个包尾
			{
				RxState = 0;			//状态归0
				Serial_RxPacket[pRxPacket] = '\0';			//将收到的字符数据包添加一个字符串结束标志
				Serial_RxFlag = 1;		//接收数据包标志位置1,成功接收一个数据包
			}
		}
		
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);		//清除标志位
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2109749.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

麦克风品牌排行榜前十名,浩瀚、西圣、优篮子领夹麦克风测评PK

​我是一名数码测评师&#xff0c;对各类数码都颇为了解&#xff0c;特别是无线麦克风这种使用频繁的设备&#xff0c;已经上手测评过众多产品。然而测评过程中发现&#xff0c;市场上存在不少劣质产品&#xff0c;打着知名品牌却用着成本很低的材料&#xff0c;性能说很强&…

无人机之摄像头篇

无人机的摄像头是无人机系统中的一个重要组成部分&#xff0c;它承担着捕捉视频和图像的关键任务。随着技术的不断发展&#xff0c;无人机摄像头在分辨率、稳定性、视角、夜视能力、变焦功能以及数据传输等方面都有了显著的进步。以下是一些关于无人机摄像头的主要特点和类型&a…

足球预测可以人工智能AI吗

足球预测可以使用人工智能&#xff08;AI&#xff09;进行&#xff0c;AI在分析数据的速度和精度方面具有显著优势。通过综合考虑球员状态、战术布局、队伍历史表现等众多因素&#xff0c;AI技术可以提供高精度的比赛预测。其中&#xff0c;最值得关注的是AI的机器学习能力&…

9月报名 | 海克斯康电子散热从入门到精通培训

您好&#xff01;感谢您长期以来对优飞迪科技与海克斯康的关注与支持。我们诚邀您参加9月23日-24日的海克斯康电子散热从入门到精通培训&#xff0c;本次培训将帮助您更好了解电子散热基础理论知识、CFD仿真流程及规范、电子散热中所遇现象的分析方法&#xff0c;以及通过采用s…

精益思维在智能电网中的应用和展望

在能源转型与数字化转型的双轮驱动下&#xff0c;智能电网正逐步成为未来电力系统的核心。它不仅提升了电网的灵活性、可靠性和效率&#xff0c;还为实现清洁能源的大规模接入和高效利用提供了坚实基础。而精益思维&#xff0c;这一源自制造业的管理哲学&#xff0c;正悄然融入…

镭射定位灯激光定位使用注意事项?

在现代工业、建筑测量、舞台设计以及科研实验等领域&#xff0c;镭射定位灯(常称激光定位器)因其高精度、远射程和直观性而得到广泛应用。然而&#xff0c;激光作为一种高强度光束&#xff0c;其使用若不当&#xff0c;不仅可能损害设备&#xff0c;还可能对人体健康造成危害。…

java开发简历详解

1、个人情况 姓名&#xff0c;性别&#xff0c;年龄&#xff0c;工作经验&#xff0c;邮箱&#xff0c;联系方式学校、学历、专业竞赛、获奖、班干部 2、个人技术栈 尽量详细的去展示每一个技术栈 1、JVM的写法&#xff1a;技术服务于项目。 2、写法的底气问题。谓词。肯定…

浅析网页不安装插件播放RTSP/FLV视频的方法

早期很多摄像头视频流使用的是RTSP、RTMP协议&#xff0c;播放这类协议的视频通常是在网页上安装插件。但现在越来越多的用户&#xff0c;对于网页安装插件比较反感&#xff0c;且随着移动设备的普及&#xff0c;用户更多的希望使用手机、平板等移动设备&#xff0c;直接可以查…

【MySQL】深圳大学数据库实验一

目录 一、实验目的 二、实验要求 三、实验设备 四、建议的实验步骤 4.1 使用SQL语句创建如上两张关系表 4.2 EXERCISES. 1 SIMPLE COMMANDS 4.3 EXERCISES 2 JOINS 4.4 EXERCISES 3 FUNCTIONS 4.5 EXERCISES 4 DATES 五、实验总结 5.1 数据库定义语言&#xff08;DDL…

mac上jmeter的安装和使用

1、下载jmeter&#xff1a; https://jmeter.apache.org/download_jmeter.cgi 2、解压下载后的文件 3、在解压后的文件夹中&#xff0c;找到bin目录&#xff0c; 4、修改jmeter.properties文件&#xff0c;将界面改为中文 languagezh_CN 5、执行命令&#xff0c;运行Apac…

docker构建多系统架构

manifest是什么&#xff0c;干什么用 manifest是一个文件&#xff0c;这个文件包含了有关于镜像信息&#xff0c;如层、大小和摘要。docker manifest命令还向用户提供附加信息&#xff0c;比如构建镜像的操作系统和体系结构。而manifest list是一个镜像清单列表&#xff0c;用…

必备资源!精选大模型领域100篇必读论文,赶紧加入收藏夹!

本文主要为当前大模型领域热门研究方向&#xff08;如文生图、文生视频、文生音乐等&#xff09;的热门论文。希望能够为大家提供较为全面的大模型最新研究进展。当然&#xff0c;目前还无法涵盖所有热门论文以及研究方向&#xff0c;望请见谅。 以下&#xff0c;为2024年2月份…

spring cloud 应用连接开启鉴权的nacos 的一个坑(鉴权失败)

Nacos开启鉴权之后&#xff0c;bootstrap.yml增加spring.cloud.nacos.config.username 和 password&#xff0c;如下&#xff1a; spring:application:name: artifactIdactive: testcloud:nacos:config:server-addr: ${NACOS_HOST:192.168.21.60:8848}namespace: ${NACOS_NAME…

污泥浓度在线分析仪

污泥浓度在线分析仪在保障水质安全、守护碧水蓝天方面扮演着至关重要的角色。随着城市化进程的加快和工业发展的不断推进&#xff0c;水体污染问题日益严峻&#xff0c;污泥作为污水处理过程中的副产物&#xff0c;其浓度的高低直接影响到出水水质和后续处理工艺的效果。因此&a…

无线麦克风十大品牌推荐,无线领夹麦克风哪个牌子好,领夹麦推荐

在寻找适合团队采访或户外拍摄的无线领夹麦克风时&#xff0c;是否也曾为“哪款最适合我们&#xff1f;”而犹豫不决&#xff1f;市场上的麦克风种类繁多&#xff0c;价格跨度大&#xff0c;质量参差不齐。一些产品虽然价格诱人&#xff0c;但在实际应用中却暴露出收音范围狭窄…

RS485差分信号不对称

在RS485总线通信中&#xff0c;差分信号不对称的问题时常出现&#xff0c;尤其是在总线未接从机设备的情况下。这一问题不仅影响通信质量&#xff0c;还可能导致信号传输错误。通过对实际波形、芯片手册及电路的深入分析&#xff0c;可以找出引发差分信号不对称的根本原因&…

1.ASRPRO天问--开发板介绍及第一次使用--开发板挖掘系列

1. 前言 时光不问赶路人&#xff0c;一切尽在不言中&#xff0c;大家好&#xff0c;我是繁花&#xff0c;oh&#xff0c;不对&#xff0c;是繁华的地方不一定留下你的脚印。开学季的到来&#xff0c;也让我这个老人是思绪万千&#xff0c;不要问为啥是老人&#xff0c;因为中人…

关于Vscode的vscode-cpptools中的icph占用c盘内存过大问题

问题 : 解决 : 设置->扩展->c/c->Intelli Sense Cache Path ,修改默认路径到其他盘 ;

基于.NET6的WPF基础总结(上)

目录 一.常用属性介绍 二、 程序退出方式 三、布局样式 3.1 Panel的附加属性ZIndex 3.2 Grid(网格)布局 3.3 UniformGrid&#xff08;均分布局&#xff09; 3.4 StackPanel&#xff08;堆积面板&#xff09; 3.5 WrapPanel&#xff08;换行面板&#xff09; 3.6 Doc…

大势智慧携“实景三维+AI”信创产品体系亮相2024中国地理信息产业大会

8月28-29日&#xff0c;由云南省人民政府指导&#xff0c;中国地理信息产业协会、昆明市人民政府共同主办的2024中国地理信息产业大会在昆明滇池国际会展中心盛大召开。第二届中国地理信息高新技术成果展同期举办。 本次大会以“聚焦新质生产力&#xff0c;激发产业新活力”为主…