串口程序(1)-接收多个字节程序设计

数据寄存器

关键的标志位

通过该宏定义可以开启对应的串口中断,之前用该宏定义代替标准库函数USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);  //使能接收中断

HAL库程序

1.串口发送程序

HAL库串口发送一个/一组数据是很简单的,可以直接调用HAL_UART_Transmit(&g_uart1_handle,(uint8_t*)g_usart_rx_buf, len, 1000);    /* 发送一个/一组数据*/

第一个参数是对应的串口句柄地址,第二个参数是要发送的数据指针,第三个参数是发送的长度,最后一个参数是设置的超时时间。

2.串口接收程序

串口的接收一般不直接使用对应的库函数HAL_UART_Receive(&uartx_handle,Rxdata,3,1000);,而是自定义通过中断的方式接收一定数量的数据。经过测试hal库接收函数也可以接收相应数量的数据(如下图),但不知道为什么用的不普遍。

2.1 串口接收程序1

#define USART_REC_LEN               200         /* 定义最大接收字节数 200 */
#define USART_EN_RX                 1           /* 使能(1)/禁止(0)串口1接收 */
#define RXBUFFERSIZE   1                        /* 缓存大小 */
//可以看到 USART_REC_LEN 表示最大接收字节数,这里定义的是 200 个字节,后续如果
//有需求要发送更大的数据包,可以改大这个值,这里不改太大,是避免浪费太多内存。
#if USART_EN_RX /*如果使能了接收*/     //宏定义已经使能了/* 接收缓冲, 最大USART_REC_LEN个字节. */
uint8_t g_usart_rx_buf[USART_REC_LEN];/*  接收状态*  bit15,      接收完成标志*  bit14,      接收到0x0d*  bit13~0,    接收到的有效字节数目
*/
uint16_t g_usart_rx_sta = 0;uint8_t g_rx_buffer[RXBUFFERSIZE];  /* HAL库使用的串口接收缓冲 */UART_HandleTypeDef g_uart1_handle;  /* UART句柄 *//*** @brief       串口X初始化函数* @param       baudrate: 波特率, 根据自己需要设置波特率值* @note        注意: 必须设置正确的时钟源, 否则串口波特率就会设置异常.*              这里的USART的时钟源在sys_stm32_clock_init()函数中已经设置过了.* @retval      无*/
void usart_init(uint32_t baudrate)
{/*UART 初始化设置*/g_uart1_handle.Instance = USART_UX;                                       /* USART_UX */g_uart1_handle.Init.BaudRate = baudrate;                                  /* 波特率 */g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */g_uart1_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */g_uart1_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */g_uart1_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */HAL_UART_Init(&g_uart1_handle);                                           /* HAL_UART_Init()会使能UART1 *//* 该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 */HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE); 
}/*** @brief       UART底层初始化函数* @param       huart: UART句柄类型指针* @note        此函数会被HAL_UART_Init()调用*              完成时钟使能,引脚配置,中断配置* @retval      无*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{GPIO_InitTypeDef gpio_init_struct;if (huart->Instance == USART_UX)                            /* 如果是串口1,进行串口1 MSP初始化 */{USART_TX_GPIO_CLK_ENABLE();                             /* 使能串口TX脚时钟 */USART_RX_GPIO_CLK_ENABLE();                             /* 使能串口RX脚时钟 */USART_UX_CLK_ENABLE();                                  /* 使能串口时钟 */gpio_init_struct.Pin = USART_TX_GPIO_PIN;               /* 串口发送引脚号 */gpio_init_struct.Mode = GPIO_MODE_AF_PP;                /* 复用推挽输出 */gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* IO速度设置为高速 */HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct);gpio_init_struct.Pin = USART_RX_GPIO_PIN;               /* 串口RX脚 模式设置 */gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;    HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct);   /* 串口RX脚 必须设置成输入模式 */#if USART_EN_RX                //宏定义中已经使能HAL_NVIC_EnableIRQ(USART_UX_IRQn);                      /* 使能USART1中断通道 */HAL_NVIC_SetPriority(USART_UX_IRQn, 3, 3);              /* 组2,最低优先级:抢占优先级3,子优先级3 */
#endif}
}/*** @brief       串口数据接收回调函数数据处理在这里进行* @param       huart:串口句柄* @retval      无*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if (huart->Instance == USART_UX)                    /* 如果是串口1 */{if ((g_usart_rx_sta & 0x8000) == 0)             /* 接收未完成 */{if (g_usart_rx_sta & 0x4000)                /* 接收到了0x0d(即回车键) */{if (g_rx_buffer[0] != 0x0a)             /* 接收到的不是0x0a(即不是换行键) */{g_usart_rx_sta = 0;                 /* 接收错误,重新开始 */}else                                    /* 接收到的是0x0a(即换行键) */{g_usart_rx_sta |= 0x8000;           /* 接收完成了 */}}else                                        /* 还没收到0X0d(即回车键) */{if (g_rx_buffer[0] == 0x0d)g_usart_rx_sta |= 0x4000;else{g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = g_rx_buffer[0];g_usart_rx_sta++;if (g_usart_rx_sta > (USART_REC_LEN - 1)){g_usart_rx_sta = 0;             /* 接收数据错误,重新开始接收 */}}}}HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);}
}/*** @brief       串口1中断服务函数* @param       无* @retval      无*/
void USART_UX_IRQHandler(void)
{
#if SYS_SUPPORT_OS                          /* 使用OS */OSIntEnter();    
#endifHAL_UART_IRQHandler(&g_uart1_handle);   /* 调用HAL库中断处理公用函数 */#if SYS_SUPPORT_OS                          /* 使用OS */OSIntExit();
#endif}#endif

注意点:

1.发送(TX)引脚模式:因为要输出高低电平,所以要设置为复用推挽输出模式。接收(RX)引脚模式:设置为复用浮空输入模式。

2.串口在初始化开启了接收中断,为什么在接收到一字节后再次开启中断(如下图)?

 答:因为此程序调用 HAL 库中断处理公用函数HAL_UART_IRQHandler(&g_uart1_handle),在该函数中调用了UART_Receive_IT(huart)函数,该函数的作用就是清除相关中断标志位并调用回调函数且失能数据寄存器非空中断,所以若用中断接收到一个数据后若不开启中断则接收数据后不能产生中断,故每次接收一个数据后都要再次调用HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE)函数。

在UART_Receive_IT(huart)函数中失能接收中断的具体程序如下:

 3.串口接收多个数据的设计。

因为我们设置了串口句柄成员变量 RxXferSize 为 1,那么每当串口 1 接收到一个字符后触 发接收完成中断,便会在中断服务函数中引导执行该回调函数。当串口接受到一个字符后,它会保存在缓存 g_rx_buffer 中,由于我们设置了缓存大小为 1,而且 RxXferSize=1,所以每次接受一个字符,会直接保存到 RxXferSize[0]中,我们直接通过读取RxXferSize[0]的值就是本次接收到的字符。这里我们设计了一个小小的接收协议:通过这个函数,配合一个数组 g_usart_rx_buf, 一个接收状态寄存器 g_usart_rx_sta(此寄存器其实就是一个全局变量,由作者自行添加。由于 它起到类似寄存器的功能,这里暂且称之为寄存器)实现对串口数据的接收管理。数组 g_usart_rx_buf 的大小由 USART_REC_LEN 定义,也就是一次接收的数据最大不能超过 USART_REC_LEN 个字节。g_usart_rx_sta 是一个接收状态寄存器其各的定义如下表所示:

设计思路如下: 当接收到从电脑发过来的数据,把接收到的数据保存在数组 g_usart_rx_buf 中,同时在接 收状态寄存器(g_usart_rx_sta)中计数接收到的有效数据个数,当收到回车(回车的表示由 2 个 字节组成:0X0D 和 0X0A)的第一个字节 0X0D 时,计数器将不再增加,等待 0X0A 的到来, 而如果 0X0A 没有来到,则认为这次接收失败,重新开始下一次接收。如果顺利接收到 0X0A, 则标记 g_usart_rx_sta 的第 15 位,这样完成一次接收,并等待该位被其他程序清除,从而开始下一次的接收,而如果迟迟没有收到 0X0D,那么在接收数据超过 USART_REC_LEN 的时候, 则会丢弃前面的数据,重新接收。

可以看到,在回调函数后面调用了 UART_Receive_IT 函数。该函数在这里的主要作用是重新开启接收完成中断。 

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

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

相关文章

MySQL和Java通用加密解密方式

加密方式使用 AES 加密,再转成 Base64。 SQL -- 加密 update your_table set your_columnto_base64(aes_encrypt(your_column, "password"));-- 解密 select aes_decrypt(from_base64(your_column) ,"password") from your_table; 使用原生 …

ruoyi若依三级菜单名title长度超出优化

项目中遇到菜单名长度超出&#xff0c;又不想更改宽度&#xff0c;可以通过截取title然后加上...和title来实现tooltip提示功能来达到优化&#xff0c; 若依的菜单名是layout/components/Sidebar/Item文件设置的下面是修改后的代码 <script> export default {name: Men…

9-tornado-Template优化方法、个人信息案例、tornado中ORM的使用(peewee的使用、peewee_async)、WTForms的使用

在很多情况下&#xff0c;前端模板中在很多页面有都重复的内容可以使用&#xff0c;比如页头、页尾、甚至中间的内容都有可能重复。这时&#xff0c;为了提高开发效率&#xff0c;我们就可以考虑在共同的部分提取出来&#xff0c; 主要方法有如下&#xff1a; 1. 模板继承 2. U…

互联网Java工程师面试题·Spring Cloud篇

目录 1、什么是 Spring Cloud&#xff1f; 2、使用 Spring Cloud 有什么优势&#xff1f; 3、服务注册和发现是什么意思&#xff1f;Spring Cloud 如何实现&#xff1f; 4、负载平衡的意义什么&#xff1f; 5、什么是 Hystrix&#xff1f;它如何实现容错&#xff1f; 6、什么是…

Docker中安装Oracle10g和oracle增删改查

Docker中安装Oracle 10g 一、Docker中安装Oracle 10安装步骤二、连接数据库登录三 oracle数据库的增删改查及联表查询的相关操作oracle数据库,创建students数据表,创建100万条数据增删改查 一、Docker中安装Oracle 10安装步骤 Docker中安装Oracle 10g 1.下载镜像 docker pull …

ES6中的Promise

Promise 是一种异步编程解决方案&#xff0c;Promise是一个容器&#xff0c;保存着将来才会执行的代码&#xff1b;从语法角度来说Promise是一个对象&#xff0c;可以用来获取异步操作的消息。异步操作&#xff0c;同步解决&#xff0c;避免了层层嵌套的回调函数&#xff0c;可…

Golang实践录:读取toml配置

本文对 toml 文件进行解析。 下载 对于toml格式文件&#xff0c;golang 有很多库可以解释 yaml 文件&#xff0c;如toml、viper。由于 viper 可解析格式较多&#xff0c;本文采用该库。 toml语法规则 toml语法规则在官方中文文档上有说明&#xff0c;这里直接使用。 TOML 是…

【毕业设计】基于雷达与深度学习的摔倒检测——短时傅里叶变换

在雷达的探测过程中,雷达信号合成器产生一个高频的连续波信号,该信号的瞬时频率随时间线性增加。这种类型的信号也被称为线性调频脉冲信号。雷达回波信号包含人体动作的特征信息,由于雷达信号是非平稳信号,需要采用相应的处理方式,例如短时傅里叶变换,小波变换。 目录 1…

在Linux系统中更换yum源为阿里云

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;数据结构&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;网络编程等领域UP&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff0…

vue v-for获取子组件$ref总是拿到最后一个元素

页面循环列表&#xff0c;把子组件放在循环里面&#xff0c;此处获取this.$refs返回的应该是个数组&#xff0c;但是不知道为什么&#xff0c;一直返回的是循环的最后一个的子组件实列&#xff0c;官网上已经说明v-for返回的就是数组&#xff0c;所以一直很困惑 代码如下&#…

C++指针作业

一、编程题&#xff08;下面所有题目均需使用指针&#xff0c;可任选 3 道 &#xff09; 利用指针统计整型数组&#xff08;10个数组元素&#xff09;中大于平均数的元素个数。 #include <iostream> using namespace std; int main() {int n[10],i,num, sum, avg,*p;fo…

DevOps搭建(一)-之swappiness安装详细步骤

1、安装swappiness yum install procps 修改配置 vim /etc/sysctl.conf 在配置文件中添加参数 vm.swappiness10 使生效 sysctl -p 如何确认swap分区是否开启 # free -mtotal used free shared buff/cache available Mem: 971 …

Ubuntu-rsyslog和systemd-journald日志服务

rsyslog日志服务 rsyslog作为传统的系统日志服务&#xff0c;把所有收集到的日志都记录到/var/log/目录下的各个日志文件中。 常见的日志文件如下&#xff1a; /var/log/messages 绝大多数的系统日志都记录到该文件 /var/log/secure 所有跟安全和认证授权等日志…

C++标准模板(STL)- 类型支持 (杂项变换,基于编译时布尔值选择一个类型或另一个,std::conditional)

类型特性 类型特性定义一个编译时基于模板的结构&#xff0c;以查询或修改类型的属性。 试图特化定义于 <type_traits> 头文件的模板导致未定义行为&#xff0c;除了 std::common_type 可依照其所描述特化。 定义于<type_traits>头文件的模板可以用不完整类型实…

【唐山海德教育】一级建造师社保需交满多少年

一建对社保缴纳年限的要求需要具体看各省份的报考通知&#xff0c;因为不同地区要求不一致。 一般来说要在报考所在地交满一年以上的社保才行&#xff0c;但也有要求缴纳满4年的&#xff0c;也有对社保没有缴纳年限要求的省份&#xff0c;这个就要考生自己咨询当地人事考试中心…

基于Java SSM框架实现弹幕视频网站系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现弹幕视频网站系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;弹幕视频网站当然也不能排除在外。弹幕视频网站是以实际运用为开发背景&…

DBSCAN聚类算法学习笔记

DBSCAN聚类算法学习笔记 一些概念名词 MinPts&#xff1a;聚类在一起的点的最小数目&#xff0c;超过这一阈值才算是一个族群 核心点&#xff1a;邻域内数据点超过MinPts的点 边界点&#xff1a;落在核心点邻域内的点称为边界点 噪声点&#xff1a;既不是核心点也不是边界点的…

CGAL的泊松曲面重构

1、介绍 该CGAL组件实现了一种曲面重建方法&#xff0c;该方法将具有定向法线的点集作为输入&#xff0c;并计算隐式函数。我们假设输入点不包含异常值和少量噪声。通过使用CGAL曲面网格生成器[4]或可能使用任何其他曲面轮廓算法提取该函数的等值面来生成输出曲面网格。 更具体…

如何在业务中体现 TCC 事务模型?

在分布式系统设计中&#xff0c;随着微服务的流行&#xff0c;通常一个业务操作被拆分为多个子任务&#xff0c;比如电商系统的下单和支付操作&#xff0c;就涉及到了创建和更新订单、扣减账户余额、扣减库存、发送物流消息等&#xff0c;那么在复杂业务开发中&#xff0c;如何…

VSCode 创建工作区,多文件夹终端切换

VSCode 创建工作区的好处有以下几点&#xff1a; 项目结构清晰&#xff1a;每个工作区都有自己的文件夹结构&#xff0c;可以更好地组织和管理项目文件。版本控制&#xff1a;VSCode 支持多种版本控制系统&#xff0c;如Git&#xff0c;可以在工作区内进行代码的版本管理。插件…