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

7.串口通信uart编写思路及自定义协议

前言:

        串口是很重要的,有许多模块通信接口就是串口,例如gps模块,蓝牙模块,wifi模块还有一些精度比较高的陀螺仪模块等等,所以学会了串口之后,这些听起来很牛批的模块都能够用起来了。此外,单片机的之间的通信,也大多用串口,如距离比较长的RS485,RS232,光纤通信等等有线通信,也只是电平转换芯片不一样,但是代码层面完成是一样的,作为单片机开发串口是很必要熟练的。在学习的第二阶段,尽量还是照着手册来编写代码,或者说,根据自己的思路来嫖代码,而不是像初次学习一样代码、思路都嫖别人的。这样才能最大限度的检验自己的能力,当然,做项目怎样都成,怎么方便怎么来。

思路:

       下面就来记录记录我个人的编码思路,首先由下图可以看到,串口的模式还是挺多的:

        这样相应的寄存器也就必然很多,所以从一开始就需要明确我们需要的是哪种模式,然后就只关注这个模式,与之无关的寄存器都可以忽略,如此编码就简单清晰了。以最常用的异步模式为例:

        手册没有讲初始化流程,所以只能按照经验来写代码了,回忆串口无非就是:串口时钟使能,配置数据位,停止位等,配置波特率,使能串口,从寄存器读出数据/向寄存器写入数据。一般为了方便数据处理,还加一个接收中断。但是~串口不只是串口,还涉及GPIO初始化,GPIO复用配置。

1.初始化GPIO相关配置

三部曲:时钟,IO,复用啥

        GPIOx->AFR这个寄存器就是将某个GPIO管脚复用成指定功能的。下面AF虽多,但是要根据数据手册引脚说明来选,芯片没有设计的当然选了也没用。我没有在手册找到AF对应的是什么,不过正点原子的代码有写,也不知哪里找的。

//AF0~15设置情况(这里仅是列出常用的,详细的请见407数据手册,56页Table 7):
//AF0:MCO/SWD/SWCLK/RTC   AF1:TIM1/TIM2;            AF2:TIM3~5;               AF3:TIM8~11
//AF4:I2C1~I2C3;          AF5:SPI1/SPI2;            AF6:SPI3;                 AF7:USART1~3;
//AF8:USART4~6;           AF9;CAN1/CAN2/TIM12~14    AF10:USB_OTG/USB_HS       AF11:ETH
//AF12:FSMC/SDIO/OTG/HS   AF13:DCIM                 AF14:                     AF15:EVENTOUT

 编码如下:

1.时钟
RCC->AHB1ENR|=1<<0;   	//GPIOA时钟附属于AHB1
2.IO  
GPIO_Set(GPIOA,PIN9|PIN10,GPIO_MODE_AF,0,0,0);//PA9,PA10,都配置为复用模式,其他电气属性如上下拉之类的可以根据需要配置,不配置也行的。
3.复用啥,直接用正点原子的函数,里面其实就是对GPIOx->AFRH和GPIOx->AFRL这两个寄存器进行编写,不过正点原子这个封装的挺好的,一目了然
GPIO_AF_Set(GPIOA,9,7);	//PA9,AF7
GPIO_AF_Set(GPIOA,10,7);//PA10,AF7  	   

2.串口相关初始化

目的是:串口时钟使能,配置数据位,停止位,接收中断,使能串口等,配置波特率,从寄存器读出数据/向寄存器写入数据。

由手册第66页可知,USART1时钟隶属于APB2

1.使能串口1时钟    

 RCC->APB2ENR|=1<<4;   

2.配置波特率:

根据公式算,然后填到对应的位里面去

	float temp;u16 mantissa;    //整数部分u16 fraction;	 //小数部分temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV@OVER8=0mantissa=temp;				 //得到整数部分fraction=(temp-mantissa)*16; //得到小数部分@OVER8=0 mantissa<<=4;mantissa+=fraction; USART1->BRR=mantissa; 	//波特率设置	 

这个是正点原子那嫖的,适用于多种时钟,多种波特率的情况,挺好用的。

3.配置数据位,停止位,中断等

USART_CR1检索:只看和异步通信有关的位,其他的不管

bit[2]:        接收使能

bit[3]:        发送使能

bit[5]:        接收中断使能

bit[10]:       关/开奇偶检验

bit[12]:       置零:1 起始位,8 数据位,n 停止位

bit[13]:        串口关闭/使能,后面记得给这个串口中断分组以及设置优先级

bit[15]:        0:16倍过采样率,1:8倍过采样率,这个是和波特率计算有关的,设为0

其他就无所谓了,好像这个寄存器就完全够配置我们所需了

USART1->CR1 = 0<<15 | 1<<13 | 0<<12 | 0<<10 | 1<<5 | 1<<3 | 1<<2 ;
//中断分组及优先级,中断后面有时间再讲(其核心思想就是分组,中断线,设优先级三部曲)
MY_NVIC_Init(3,3,USART1_IRQn,2);//组2,最低优先级 

4.完整的串口初始化代码如下:

void uart_init(u32 pclk2,u32 bound)
{ float temp;u16 mantissa;u16 fraction;	   //1.GPIO初始化相关RCC->AHB1ENR|=1<<0;   	//使能PORTA口时钟  GPIO_Set(GPIOA,PIN9|PIN10,GPIO_MODE_AF,0,0,0);//PA9,PA10,复用功能GPIO_AF_Set(GPIOA,9,7);	//PA9,AF7GPIO_AF_Set(GPIOA,10,7);//PA10,AF7  //2.使能串口1时钟 RCC->APB2ENR|=1<<4;  	//3.波特率设置temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV@OVER8=0mantissa=temp;				 //得到整数部分fraction=(temp-mantissa)*16; //得到小数部分@OVER8=0 mantissa<<=4;mantissa+=fraction; USART1->BRR=mantissa; 	//波特率设置	 //4.配置数据位,停止位,中断等USART1->CR1 = 0<<15 | 1<<13 | 0<<12 | 0<<10 | 1<<5 | 1<<3 | 1<<2 ;//中断分组及优先级,中断后面有时间再讲(其核心思想就是分组,中断线,设优先级三部曲)MY_NVIC_Init(3,3,USART1_IRQn,2);//组2,最低优先级 
}

运行效果:

其他代码不改动,换上自己思路写的代码运行ok:

3.中断服务函数

a.它最原始的模样:
void USART1_IRQHandler(void)
{u8 res;	if(USART1->SR&(1<<5))//接收到数据的标志置1---->>有数据{	 res=USART1->DR; //取出接收到的数据---->>1B} } 

如果要发送数据,可以编写如下:

u8 res;
USART1->DR = res;//要发送的数据  1B
while((USART1->SR&0X40)==0);//等待发送结束
b.正点原子给的:
u8 USART_RX_BUF[USART_REC_LEN];     //里面存着接收到的数据u16 USART_RX_STA=0;                 //是否有数据标志+接收到的字节数//下面这个不用改它,原封不动放代码里就能用
void USART1_IRQHandler(void)
{u8 res;	if(USART1->SR&(1<<5))//接收到数据{	 res=USART1->DR; if((USART_RX_STA&0x8000)==0)//接收未完成{if(USART_RX_STA&0x4000)//接收到了0x0d{if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始else USART_RX_STA|=0x8000;	//接收完成了 }else //还没收到0X0D{	if(res==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=res;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  }		 }}  		 									     } } 

其中:

判断有无数据接收:

		if(USART_RX_STA&0x8000){。。。。。。}

得知数据共有多少B:

int len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度

数据存放的数组:

USART_RX_BUF[]

相对来说以及挺方便的了~下面还有一个比赛常用的,自定义的协议

c.自定义协议:
首先在usart.c中加入变量:
/*更改变量 BEGIN-- */
uint8_t uart1_rxbuff;//引入该.h可使用
uint8_t uart2_rxbuff;//引入该.h可使用
uint8_t uart3_rxbuff;//引入该.h可使用uint8_t sendBuf[1]; 
u8 uart1_sdbuffer[11]={0x2c,0x12,0x11,0x22,0x33,0x5b,0,0,0,0};//从索引2开始赋值
/*更改变量 END-- */
在usart.h中导出方便别的文件使用:
extern uint8_t uart1_rxbuff;
extern uint8_t uart2_rxbuff;
extern uint8_t uart3_rxbuff;extern uint8_t uart1_sdbuffer[11];
下面是协议解析函数,自定义的协议是:

协议头0x2c,0x12

协议尾0x5b,想要让协议数据位变多,只需要修改变量RxBuffer1[]的定义即可

//解析接收的数据 最多11哥,两个帧头,一个帧尾,其他是数据位
void Portocol_Receive_Data(uint8_t com_data)
{uint8_t i;static uint8_t RxCounter1=0;//计数static uint8_t RxBuffer1[11]={0};static uint8_t RxState = 0;	static uint8_t RxFlag1 = 0;u8 pi=0;//printf("%x\t",com_data);//打印调试if(RxState==0&&com_data==0x2C)  //0x2c帧头 RxCounter1==1{RxState=1;RxBuffer1[RxCounter1++]=com_data;  }else if(RxState==1&&com_data==0x12)  //0x12帧头 RxCounter1==2{RxState=2;RxBuffer1[RxCounter1++]=com_data;}else if(RxState==2)//开始接收数据位{                     RxBuffer1[RxCounter1++]=com_data;if(RxCounter1>=10||com_data == 0x5B){//RxCounter1-1是帧尾if(RxBuffer1[RxCounter1-1] == 0x5B)//接收到贞结尾了{/* USER CODE BEGIN 2 */
//                    for(i = 0; i <= 10; i++)
//                    {
//                        printf("%x\t",RxBuffer1[i]);
//                    }
//                    printf("\r\n");USART1_Portocol_Send_Data();
//                    printf("\r\n");/* USER CODE END 2 */RxFlag1 = 0;RxCounter1 = 0;RxState = 0;}else   //接收错误{RxState = 0;RxCounter1=0;for(i=0;i<11;i++){RxBuffer1[i]=0x00;      //将存放数据数组清零}}}}else   //接收异常{RxState = 0;RxCounter1=0;for(i=0;i<10;i++){RxBuffer1[i]=0x00;      //将存放数据数组清零}}
}
中断服务函数是这样滴:
void USART1_IRQHandler(void)
{if(USART1->SR&(1<<5))//接收到数据{	uart1_rxbuff = USART1->DR;Portocol_Receive_Data(uart1_rxbuff);}
}
另外还有一个协议配套的发送函数:

要修改发送的内容只需修改uart1_sdbuffer数组的内容即可:

//串口X发送函数
void USART1_Portocol_Send_Data(void)
{u8 i;for(i = 0; i <= 10; i++){USART1->DR=uart1_sdbuffer[i];//要发送的数据  1Bwhile((USART1->SR&0X40)==0);//等待发送结束}
}
效果如下:

在协议代码中,下面这部分就是给你自由发挥的,进到这段代码里说明成功接收到了按协议格式发来的信息;

4.拓展到其他串口:

复用到其他的串口也很简单,仿照把发送呀接收呀里面的寄存器改一改就行了

        比赛常用的还是自定义协议的串口,比如双车用蓝牙通讯呀,或者stm32和openmv通讯,几乎都要自己写一个协议去收发数据,这样才会可靠。

        完~

相关文章:

7.串口通信uart编写思路及自定义协议

前言&#xff1a; 串口是很重要的&#xff0c;有许多模块通信接口就是串口&#xff0c;例如gps模块&#xff0c;蓝牙模块&#xff0c;wifi模块还有一些精度比较高的陀螺仪模块等等&#xff0c;所以学会了串口之后&#xff0c;这些听起来很牛批的模块都能够用起来了。此外&#…...

【物联网】光影之谜:RGB-LED传感器引领科技变革之路

​​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《物联网实战 | 数字奇迹记》⏰翰墨致赠&#xff1a;狂风挟雷霆舞苍穹&#xff0c;剑气横扫万里空。英雄豪情铸不朽&#xff0c;激荡壮志燃热风。 ​ 目录 ⛳️1. 初识物联网 ⛳…...

promise的使用和实例方法

前言 异步,是任何编程都无法回避的话题。在promise出现之前,js中也有处理异步的方案,不过还没有专门的api能去处理链式的异步操作。所以,当大量的异步任务逐个执行,就变成了传说中的回调地狱。 function asyncFn(fn1, fn2, fn3) {setTimeout(() > {//处理第一个异步任务fn1…...

Visual Studio2022配置ReSharper C++ 常用设置

如需安装免费的可以在下面留言&#xff0c;看到即回复 文章目录 Visual Studio2022配置ReSharper C 常用设置配置Visual Studio2022&#xff0c;使其能够按回车进行补全配置ReSharper C 设置自动弹出配置ReSharper C 的快捷键ReSharper C 去掉注释拼写使用中文注释 如何关闭新版…...

论文解读类的公众号/知乎专栏,交给agent去做吧...

作者 | 二狗 大家好我是二狗&#xff0c;是夕小瑶科技说编辑部的一名作者。 我平时主要负责写AI资讯报道的文章。 大家知道&#xff0c;前阵子二狗我在「赛博马良」平台弄了个AI员工后每次都非常顺利地在第一时间精准抓到爆火的选题。 比如当时Sam Altman被开除的事件&…...

【学习笔记】Java函数式编程03 Stream流-终结操作

书接上回 3.3.3 终结操作 3.3.3.1 forEach 对集合的每一个元素进行处理 接触很多了不赘述 3.3.3.2 count 用来获取当前流中的元素的个数 比如&#xff0c;打印出所有作家的作品的总数 System.out.println(authors.stream().flatMap(author -> author.getBooks().stre…...

2024 Android保活总结

本文介绍的方案都是无需用户主动开启权限的。如果需要用户主动开启权限或者加白名单之类的话保活的意义就不大了&#xff0c;毕竟用户不大可能主动原因让app一直在后台运行 常规的方案 OnePixelActivity 1&#xff0c;监听SCREEN_OFF启动一个像素的Activity&#xff0c;灭屏…...

迅为RK3568开发板Ubuntu上使用串口调试

我们也可以在虚拟机的 Ubuntu 上使用调试串口。首先要参考 11 手册安装好 Ubuntu20.04。在 Ubuntu上可以安装多种串口工具&#xff0c;比如 minicom&#xff0c;picocom&#xff0c;kermit。本章节我们来介绍 minicom 的安装和使用。 输入以下命令安装 minicomapt-get install…...

【http】HTTP/1.0、HTTP/1.1和HTTP/2.0

✨ 专栏介绍 在当今互联网时代&#xff0c;计算机网络已经成为了人们生活和工作中不可或缺的一部分。而要实现计算机之间的通信和数据传输&#xff0c;就需要依靠各种网络协议来进行规范和约束。无论是浏览网页、发送电子邮件还是进行在线交流&#xff0c;都离不开各种各样的网…...

automkcert使用教程

我们在开发的时候往往需要https开启一些浏览器功能,比如摄像头、wss等&#xff0c;自己的云服务器申请证书一个是麻烦&#xff0c;一个是价格贵&#xff0c;这种情况下可以用自签名证书。但自签名证书下有不通用的特点&#xff0c;其他机器无法快速信任自己的网站&#xff0c;因…...

tekton 发布 kubernetes 应用

tekton 发布 kubernetes 应用 基于Kubernetes 服务部署 Tekton Pipeline 实例&#xff0c;部署完成后使用tekton来完成源码拉取、应用打包、镜像推送和应用部署。 本文实现一个 golang-helloworld 项目 CI/CD 的完整流程&#xff0c;具体包括以下步骤&#xff1a; 从 gitee…...

unity脚本API中OnCollisionEnter()、OnTriggerEnter()二者的区别

Unity中的OnCollisionEnter和OnTriggerEnter两个函数在日常的开发中很常见但也容易混淆&#xff0c;下面说一说两者的区别。 碰撞器&#xff08;Collider&#xff09;与触发器&#xff08;Trigger&#xff09;的概念 碰撞器&#xff08;Collider&#xff09;和触发器&#xff…...

2023年12月【考试战报】|ORACLE OCP 19C考试通过

2023年10月【考试战报】|ORACLE OCP 19C考试通过-CSDN博客文章浏览阅读122次。自OCP认证进入中国以来&#xff0c;越来越被大多数DBA所认可&#xff0c;也越来越被企业所重视&#xff0c;90%以上DBA深造&#xff0c;都会选择OCP认证。随着OCP认证在全国范围内的普及&#xff0c…...

鸿蒙操作系统:从手机到物联网,打造全场景智能体验

随着科技的不断发展&#xff0c;人们对于操作系统的需求也在不断升级。鸿蒙操作系统&#xff0c;作为华为推出的新一代智能终端操作系统&#xff0c;凭借其强大的分布式能力、流畅的用户体验以及丰富的应用生态&#xff0c;正逐渐成为人们关注的焦点。 一、鸿蒙操作系统概述 …...

[Ray Tracing: The Next Week] 笔记

前言 本篇博客参照自《Ray Tracing: The Next Week》教程&#xff0c;地址为&#xff1a;https://raytracing.github.io/books/RayTracingTheNextWeek.html 该教程在ray tracing in one weekend的基础上&#xff0c;增加了运动模糊、BVH树、Texture映射、柏林噪声、光照、体积…...

企业级实战项目:基于 pycaret 自动化预测公司是否破产

本文系数据挖掘实战系列文章&#xff0c;我跟大家分享一个数据挖掘实战&#xff0c;与以往的数据实战不同的是&#xff0c;用自动机器学习方法完成模型构建与调优部分工作&#xff0c;深入理解由此带来的便利与效果。 1. Introduction 本文是一篇数据挖掘实战案例&#xff0c;…...

dl转置卷积

转置卷积 转置卷积&#xff0c;顾名思义&#xff0c;通过名字我们应该就能看出来&#xff0c;其作用和卷积相反&#xff0c;它可以使得图像的像素增多 上图的意思是&#xff0c;输入是22的图像&#xff0c;卷积核为22的矩阵&#xff0c;然后变换成3*3的矩阵 代码如下 import…...

详解结构体(包含结构体内存对齐,柔性数组,位段)【尊嘟很详细】

​ 结构体 结构体是一些值的集合&#xff0c;这些值称为成员变量&#xff0c;结构的成员可以是标量、数组、指针,甚至是其他结构体。 成员名可以与程序中其它变量同名&#xff0c;互不干扰。 结构体的定义 &#xff08;struct结构名{}&#xff09; struct books {int a;c…...

我的NPI项目之Android系统升级 - 同平台多产品的OTA

因为公司业务中涉及的面比较广泛&#xff0c;虽然都是提供移动终端PDA&#xff0c;但是使用的场景很多时候是不同的。例如&#xff0c;有提供给大型物流仓储的设备&#xff0c;对这样的设备必需具备扫码功能&#xff0c;键盘&#xff08;戴手套操作&#xff09;&#xff0c;耐用…...

pnpm包管理器

官网 优点 快速 pnpm 比 npm 快了近 2 倍高效 node_modules 中的所有文件均克隆或硬链接自单一存储位置支持单体仓库 pnpm 内置了对单个源码仓库中包含多个软件包的支持权限严格 pnpm 创建的 node_modules 默认并非扁平结构&#xff0c;因此代码无法对任意软件包进行访问 安…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用

前言&#xff1a;我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM&#xff08;Java Virtual Machine&#xff09;让"一次编写&#xff0c;到处运行"成为可能。这个软件层面的虚拟化让我着迷&#xff0c;但直到后来接触VMware和Doc…...