物联网实战--入门篇之(四)嵌入式-UART驱动

        

目录

一、串口简介

二、串口驱动设计

三、串口发送

四、串口接收处理

五、PM2.5数据接收处理

六、printf重定义

七、总结


一、串口简介

        串口在单片机的开发中属于非常常用的外设,最基本的都会预留一个调试串口用来输出调试信息,串口时序这里就不谈了,主要来看看STM32中串口是怎么运行的。

void UART1_Init(void) 
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;// GPIO_Mode_AF_ODGPIO_Init(GPIOA, &GPIO_InitStructure);    //TXGPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOA, &GPIO_InitStructure);    //RX	USART_InitStructure.USART_BaudRate = BAUD_UART1;//波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//数据长度USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位USART_InitStructure.USART_Parity = USART_Parity_No;//奇偶校验USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收和发模式USART_Init(USART1, &USART_InitStructure); //初始化串口NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //使能串口中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //主优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道NVIC_Init(&NVIC_InitStructure); //初始化NVICUSART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//接收中断USART_Cmd(USART1, ENABLE);                    //使能串口	g_sUART1.USARTx=USART1;g_sUART1.PortNum=1;g_sUART1.pBuff=g_u8UART1_Buff;//把数据指针指向接收缓冲区g_sUART1.total_len=UART1_LEN;
}

        先从初始化开始,基本上就是打开对应的时钟,初始化引脚,配置串口参数,配置接收中断,最后使能串口。

/*		
================================================================================
描述 :串口中断函数
输入 : 
输出 : 
================================================================================
*/
void USART1_IRQHandler(void)
{if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//获取接收状态{g_sUART1.pBuff[g_sUART1.iRecv]=USART_ReceiveData(USART1);//缓存接收字节		USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清理中断位USART_ClearFlag(USART1, USART_FLAG_RXNE);//清理标志位g_sUART1.iRecv++;//接收长度加1if(g_sUART1.iRecv>=UART1_LEN)g_sUART1.iRecv=0;}else if (USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)//接收错误{USART_ReceiveData(USART1);
//		USART_ClearITPendingBit(USART1, USART_IT_ORE);USART_ClearFlag(USART1, USART_FLAG_ORE);}	
}

        串口是接收一个字节产生一个中断,不要再中断函数里面有过多的操作,就是连续缓存数据就好,其他的操作在外部程序中去执行;另外,如果串口数据接收错误或者溢出经常会发生ORE错误,导致一直进中断,程序直接卡死,这里就要处理下这个错误,避免卡死。

二、串口驱动设计

        我这里要重点说明的是设计思想,根据下面的头文件可知,首先定义一个结构体,用来存放串口的配置信息;结合附图,在初始化阶段把串口地址、串口号、缓冲区指针和长度都赋值了;最后将定义的三个串口结构体用extern 暴露给外部文件使用。

#ifndef __DRV_UART_H__
#define __DRV_UART_H__#include "drv_common.h"typedef struct
{USART_TypeDef* USARTx;u8 PortNum;//串口号u8 *pBuff;//数据指针u16 iRecv;//已接收长度u16 iDone;//已处理长度u16 total_len;//总长度
}UART_Struct;extern UART_Struct g_sUART1;
extern UART_Struct g_sUART2;
extern UART_Struct g_sUART3;void UART_Init(void);
void UART1_Init(void); 
void UART2_Init(void); 
void UART3_Init(void);void UART_Send(u8 PortNum, u8 *buf, u16 len);
void UART_Clear(UART_Struct *pUART);void UART1_Send(u8 *buf, u16 len);
void UART2_Send(u8 *buf, u16 len);
void UART3_Send(u8 *buf, u16 len);#endif

三、串口发送

        串口发送较为简单,注意点就是while状态检测,一个字节发送完成才能发送下一个字节;还有就是最后的延时2ms,这个主要是在实际测试中发现有时候最后一个字节发送不完整,特别是RS485发送的时候,加个延时就能解决了。

/*		
================================================================================
描述 :串口1发送
输入 : 
输出 : 
================================================================================
*/
void UART1_Send(u8 *buf, u16 len)
{u16 i;for(i=0;i<len;i++){while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);	//发送完成USART_SendData(USART1,buf[i]);}while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);	//发送完成		delay_ms(2); 
}
四、串口接收处理

        串口在接收中断函数里不断缓存数据,在应用层只需要引用相应数据指针即可,这里以调试串口指令处理为例,具体代码如下。

        首先引用串口1指针,然后在while循环里不断检测是否有收到数据,如果iRecv>0,那么说明有数据,但是此时不一定已经接收完成了,所以定义了recv_len缓存当前的数据长度,经过5ms延时后,如果recv_len和iRecv相等,那么就说明接收完整了,可以做进一步的数据处理。

        接下来是数据处理,根据个人应用具体修改,这里我是复位、取消订阅和PWM设置指令,在开发过程中做调试使用,项目完成后可删除;具体就是使用strstr()函数去检索是否包含指定字符串,然后根据指令去执行对应的动作。

        最后就是要用UART_Clear(pUART);清理下缓冲区数据。

五、PM2.5数据接收处理

        以下是PM2.5传感器的说明书,波特率是9600,传感器每隔1秒主动输出数据,其数据流的第一字节固定是0xA5,中间两字节是浓度值,最后一个字节是校验码。先思考下这种数据流应该如何解析?

        因为我们对PM2.5的实时性要求不会很高,三四秒更新一次即可,也就是三四秒处理一次串口的缓冲数据即可,代码如下所示。判断下接收长度是否大于等于4个字节,是的话进入校验环节,校验通过后再根据说明书组合浓度值,这里要注意的是,原始浓度值只是粉尘浓度,并不是PM2.5,根据XM净化器的数值大概标定下,乘以系数0.04;最后就是清理下缓冲区即可。

        主程序或任务线程调用时就四秒调用一次,如下图所示,就是在每次上报时更新下数据即可。

六、printf重定义

        printf是一个很有用的格式化输出函数,在PC端编写C语言的时候经常会用到,它会将数据打印到控制台上。那么,在单片机上,我们要如何把它的内容输出到指定的串口呢?这里就涉及到了重定义函数了,具体如下所示:

        其中UART_DEBUG在user_opt.h中定义,这样可以决定是否需要输出、用哪个串口输出。

七、总结

        至此,串口驱动也就差不多了,串口作为常用外设,几乎在每个项目中都会用到,这里通过驱动函数的形式保证了代码的重复利用的能力,再利用宏定义的方式对具体参数进行自定义配置,从而保证了通用型和灵活性。

本项目的交流QQ群:701889554

   写于2024-3-30

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

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

相关文章

既有理论深度又有技术细节——深度学习计算机视觉

推荐序 我曾经试图找到一本既有理论深度、知识广度&#xff0c;又有技术细节、数学原理的关于深度学习的书籍&#xff0c;供自己学习&#xff0c;也推荐给我的学生学习。虽浏览文献无数&#xff0c;但一直没有心仪的目标。两周前&#xff0c;刘升容女士将她的译作《深度学习计…

java中的单例模式

一、描述 单例模式就是程序中一个类只能有一个对象实例 举个例子: //引出单例模式&#xff0c;一个类中只能由一个对象实例 public class Singleton1 {private static Singleton1 instance new Singleton1();//通过这个方法来获取实例public static Singleton1 getInstance…

Verilog语法回顾--门级和开关级模型

目录 门和开关的声明 门和开关类型 支持驱动强度的门 延迟 实例数组 and&#xff0c;nand&#xff0c;nor&#xff0c;or&#xff0c;xor&#xff0c;xnor buf&#xff0c;not bufif1&#xff0c;bufif0&#xff0c;notif1&#xff0c;notif0 MOS switches Bidirecti…

TSINGSEE青犀智慧工厂视频汇聚与安全风险智能识别和预警方案

在智慧工厂的建设中&#xff0c;智能视频监控方案扮演着至关重要的角色。它不仅能够实现全方位、无死角的监控&#xff0c;还能够通过人工智能技术&#xff0c;实现智能识别、预警和分析&#xff0c;为工厂的安全生产和高效运营提供有力保障。 TSINGSEE青犀智慧工厂智能视频监…

公司官网怎么才会被百度收录

在互联网时代&#xff0c;公司官网是企业展示自身形象、产品与服务的重要窗口。然而&#xff0c;即使拥有精美的官网&#xff0c;如果不被搜索引擎收录&#xff0c;就无法被用户发现。本文将介绍公司官网如何被百度收录的一些方法和步骤。 1. 创建和提交网站地图 创建网站地图…

C语言例3-5:阅读下列程序,写出程序运行的结果。

代码如下&#xff1a; #include <stdio.h> int main(void) {int i1,s3;do{si;if(s%70) continue;else i;}while(s<15);printf("%d",i);return 0; } 结果如下&#xff1a; 分析&#xff1a; s314437741111617i3468

四、e2studio VS STM32CubeIDE之STM32CubeIDE线程安全解决方案

目录 一、概述/目的 二、原因和办法 三、线程安全问题的描述 四、STM32解决方案 4.1 通用策略 4.2 RTOS策略 4.3 策略的讲解 4.3.1 裸机应用(策略2、3) 4.3.2 RTOS应用(策略4、5) 五、关键源码 四、e2studio VS STM32CubeIDE之STM32CubeIDE线程安全解决方案 一、概述…

Spring Boot简介及案例

文章目录 Spring Boot简介以下是一个简单的 Spring Boot Web 应用实例**步骤 1&#xff1a;创建 Spring Boot 项目****步骤 2&#xff1a;编写 RESTful 控制器****步骤 3&#xff1a;配置主类****步骤 4&#xff1a;运行并测试应用** Spring Boot简介 Spring Boot 是一个用于简…

怎么让ChatGPT批量写作原创文章

随着人工智能技术的不断发展&#xff0c;自然语言处理模型在文本生成领域的应用也日益广泛。ChatGPT作为其中的佼佼者之一&#xff0c;凭借其强大的文本生成能力和智能对话特性&#xff0c;为用户提供了一种高效、便捷的批量产出内容的解决方案。以下将就ChatGPT批量写作内容进…

【AI】命令行调用大模型

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 【AI】命令行调用大模型引入正文初始化项目撰写脚本全局安装 成果展示 【AI】命令…

Ubuntu20.04LTS+uhd3.15+gnuradio3.8.1源码编译及安装

文章目录 前言一、卸载本地 gnuradio二、安装 UHD 驱动三、编译及安装 gnuradio四、验证 前言 本地 Ubuntu 环境的 gnuradio 是按照官方指导使用 ppa 的方式安装 uhd 和 gnuradio 的&#xff0c;也是最方便的方法&#xff0c;但是存在着一个问题&#xff0c;就是我无法修改底层…

亚信安全联合人保财险推出数字安全保障险方案,双重保障企业数字化转型

数字化发展&#xff0c;新兴技术的应用与落地带来网络攻击的进一步演进升级&#xff0c;同时全球产业链供应链融合协同的不断加深&#xff0c;更让网络威胁的影响范围与危害程度不断加剧。 企业单纯依靠自身安全能力建设&#xff0c;能否跟上网络威胁的进化速度&#xff1f;能否…

Day49:WEB攻防-文件上传存储安全OSS对象分站解析安全解码还原目录执行

目录 文件-解析方案-目录执行权限&解码还原 目录执行权限 解码还原 文件-存储方案-分站存储&OSS对象 分站存储 OSS对象存储 知识点&#xff1a; 1、文件上传-安全解析方案-目录权限&解码还原 2、文件上传-安全存储方案-分站存储&OSS对象 文件-解析方案-目…

【深耕 Python】Data Science with Python 数据科学(2)jupyter-lab和numpy数组

关于数据科学环境的建立&#xff0c;可以参考我的博客&#xff1a;【深耕 Python】Data Science with Python 数据科学&#xff08;1&#xff09;环境搭建 Jupyter代码片段1&#xff1a;简单数组的定义和排序 import numpy as np np.array([1, 2, 3]) a np.array([9, 6, 2, …

深入解析快速排序算法

深入解析快速排序算法 一、快速排序算法简介二、快速排序算法过程三、快速排序算法示例四、快速排序算法分析1. 时间复杂度&#xff1a;2. 空间复杂度&#xff1a;3. 稳定性&#xff1a; 五、快速排序算法优化1. 优化基准元素的选择&#xff1a;2. 优化小数组的排序&#xff1a…

WIFI驱动移植实验:WIFI从路由器动态获取IP地址与联网

一. 简介 前面两篇文章&#xff0c;一篇文章实现了WIFI联网前要做的工作&#xff0c;另一篇文章配置了WIFI配置文件&#xff0c;进行了WIFI热点的连接。文章如下&#xff1a; WIFI驱动移植实验&#xff1a;WIFI 联网前的工作-CSDN博客 WIFI驱动移植实验&#xff1a;连接WIF…

工业镜头常用参数之实效F(Fno.)和像圈

Fno. 工业镜头中常用到的参数F&#xff0c;有时候用F/#&#xff0c;Fno.来表示&#xff0c;指的是镜头通光能力的参数。它可用镜头焦距及入瞳直径来表示&#xff0c;也可通过镜头数值孔径&#xff08;NA&#xff09;和光学放大倍率&#xff08;β&#xff09;来计算。有效Fno.…

【御控物联】JavaScript JSON结构转换(11):数组To数组——综合应用

文章目录 一、JSON结构转换是什么&#xff1f;二、术语解释三、案例之《JSON数组 To JSON数组》四、代码实现五、在线转换工具六、技术资料 一、JSON结构转换是什么&#xff1f; JSON结构转换指的是将一个JSON对象或JSON数组按照一定规则进行重组、筛选、映射或转换&#xff0…

VOC(客户之声)赋能智能家居:打造个性化、交互式的未来生活体验

随着科技的飞速发展&#xff0c;智能家居已成为现代家庭不可或缺的一部分。然而&#xff0c;如何让智能家居更好地满足用户需求&#xff0c;提供更贴心、更智能的服务&#xff0c;一直是行业关注的焦点。在这个背景下&#xff0c;VOC&#xff08;客户之声&#xff09;作为一种用…

Java NIO详解

一、概念 NIO, 即new io&#xff0c;也叫非阻塞io 二、NIO三个核心组件&#xff1a; Buffer数据缓冲区Channel通道Selector选择器 1、Buffer缓冲区 缓冲区本质上是一个可以存放数据的内存块&#xff08;类似数组&#xff09;&#xff0c;可以在这里进行数据写入和读取。此…