STM32HAL库 串口USART的使用

news2024/11/25 0:58:41

STM32HAL库 串口USART的使用


文章目录

  • STM32HAL库 串口USART的使用
  • 前言
  • 一、配置USART1串口通信引脚
  • 二、使用步骤
  • 三、串口中断回调函数
    • 1. 配置
    • 2. 在icode中增加usart.c和usart.h文件
    • 3. 中断处理对比
    • 4. 编写串口控制程序
  • 总结


前言

本文为串口输出打印的hal库,参考洋桃电子的入门30步总结而来。


一、配置USART1串口通信引脚

在这里插入图片描述
进入CubeIDE,配置串口通信引脚
在这里插入图片描述

二、使用步骤

打开core文件夹下src文件夹,找到syscalls.c文件将其排除编译。因为syscalls.c文件与我们即将添加的文件内容相冲突。
在这里插入图片描述
在这里插入图片描述
在core——inc中增加retarget.h文件;在在core——src中增加retarget.c文件

retarget.h文件

#ifndef INC_RETARGET_H_
#define INC_RETARGET_H_

#include "stm32f1xx_hal.h"
#include "stdio.h"//用于printf函数串口重映射
#include <sys/stat.h>

void RetargetInit(UART_HandleTypeDef  *huart);

int _isatty(int fd);
int _write(int fd, char* ptr, int len);
int _close(int fd);
int _lseek(int fd, int ptr, int dir);
int _read(int fd, char* ptr, int len);
int _fstat(int fd, struct stat* st);

#endif /* INC_RETARGET_H_ */

retarget.c文件

#include <_ansi.h>
#include <_syslist.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/times.h>
#include <limits.h>
#include <signal.h>
#include <../Inc/retarget.h>
#include <stdint.h>
#include <stdio.h>
#if !defined(OS_USE_SEMIHOSTING)
#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2

UART_HandleTypeDef *gHuart;

void RetargetInit(UART_HandleTypeDef *huart)  {
  gHuart = huart;
  /* Disable I/O buffering for STDOUT  stream, so that
   * chars are sent out as soon as they are  printed. */
  setvbuf(stdout, NULL, _IONBF, 0);
}
int _isatty(int fd) {
  if (fd >= STDIN_FILENO && fd <=  STDERR_FILENO)
    return 1;
  errno = EBADF;
  return 0;
}
int _write(int fd, char* ptr, int len) {
  HAL_StatusTypeDef hstatus;
  if (fd == STDOUT_FILENO || fd ==  STDERR_FILENO) {
    hstatus = HAL_UART_Transmit(gHuart,  (uint8_t *) ptr, len, HAL_MAX_DELAY);
    if (hstatus == HAL_OK)
      return len;
    else
      return EIO;
  }
  errno = EBADF;
  return -1;
}
int _close(int fd) {
  if (fd >= STDIN_FILENO && fd <=  STDERR_FILENO)
    return 0;
  errno = EBADF;
  return -1;
}
int _lseek(int fd, int ptr, int dir) {
  (void) fd;
  (void) ptr;
  (void) dir;
  errno = EBADF;
  return -1;
}
int _read(int fd, char* ptr, int len) {
  HAL_StatusTypeDef hstatus;
  if (fd == STDIN_FILENO) {
    hstatus = HAL_UART_Receive(gHuart,  (uint8_t *) ptr, 1, HAL_MAX_DELAY);
    if (hstatus == HAL_OK)
      return 1;
    else
      return EIO;
  }
  errno = EBADF;
  return -1;
}
int _fstat(int fd, struct stat* st) {
  if (fd >= STDIN_FILENO && fd <=  STDERR_FILENO) {
    st->st_mode = S_IFCHR;
    return 0;
  }
  errno = EBADF;
  return 0;
}

#endif //#if !defined(OS_USE_SEMIHOSTING)

在main.c文件中增加retarget.h文件,并开启printf串口打印功能。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、串口中断回调函数

1. 配置

在这里插入图片描述

2. 在icode中增加usart.c和usart.h文件

usart.c

/*
 * usart1.c
 *
 *  Created on: Oct 20, 2021
 *      Author: Administrator
 */

#include "usart.h"

uint8_t USART1_RX_BUF[USART1_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.接收usart1串口接收的全部数据
uint16_t USART1_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目。保存接收数据的数量和是否收到回车键
uint8_t USART1_NewData;//当前串口中断接收的1个字节数据的缓存,产生串口中断时,此变量存储最新收到的一个字符

uint8_t USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.
uint16_t USART2_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目
uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存
uint8_t RS485orBT;//当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式

uint8_t USART3_RX_BUF[USART3_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.
uint16_t USART3_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目
uint8_t USART3_NewData;//当前串口中断接收的1个字节数据的缓存

void  HAL_UART_RxCpltCallback(UART_HandleTypeDef  *huart)//串口中断回调函数
{
	if(huart ==&huart1)//判断中断来源(串口1:USB转串口)
    {
       printf("%c",USART1_NewData); //把收到的数据以 a符号变量 发送回电脑
       if((USART1_RX_STA&0x8000)==0){//接收未完成,1000 0000 0000 0000
           if(USART1_RX_STA&0x4000){//接收到了0x0d回车符,0100 0000 0000 0000
               if(USART1_NewData!=0x0a)USART1_RX_STA=0;//接收错误,重新开始,0x0a表示换行符
               else USART1_RX_STA|=0x8000;   //接收完成了
           }else{ //还没收到0X0D
               if(USART1_NewData==0x0d)USART1_RX_STA|=0x4000;//将 USART1_RX_STA 的第14位设置为1,表示已经接收到回车符(0x0d)
               else{
                  USART1_RX_BUF[USART1_RX_STA&0X3FFF]=USART1_NewData; //将收到的数据放入数组
                  USART1_RX_STA++;  //数据长度计数加1
                  if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收
               }
           }
       }
       HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); //再开启接收中断
    }
    if(huart ==&huart2)//判断中断来源(RS485/蓝牙模块)
    {
       if(RS485orBT){//当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式
    	   USART2_RX_BUF[0]=USART2_NewData;//将接收到的数据放入缓存数组(因只用到1个数据,所以只存放在数据[0]位置)
    	   USART2_RX_STA++;//数据接收标志位加1
       }else{
    	   printf("%c",USART2_NewData); //把收到的数据以 a符号变量 发送回电脑
       }
       HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData, 1); //再开启接收中断
    }
	if(huart ==&huart3)//判断中断来源(串口3:WIFI模块)
	{
		printf("%c",USART3_NewData); //把收到的数据以 a符号变量 发送回电脑
		HAL_UART_Receive_IT(&huart3,(uint8_t *)&USART3_NewData,1); //再开启接收中断
	}
}

usart.h

/*
 * usart1.h
 *
 *  Created on: Oct 20, 2021
 *      Author: Administrator
 */

#ifndef INC_USART_H_
#define INC_USART_H_

#include "stm32f1xx_hal.h" //HAL库文件声明
#include <string.h>//用于字符串处理的库
#include "../inc/retarget.h"//用于printf函数串口重映射

extern UART_HandleTypeDef huart1;//声明USART1的HAL库结构体
extern UART_HandleTypeDef huart2;//声明USART2的HAL库结构体
extern UART_HandleTypeDef huart3;//声明USART2的HAL库结构体

#define USART1_REC_LEN  200//定义USART1最大接收字节数
#define USART2_REC_LEN  200//定义USART1最大接收字节数
#define USART3_REC_LEN  200//定义USART1最大接收字节数

extern uint8_t  USART1_RX_BUF[USART1_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern uint16_t USART1_RX_STA;//接收状态标记
extern uint8_t USART1_NewData;//当前串口中断接收的1个字节数据的缓存

extern uint8_t  USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern uint16_t USART2_RX_STA;//接收状态标记
extern uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存
extern uint8_t RS485orBT;//当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式

extern uint8_t  USART3_RX_BUF[USART3_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern uint16_t USART3_RX_STA;//接收状态标记
extern uint8_t USART3_NewData;//当前串口中断接收的1个字节数据的缓存

void  HAL_UART_RxCpltCallback(UART_HandleTypeDef  *huart);//串口中断回调函数声明

#endif /* INC_USART_H_ */

3. 中断处理对比

串口接收有两种方法:查询法、中断法
查询法:在主循环中写一个if判断语句,循环判断串口接收标志位,因为usart1串口收到数据后,硬件会自动将接收标志位从0改为1。若标志位为1,则串口接收数据,会自动从对应的串口接收寄存器中读出数据。
中断法:开启nvic中断控制器中的usart1串口中断功能,当串口收到数据,硬件自动产生中断,单片机中止当前的程序,进入中断处理程序,读出接收寄存器中收到的数据。
在这里插入图片描述

  • 在标准库中,是在中断处理函数中完成对接收数据的处理,当产生串口接收中断,程序会中止运行主函数,自动跳转到对应的中断函数。优点:简单直接,缺点:程序停留在中断处理函数的时间太久,会耽误主函数的时间,也会让其他中断事件受阻,其他中断需要等待当前中断处理完成后才能进行下一个中断的处理。

  • 在HAL库中,为中断回调函数的方式处理中断事件。当产生中断事件,程序会先跳转到中断处理函数,此中断处理函数只标注了中断来源,然后快速退出中断,回到主函数,然后并不执行之前中止的程序,而是自动调用中断回调函数,对中断事件的处理都放在回调函数里。由于此时已经退出中断状态,回归主函数,所以其他中断不会受阻

在这里插入图片描述
中断回调函数还有一个弱函数定义的概念
这段代码是HAL库中UART接收完成回调函数的定义,当UART接收完成时,HAL库会自动调用这个函数。

__weak 关键字表示该函数是一个弱符号,允许用户在自己的代码中定义同名的函数以覆盖这个函数的默认实现。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 中的 UART_HandleTypeDef *huart 是指向 UART_HandleTypeDef 结构体的指针,它包含了与 UART 通信相关的参数和状态信息。

该回调函数在默认情况下是空函数,通过在函数体中调用 UNUSED(huart); 可以防止编译器产生未使用参数的警告。

注释中指出,如果需要使用回调函数,则应在用户文件中实现 HAL_UART_RxCpltCallback 函数。这个回调函数可以用来处理 UART 接收完成事件,例如读取接收缓冲区中的数据。
在这里插入图片描述

4. 编写串口控制程序

在这里插入图片描述
开启串口接收中断函数,这一行函数内容与usart.c文件第41行的内容一样。因为单片机上电启动时,串口接收中断默认关闭状态,所以需要先开启
在这里插入图片描述
主循环增加串口控制继电器的程序。只有判断有回车键,才会进行对串口接收数据的处理。
在这里插入图片描述
此时串口程数据已经在中断回调函数中处理过了,处理号的内容存放在usart.c文件的10、11行
在这里插入图片描述


总结

以上内容主要让大家掌握串口通信方式,前期学习,只要先会用即可。

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

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

相关文章

【LeetCode】剑指 Offer 57. 和为 s 的数字 p280 -- Java Version

1. 题目介绍&#xff08;57. 和为 s 的数字&#xff09; 面试题57&#xff1a;和为 s 的数字&#xff0c; 一共分为两小题&#xff1a; 题目一&#xff1a;和为 s 的两个数字题目二&#xff1a;和为 s 的连续正数序列 2. 题目1&#xff1a;和为s的两个数字 题目链接&#xff1…

图结构基本知识

图1. 相关概念2. 图的表示方式3. 图的遍历3.1 深度优先遍历&#xff08;DFS&#xff09;3.2 广度优先遍历&#xff08;BFS&#xff09;1. 相关概念 图G(V,E) &#xff1a;一种数据结构&#xff0c;可表示“多对多”关系&#xff0c;由顶点集V和边集E组成&#xff1b;顶点(vert…

数据库管理-第六十七期 SQL Domain 2(20230414)

数据库管理 2023-04-14第六十七期 SQL Domain 21 Domain函数示例总结第六十七期 SQL Domain 2 昨晚割接&#xff0c;搭了一套19c的ADG&#xff0c;今天睡了个懒觉&#xff0c;早上把笔记本内存扩到了64GB&#xff0c;主要是为了后面做实验。然后下午拼了个乐高&#xff0c;根据…

Excel小技巧:对比两列数据的异同、vlookup使用方法

目录 问题一&#xff1a; 在联盟对接的时候&#xff0c;团购站会推送一个返利值&#xff0c;称为“推送返利”&#xff0c;联盟后台又会计算一个返利值&#xff0c;称为“计算返利”。当团购站的推送返利与计算返利相同的时候&#xff0c;我们才认为这个团购站在返利上对接完…

JMeter全局变量在使用时第一次取到null的问题解决

1. 在执行JMeter测试时&#xff0c;登录操作只需要执行一次。这样就需要用到全局变量&#xff0c;但在前一个提取器执行了请求后&#xff0c;如果返回结果是json格式的数据&#xff0c;会在下面添加一个JSON提取器&#xff0c;并在json提取器下会添加一个后置BeanShell PostPro…

10个镜像网站工具箱供你使用,不注册ChatGPT也能免费使用ChatGPT

ChatGPT已经成为了人工智能技术中备受瞩目的一员&#xff0c;它可以为我们带来更加智能化、个性化的交互体验。对于没有ChatGPT账号或者不想注册账号的人来说&#xff0c;他们可能会错过这种神奇的体验。 而本篇文章就帮大家解决这个问题&#xff0c;不用登录ChatGPT账号&…

Spark 写 MySQL经典50题

目录 建表 & 添加数据 表结构分析图 连接数据库 题目 1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数 2、查询"01"课程比"02"课程成绩低的学生的信息及课程分数 3、查询平均成绩大于等于60分的同学的学生编号和学…

如何将GIS地图和可视化结合使用实现更好的数据呈现

GIS&#xff08;地理信息系统&#xff09;和可视化&#xff08;visualization&#xff09;是两个紧密相关的领域。GIS是一种用于管理、分析和展示地理空间数据的技术&#xff0c;而可视化则是一种用图形、图表、动画等形式展示数据的方式。GIS地图则是指基于地理信息系统技术&a…

【举一反三】只出现一次的数字

本文&#xff0c;讲位运算——异或运算。因为题干中说明要线性时间复杂度&#xff0c;所以采用位运算进行操作&#xff0c;而没有采用哈希表。 目录 1.只出现一次的数字 I 2.只出现一次的数字 II 3.只出现一次的数字 III 1.只出现一次的数字 I 136. 只出现一次的数字 - 力扣&…

浅析Dubbo核心设计

大家好&#xff0c;我是易安&#xff01; 当今互联网时代&#xff0c;随着企业业务的不断扩展和用户量的增加&#xff0c;分布式系统已成为大型企业必不可少的组成部分。而Dubbo框架作为阿里巴巴开源的高性能Java RPC框架&#xff0c;一直以来都备受关注和使用。其核心设计思想…

机器学习正以惊人的速度破解宇宙奥秘

宇航员、科学家和其他以探索并记录终极边界为己任的人们&#xff0c;才积极转向机器学习&#xff08;ML&#xff09;以协助应对自己面临的非凡挑战。从引导火箭穿越太空到研究遥远行星的表面&#xff0c;再到测量宇宙大小和计算天体的运动轨迹&#xff0c;AI在太空中拥有着众多…

300左右蓝牙耳机推荐,口碑好的平价蓝牙耳机选购指南

300元预算&#xff0c;想入手一款最值得蓝牙耳机&#xff0c;咋选&#xff1f;作为一个有4年玩机经验的爱好者&#xff0c;蓝牙耳机推荐性价比高的产品&#xff0c;今天就总结了几款目前很受欢迎&#xff0c;同时性能各方面都不错的机型&#xff0c;选对这几款&#xff0c;不用…

WIN10、WIN11 新电脑配置

WIN10、WIN11 新电脑配置WIN10、WIN11 新电脑配置开启管理员模式启用power shell脚本WIN10、WIN11 新电脑配置 开启管理员模式 WIN11 下没有安装本地安全策略组件&#xff0c;表现为CMD运行 secpol.msc 命令会提示异常 开启本地安全策略 echo off pushd "%~dp0"…

电脑上怎么把PDF转换成PPT?几步教你轻松转换

PDF文件是一种常见的办公文档格式&#xff0c;它具有固定格式和内容不易改变的特点&#xff0c;广泛应用于各种正式场合。虽然各种办公软件都能够将文件转换为PDF格式&#xff0c;但如果需要将PDF文件转换为其他格式&#xff0c;就需要使用专门的工具了。今天我们将介绍两款常用…

论文解读 | 解耦知识蒸馏

10 年来&#xff0c;DNN 的变革给计算机视觉领域带来了重大发展&#xff0c;促成了各种实时任务的繁荣&#xff0c;如图像分类、目标检测、语义分割等。然而强大的网络通常得益于大的网络容量&#xff0c;这通常以大量的计算和存储为代价&#xff0c;是工业应用所不喜欢的。在工…

一觉醒后ChatGPT 被淘汰了

OpenAI 的 Andrej Karpathy 都大力宣传&#xff0c;认为 AutoGPT 是 prompt 工程的下一个前沿。 近日&#xff0c;AI 界貌似出现了一种新的趋势&#xff1a;自主人工智能。 这不是空穴来风&#xff0c;最近一个名为 AutoGPT 的研究开始走进大众视野。特斯拉前 AI 总监、刚刚回归…

X进制转十进制黄金万能算法

单纯、混合进制通吃&#xff0c;真正的黄金万能的进制转换方法。 【学习的细节是欢悦的历程】Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础那么简单…… 地址&#xff1a;https://…

LINUX_kali学习笔记

基础命令 命令说明示例pwd查看当前路径ls查看当前文件夹下文件 .开头为隐藏文件 &#xff08;文件夹下使用ctrlh查看&#xff09;ls -a&#xff08;查看文件及隐藏文件&#xff09;ls -alh&#xff08;查看文件及显示详情&#xff09;cd切换目录cd /&#xff08;切换到根目录&…

为何MySQL 8.0开始取消了查询缓存

官方文档说明&#xff1a;MySQL :: MySQL 8.0: Retiring Support for the Query Cache MySQL查询缓存是查询结果缓存。它将以SEL开头的查询与哈希表进行比较&#xff0c;如果匹配&#xff0c;则返回上一次查询的结果。 进行匹配时&#xff0c;查询必须逐字节匹配&#xff0c;例…

[Netty源码] 服务端启动过程 (二)

文章目录1.ServerBootstrap2.服务端启动过程3.具体步骤分析3.1 创建服务端Channel3.2 初始化服务端Channel3.3 注册selector3.4 端口绑定3.5 服务端的读事件1.ServerBootstrap ServerBootstrap引导服务端启动流程: //主EventLoopGroup NioEventLoopGroup master new NioEvent…