STM32学习笔记4 --- USART

目录

通信接口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);	//1RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//2//1GPIO_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);					//2USART_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;	//设置结果初值为1while (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;					//定义可变参数列表数据类型的变量argva_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中va_end(arg);					//结束变量argSerial_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线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1NVIC_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;										//置接收标志位变量为1USART_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;			//状态归0Serial_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;			//状态归0Serial_RxPacket[pRxPacket] = '\0';			//将收到的字符数据包添加一个字符串结束标志Serial_RxFlag = 1;		//接收数据包标志位置1,成功接收一个数据包}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);		//清除标志位}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java+Selenium+ChromeDriver谷歌版环境搭建

1、创建测试项目 创建一个Maven项目即可 2、添加Selenium依赖 最好使用Selenium3版本 3、下载对应版本的ChromeDriver 找到自己浏览器对应的版本 下载ChromeDriver&#xff08;114版本以后的&#xff0c;114版之前的直接到官网下载&#xff09;下载地址 将下载好的驱动…

Nacos注册中心与OpenFeign远程调用

文章目录 一、注册中心原理二、Nacos注册中心三、服务注册四、服务发现五、OpenFeign 一、注册中心原理 在微服务当中必须有两个角色 服务提供者&#xff1a;提供接口供其它微服务访问 服务消费者&#xff1a;调用其它微服务提供的接口 在大型微服务项目中&#xff0c;服务提供…

探索EasyCVR与AI技术深度融合:视频汇聚平台的新增长点

随着5G、AI、边缘计算、物联网&#xff08;IoT&#xff09;、云计算等技术的快速发展&#xff0c;万物互联已经从概念逐渐转变为现实&#xff0c;AIoT&#xff08;物联网人工智能&#xff09;的新时代正在加速到来。在这一背景下&#xff0c;视频技术作为信息传输和交互的重要手…

【免费分享】GIS开发面试题(流程+自我介绍+基础篇+Openlayermapbox)

本篇文章针对GIS应届生就业方向及面试困惑问题进行了收集整理&#xff0c;并列出了关于GIS开发面试中常见的问题&#xff08;含答案&#xff09;。 “ 包括以下内容 前言 简介 面试之前 面试流程 自我介绍-AI 基础篇 1、GIS八股文基础篇 2、Openlayers图形绘制 3、倾…

注解实现json序列化的时候自动进行数据脱敏

最近在进行开发的时候遇到一个问题&#xff0c;需要对用户信息进行脱敏处理&#xff0c;原有的方式是写一个util类&#xff0c;在需要脱敏的字段查出数据后&#xff0c;显示掉用方法处理后再set回去&#xff0c;觉得这种方式能实现功能&#xff0c;但是不是特别优雅&#xff0c…

PostgreSQL + PostGIS:空间数据存储及管理解决方案

在数据库领域&#xff0c;PostgreSQL 已成为最强大、最通用的选项之一。它管理大量数据的能力、对 SQL 标准的遵守以及可扩展的架构使其受到学术界和工业界的喜爱。然而&#xff0c;真正让 PostgreSQL 脱颖而出的原因之一是它与PostGIS的集成&#xff0c;这是一个允许您有效处理…

HTML 基础,尚优选网站设计开发(二)

最近在恶补HTML相关知识点&#xff0c;本人是后端程序员&#xff0c;看到周围很多人都被裁员了&#xff0c;突然想尽早转变成全栈程序员变成独立开发者&#xff0c;有空余接接私单、商单的 尚优选网站设计开发&#xff0c;HTMLCSSJavaScript实际使用 尚优选网站设计开发页面分析…

单元测试 Mock不Mock?

文章目录 前言单元测试没必要?Mock不Mock?什么是Mock?Mock的意义何在? 如何Mock&#xff1f;应该Mock什么&#xff1f;Mock 编写示例 总结 前言 前段时间&#xff0c;我们团队就单元测试是否采用 Mock 进行了一番交流&#xff0c;各有各的说法。本文就单元测试 Mock不Mock…

流媒体技术革新,EasyCVR视频汇聚平台赋能视频监控全面升级

随着科技的飞速发展&#xff0c;流媒体技术和视频监控正经历着前所未有的变革与融合。本文将从流媒体技术的新兴趋势出发&#xff0c;探讨其与视频监控领域的深度结合&#xff0c;以及这一融合所带来的创新与发展。 一、流媒体技术的新兴趋势 1、5G网络的广泛应用 5G网络以其…

Leetcode3258. 统计满足 K 约束的子字符串数量 I

Every day a Leetcode 题目来源&#xff1a;3258. 统计满足 K 约束的子字符串数量 I 解法1&#xff1a;暴力 暴力枚举每一个子字符串&#xff0c;看是否满足 k 约束。 代码&#xff1a; /** lc appleetcode.cn id3258 langcpp** [3258] 统计满足 K 约束的子字符串数量 I*/…

[米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-36高效的VIVADO BlockDesign设计方法

软件版本&#xff1a;VIVADO2021.1 操作系统&#xff1a;WIN10 64bit 硬件平台&#xff1a;适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA 实验平台&#xff1a;米联客-MLK-H3-CZ08-7100开发板 板卡获取平台&#xff1a;https://milianke.tmall.com/ 登录“米联客”FPGA社区 http…

828华为云征文|华为云Flexus X实例docker部署MinIO对象存储系统obs

828华为云征文&#xff5c;华为云Flexus X实例docker部署MinIO对象存储系统obs 华为云最近正在举办828 B2B企业节&#xff0c;Flexus X实例的促销力度非常大&#xff0c;特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、Nginx等服务的需求&#xff0c;一定…

单例模式的总结

常规模式:有属性/构造方法/普通方法&#xff0c;也可以在类中执行主方法&#xff0c;也可以在test类中执行主方法 单例模式是什么&#xff1f; 单例模式&#xff1a;类只有1个对象&#xff1b;保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点。单例模式是在内…

【Python机器学习】词向量推理——词向量

目录 面向向量的推理 使用词向量的更多原因 如何计算Word2vec表示 skip-gram方法 什么是softmax 神经网络如何学习向量表示 用线性代数检索词向量 连续词袋方法 skip-gram和CBOW&#xff1a;什么时候用哪种方法 word2vec计算技巧 高频2-gram 高频词条降采样 负采样…

团队比赛活动如何记分?

团队比赛时如何记分&#xff1f; 在当今快节奏的社会中&#xff0c;团队合作和竞争已成为推动个人和集体发展的重要方式。无论是在学校的体育赛事、公司的团建活动&#xff0c;还是社区的娱乐竞赛中&#xff0c;团队比赛都扮演着不可或缺的角色。然而&#xff0c;组织一场成功的…

网络学习-eNSP配置路由器

#PC1网关&#xff1a;192.168.1.254 #PC3网关&#xff1a;192.168.3.254 #PC4网关&#xff1a;192.168.4.254# 注&#xff1a;路由器接口必须配置不同网段IP地址 <Huawei>system-view Enter system view, return user view with CtrlZ. #给路由器两个接口配置IP地址 [Hua…

黑马-Cloud21版-面试篇13:Sentinel源码分析

Sentinel源码分析 1.Sentinel的基本概念 Sentinel实现限流、隔离、降级、熔断等功能&#xff0c;本质要做的就是两件事情&#xff1a; 统计数据&#xff1a;统计某个资源的访问数据&#xff08;QPS、RT等信息&#xff09;规则判断&#xff1a;判断限流规则、隔离规则、降级规…

将用户证书导入到系统证书

现在大部分app已经不信任用户证书,通过传统charles安装证书抓包的方式已经不行,今天就来说一个将系统证书移动到系统目录的方法,系统证书的目录只有可读权限,有时候挂在目录会不成功 我们先下载如下模块 GitHub - ys1231/MoveCertificate: 支持Android7-14移动证书&#xff0…

WebAPI(四) BOM;延时函数;JS执行机制(同步异步);location对象;history对象;navigation对象

文章目录 BOM1. 定时器-延时函数2. JS执行机制(1)、同步与异步(2)、事件循环 3. location对象(1)、href属性获取完整的url地址(2)search属性获取地址中携带的参数(3) hash&#xff1a;获取地址中的#后边的部分(3) reload&#xff1a; 刷新当前页面&#xff0c;传入参数true表示…

2024年入职/转行网络安全,该如何规划?_网络安全职业规划

前言 前段时间&#xff0c;知名机构麦可思研究院发布了 《2022年中国本科生就业报告》&#xff0c;其中详细列出近五年的本科绿牌专业&#xff0c;其中&#xff0c;信息安全位列第一。 网络安全前景 对于网络安全的发展与就业前景&#xff0c;想必无需我多言&#xff0c;作为…