STM32CubeMX配置串口DMA传输实现不定长数据收发

2023-05-16

串口简介

串口是全双工的串行通信协议。串口通信指串口按位(bit)发送和接收字节(一个字节有8位)。尽管比特字节(byte)的串行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。串口通信协议是指规定了数据包的内容,内容包含了起始位、主体数据、校验位及停止位,双方需要约定一致的数据包格式才能正常收发数据的有关规范。串口通信协议是基于串口使得通信双方能够相互沟通信息的一种约定,其定义了双方遵循的协议数据帧格式和其传输方式。因为串口通信没有时钟线,说设备双方必须约定好相同的波特率,这样才能保证数据收发准确无误。常见的波特率有4800、9600、115200等。

起始位、停止位
数据包从起始位开始,到停止位结束。起始信号用逻辑0(最开始为拉高,当拉低后表示起始)的数据位表示,停止信号由0.5、1、1.5或2个逻辑1的数据位表示,只要双方约定一致即可。
有效数据
起始位之后便是传输的主体数据内容了,也称为有效数据,其长度一般被约定为5、6、7或8位长。
数据校验
由于在通讯过程中易受到外部干扰导致传输数据出现偏差,所以在有效数据之后加上校验位解决。校验方法有奇校验(odd)、偶校验(even)、0校验(space)、1校验(mark)及无校验(noparity)。
奇校验要求有效数据和校验位中“1”的个数为奇数,比如一个8位长的有效数据为:01101001,此时共有4个“1”,为达到奇校验效果,校验位为“1”,最后传输的是8位有效数据加1位校验位,共9位。
偶校验刚好相反,要求有效数据和校验位的“1”数量为偶数,则此时为达到偶校验效果,校验位为“0”。而0校验则无论有效数据中是什么数据内容,校验位总是为“0”,1校验校验位总是为“1”。

CubeMX配置

配置好时钟。
串口配置:使用默认配置(传输数据长度为8 Bit,奇偶检验无,停止位为1 Bit, 接收和发送都使能)
在这里插入图片描述
打开DMA传输:
在这里插入图片描述
打开串口接收中断:
在这里插入图片描述生成工程;

添加代码

添加代码实现printf函数。
在usart.c中添加如下代码,记住一定要添加在
/* USER CODE BEGIN 1 /
/
USER CODE END 1 */
里面,否则一旦CubeMX重新生成工程,就会不见。

/* USER CODE BEGIN 0 */
#include "stdio.h"
/* USER CODE END 0 */

/* USER CODE BEGIN 1 */
#if 1
#pragma import(__use_no_semihosting)                             
struct __FILE
{
int handle;
};
FILE __stdout;      
void _sys_exit(int x)
{
   x = x;
}
int fputc(int ch, FILE *f)
{     
  while((USART1->SR&0X40)==0);  
  USART1->DR = (uint8_t) ch;     
  return ch;
}
#endif

/* USER CODE END 1 */

然后就可以使用printf函数了。
这里我们先了解一下串口的IDLE中断
串口IDLE中断,能自动响应你从电脑(别的串口)接收到的不定长数据。而不是一直干等着。stm32接收数据时,并不会马上把数据马上处理掉,而是写到你定义的缓冲区里,然后你串口线上一个BYTE长度的时间如果没接收到数据,就会产生了IDLE中断(只会产生一次,别担心一直会卡在里面,除非等下次再接收时)。
接着在usart.c文件代码,重写串口的空闲中断回调函数:void USAR_UART_IDLECallback(UART_HandleTypeDef *huart,uint8_t rxlen )。HAL库中没有定义IDLE的中断处理函数,所以需要我们自己定义。
在此之前我们先在usart.h中定义一些缓存数组和一些会用到的变量,和添加必要的头文件。

#include "string.h"

/* USER CODE BEGIN Private defines */
#define UART1_RX_SIZE 100	

extern uint8_t UART1_RX_BUF[];  //串口接收缓存
extern uint8_t UART1_RX_LEN;  //接收到的数据量
extern uint8_t UART1_RX_Data[];  //数据缓存

/* USER CODE END Private defines */
void USAR_UART_IDLECallback(UART_HandleTypeDef *huart,uint8_t rxlen )
{
	if(huart == &huart1)  //判断是否为串口1产生中断
	{
		memcpy(UART1_RX_Data,UART1_RX_BUF,rxlen);  //将UART1_RX_BUF的数据复制到UART1_RX_Data中,最后两位是标志不是数据
		HAL_UART_Transmit_DMA(&huart1, UART1_RX_BUF,rxlen);//将接收到的不定长数据发送到上位机
	  rxlen = 0;//清除数据长度计数
HAL_UART_Receive_DMA(&huart1,UART1_RX_BUF,UART1_RX_SIZE);//重新打开DMA接收	        
	}
}
//写完后记得在usart.h文件中声明一下

在stm32f1xx_it.c,找到void USART1_IRQHandler(void)函数,添加我们自己的中断处理函数。

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

	if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET))
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);  //清楚空闲状态标志

		HAL_UART_DMAStop(&huart1);  //关闭DMA传输

		UART1_RX_LEN = UART1_RX_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);  //计算接收到的数据长度
		USAR_UART_IDLECallback(&huart1,UART1_RX_LEN );  //调用回调函数
	}
  /* USER CODE END USART1_IRQn 1 */
}

接着在main.c文件添加代码,使能串口空闲中断和启动DMA传输。还有记得观察一下ain函数中的DMA初始化函数有没有在串口初始化之前,我之前遇到过DMA初始化函数没在串口初始化之前导致程序没有达到预想的效果。

__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);  //使能空闲中断
	HAL_UART_Receive_DMA(&huart1,UART1_RX_BUF,UART1_RX_SIZE);  //启动DMA接收,UART1_RX_BUF:数据接收缓冲

接下我们就可以用串口做很多东西了,比如连接蓝牙,WiFi模块等进行控制单片机上的外设。
以蓝牙控制舵机为例,舵机驱动这一部分参考我前面的博客http://t.csdn.cn/4iO1r

void SG90_Select(void)
{
	if(UART1_RX_Data[0]=='S' && UART1_RX_Data[1] == 'W' && UART1_RX_Data[2] == '1') //UART1_RX_Data定义的数据接收缓冲数组
		{
			SG90_Rotate(&htim2,1,Degrees_0);
		}
    else if(UART1_RX_Data[0]=='S' && UART1_RX_Data[1] == 'W' && UART1_RX_Data[2] == '2')
    {
			SG90_Rotate(&htim2,1,Degrees_45);
		}
    else if(UART1_RX_Data[0]=='S' && UART1_RX_Data[1] == 'W' && UART1_RX_Data[2] == '3')
    {
			SG90_Rotate(&htim2,1,Degrees_90);
		}
    else if(UART1_RX_Data[0]=='S' && UART1_RX_Data[1] == 'W' && UART1_RX_Data[2] == '4')
    {
			SG90_Rotate(&htim2,1,Degrees_135);
		}
    else if(UART1_RX_Data[0]=='S' && UART1_RX_Data[1] == 'W' && UART1_RX_Data[2] == '5')
    {
			SG90_Rotate(&htim2,1,Degrees_180);
		}
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32CubeMX配置串口DMA传输实现不定长数据收发 的相关文章

随机推荐

  • Linux下载安装和使用SQLite

    Linux安装SQLite 一 SQLite下载二 SQLite安装三 SQLite的使用1 解决无法直接用sqlit3命令2 解决无法编译的问题 总结 一 SQLite下载 首先 xff0c 前往SQLite官网下载页面找到包含confi
  • 解决:‘config.status: error: Something went wrong bootstrapping makefile fragments......’问题

    解决 xff1a config status error Something went wrong bootstrapping makefile fragments 问题 一 问题二 解决方法 一 问题 首先我们来看安装sqlite时报的这
  • TFTP服务器搭建与使用

    文章目录 一 TFTP协议二 TFTP服务器搭建1 安装TFTP服务器2 创建TFTP服务文件夹3 配置tftp文件4 配置tftpd hpa文件 三 TFTP服务器使用 一 TFTP协议 TFTP xff08 Trivial File T
  • 深入探讨Linux驱动开发:Linux设备树

    文章目录 一 设备树介绍二 设备树框架1 设备树框架2 节点基本格式3 节点部分属性简介 总结 一 设备树介绍 设备树 xff08 Device Tree xff0c 简称 DT xff09 是一种在嵌入式系统中描述硬件设备的一种数据结构和
  • 深入探讨Linux驱动开发:驱动介绍与hello驱动实例

    文章目录 前言一 Linux驱动介绍1 用户态和内核态2 内核功能介绍3 驱动程序介绍 二 驱动程序分类与注意事项1 驱动程序分类2 内核驱动开发注意事项 三 hello驱动开发1 驱动模块2 模块加载和卸载函数3 编写hello模块4 M
  • ROS中使用乐视 奥比中光(Astra Pro)深度相机显示彩色和深度图像

    环境 UbuntuROS Kinect or Melodic 奥比中光ROS驱动包安装地址 xff1a https github com orbbec ros astra camera 1 安装ROS 2 安装依赖 span class t
  • 深入探讨Linux驱动开发:字符设备驱动开发与测试

    文章目录 一 字符设备驱动介绍1 设备驱动介绍 二 设备号1 设备号介绍2 分配与释放设备编号 dev t类型 静态分配设备号 动态分配设备号 释放主次设备号 手动创建设备节点 自动创建设备节点 删除设备节点 三 字符设备注册1 cdev结
  • ZED-深度感知使用

    文章目录 1 深度感知配置2 得到深度数据2 1 得到深度值 3 展示深度图4 获取点云数据4 1 从点云数据中计算距离 5 得到法线图像6 调整深度分辨率 1 深度感知配置 可以在初始化时使用InitParameters xff0c 在运
  • Linux系统之 开机自启动程序脚本 编写

    Linux系统启动加载程序 最近完成了项目 xff0c 来个开机自启运行 找到已编译好的程序 xff08 以下是我编译的house xff09 span class token function ls span l span class t
  • 算法时间复杂度、空间复杂度分析

    算法时间复杂度 在计算机程序编写前 xff0c 依据统计方法对算法进行估算 xff0c 经过总结 xff0c 我们发现一个高级语言编写的程序程序在计算机上运行所消耗的时间取决于下列因素 1 算法采用的策略和方案 编译产生的代码质量 3 问题
  • Linux 选择题一百道

    cron 后台常驻程序 daemon 用于 xff1a A 负责文件在网络中的共享 B 管理打印子系统 C 跟踪管理系统信息和错误 D 管理系统日常任务的调度 在大多数Linux发行版本中 xff0c 以下哪个属于块设备 block dev
  • 程序返回return与系统退出exit

    程序返回return与系统退出exit return是语言级别的 xff0c 它表示了调用堆栈的返回 xff1b exit则是系统调用级别的 xff0c 它表示了一个进程的结束 return是返回函数调用 xff0c 如果返回的是main函
  • 结构体、结构体变量、结构体指针、字符串

    结构体 数组是用于保存一组相同类型数据的 而结构体是用于保存一组不同类型数组的 在使用结构体之前必须先定义结构体类型 因为C语言不知道你的结构体中需要存储哪些类型数据 我们必须通过定义结构体类型来告诉C语言 我们的结构体中需要存储哪些类型的
  • linux下脚本实现 切换root用户并执行

    借助一个工具 expect sudo apt span class token operator span get install expect 编写脚本 xff1a vim root sh span class token operato
  • csdn极客江南

    零基础学会 C 语言课程学习突破 1500 人 xff1a 学习地址 xff1a https edu csdn net course detail 31452 spm 61 1001 2014 3001 5507TS TS 专栏文章更新至第
  • 音视频编码格式认知

    编码格式对应的就是音频编码和视频编码 xff0c 音频编码标准和视频编码标准 xff0c 每种编码标准都对应的编码算法 xff0c 其目的是通过一定编码算法实现数据的压缩 减少数据的冗余 视频编码指的是通过特定的压缩技术 xff0c 将某个
  • TensorFlow、PyTorch各版本对应的CUDA、cuDNN关系

    TensorFlow PyTorch各版本对应的CUDA cuDNN关系 xff08 截止2021年4月7日 xff09 TensorFowLinuxCPUGPU macOSCPUGPU WindowsCPUGPU PyTorchCPUGP
  • Android源码新大陆

    vold amp av http aospxref com android 13 0 0 r3 xref system vold model http aospxref com android 13 0 0 r3 xref framewor
  • QT读取GPS模块时,显示不完全,一条指令只能ReadAll32个字节,数据被分开

    在用QT读取GPS模块的时候 xff0c 发现读取到的数据总是显示不出来经度 xff0c 用debug调试 xff0c 发现数据被分开 xff0c 一条指令只能读到N那里 xff0c GNRMC 064401 65 A 3110 47069
  • STM32CubeMX配置串口DMA传输实现不定长数据收发

    串口简介 串口是全双工的串行通信协议 串口通信指串口按位 xff08 bit xff09 发送和接收字节 xff08 一个字节有8位 xff09 尽管比特字节 xff08 byte xff09 的串行通信慢 xff0c 但是串口可以在使用一