STM32HAL库 串口USART的使用

STM32HAL库 串口USART的使用



前言

本文为串口输出打印的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行
在这里插入图片描述


总结

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

  • 7
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZRob

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值