STM32 HAL库之串口详细篇
一、基础认识
(一) 并行通信
原理:数据的各个位同时传输
优点:速度快
缺点:占用引脚资源多,通常工作时有多条数据线进行数据传输
8bit数据传输典型连接图:
传输的数据是二进制:11101010,则通信使用8条线同时进行数据传输,发送端一次性发送8位数据,接收端一次性接收8位数据。
(二) 串行通信
原理:数据按位顺序传输
优点:占用引脚资源少
缺点:速度相对较慢,通常工作时只有一条数据线进行数据传输
8bit数据传输典型连接图:
传输的数据是二进制:11101010,则通信使用8条线同时进行数据传输,发送端一次性发送8位数据,接收端一次性接收8位数据。
8bit数据传输典型连接图:
传输的数据是二进制:11101010,则通信使用1条线进行数据传输,发送端一次性发送1位数据,接收端一次性接收1位数据。
串行通信的分类:
1.单工:数据只能在一个方向上传输,通信双方数据只能由一方传输到另一方
2.半双工:数据可以错时双向传输,通信双方数据可以支持两个方向传输,但是同一时间只能由一方传输到另外一方。
3.全双工:数据可以同时双向传输,通信双方数据可以同时进行双向传输,对于其中一个设备来说,设备需要支持发送数据时可以进行数据接收。
串行通信的通讯方式:
l 同步通信:带时钟同步信号的传输,如SPI、IIC、USART(同步)
l 异步通信:不带时钟同步信号的传输,如UART、USART(异步)
常见数据传输协议:
(三) UART和USART
UART:通用异步收发器
USART:通用同步/异步收发器,其可选使用异步方式,那将和UART无区别,如果是同步,则需要多一根时钟线(USART_CK)
(四) STM32的USART注意:
l 通常USART1接口的通信速率较快,其它USART接口较慢。如STM32F103C8T6的USART1接口通信速率是4.5Mbps,其它USART接口的通信速率是2.25Mbps。
l 片上所有的USART接口都可以使用DMA操作
l USART的扩展及距离:
UART和COM是物理接口形式(物理接口)
TTL和RS-232是电平标准(电信号)
串口接收:
l 扫描模式
l 中断模式
l DMA模式
二、串口基础配置
模式选择:
Asynchronous 异步通信
Synchronous 同步通信
Single Wire (Half-Duplex) 单线/半双工
Multiprocessor Communication 多处理器
支持局域互连网络LIN、智能卡(SmartCard)协议与lrDA(红外线数据协会) SIR ENDEC规范。
默认的TX GPIO:
l 模式为:推挽式复用功能
l 输出速率:高速
默认的RX GPIO:
l 模式为:浮空输入
参数设置
l Baud Rate
任意设置,未做限制,输入框
l Word Length
数据位可选8位或9位
l Parity
校验位可选无校验(None)、偶校验(Even)、奇校验(Odd)
l Stop Bits
停止位可选1位、2位
l Data Direction
数据方向,可选收发(Receive and Transmit)、只接收(Receive Only)、只发送(Transmit Only)
三、阻塞发送函数
以阻塞模式发送大量数据
当没有启用UART奇偶校验( PCE sign0 ),并且单词长度配置为9位( m1 - m0 sign01 )时,*发送的数据作为一组U16处理。在9位/无奇偶校验传输的情况下,pData需要作为uint16_t指针处理
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
参数:
huart: 指向uart _ handletypedef结构的huart指针,该结构包含指定uart模块的配置信息。
PData: 指向数据缓冲区的pData指针(U8或u16数据元素)。
Size: 要发送的数据元素( u8或U16 )的大小
Timeout:超时持续时间,单位ms,0就是0ms超时,数据最大发送时间,超过则返回异常
返回:
HAL 状态
typedef enum
{
HAL_OK = 0x00U,
HAL_ERROR = 0x01U,
HAL_BUSY = 0x02U,
HAL_TIMEOUT = 0x03U
} HAL_StatusTypeDef;
例如:
HAL_UART_Transmit(&huart1,"dongxiaodong\r\n",strlen("dongxiaodong\r\n"),0xFFFF);
四、串口扫描接收
(一)相关函数
l 阻塞接收函数
在阻塞模式下接收大量数据。
当没有启用UART奇偶校验( PCE sign0 ),并且单词长度配置为9位( m1 - m0 sign01 )时,*接收到的数据作为一组U16处理。
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
huart: 指向uart _ handletypedef结构的huart指针,该结构包含指定uart模块的配置信息。
pData:指向数据缓冲区的指针( u8或U16数据元素)。
Size: 要接收的数据元素数量( u8或U16 )。
Timeout:超时持续时间,单位ms,0就是0ms超时,数据接收最大等待时间,超出则异常
返回:
HAL 状态
typedef enum
{
HAL_OK = 0x00U,
HAL_ERROR = 0x01U,
HAL_BUSY = 0x02U,
HAL_TIMEOUT = 0x03U
} HAL_StatusTypeDef;
例如:
uint8_t data=0;
while (1)
{
if(HAL_UART_Receive(&huart1,&data,1,0)==HAL_OK){ }
}
(二)代码实现
HAL_UART_Transmit(&huart1,"dongxiaodong\r\n",strlen("dongxiaodong\r\n"),0xFFFF);
uint8_t data=0;
while (1)
{
//串口接收数据
if(HAL_UART_Receive(&huart1,&data,1,0)==HAL_OK){
//将接收的数据发送
HAL_UART_Transmit(&huart1,&data,1,0);
}
}
其中timeout为0表示没有延时,所以串口接收函数是不阻塞的,while循环将一直轮询
加个延时函数
这样一来的话,接收数据就异常了,会接收数据不全,所以这样是不可靠的
那改成这样呢?
uint8_t data[100]={0};
if(HAL_UART_Receive(&huart1,data,100,1000)==HAL_OK){ }
接收100个数据,等待时间为1秒,这样的话接收区没满时,每次运行这条语句都要延时等待1S,这时相当于一个HAL_Dealy(1000),这会阻塞while循环。只有数据接收到刚刚等于100才能返回HAL_OK,所以不能用于接收变成数据,这是不理想的。
五、 串口中断接收
(一)cubemx设置
使能串口中断
优先级选择
Preemption Priorit:抢占优先级
Sub Priority :子优先级
数字越小优先级越高
自动生成的代码中已经使能了中断
(二)相关函数
l 接收中断开启,只开启一次中断
以非阻塞模式接收一定数量的数据,当UART奇偶校验未启用(PCE = 0),且字长配置为9位(M1-M0 = 01)时,
*接收到的数据作为一组u16处理。在这种情况下,Size必须指出数字
*的u16可通过pData。
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
参数:
huart: 指向uart _ handletypedef结构的huart指针,该结构包含指定uart模块的配置信息。
pData:指向数据缓冲区的指针(u8或u16数据元素)。
Size:需要接收的数据元素(u8或u16)的数量。
返回:
HAL 状态
typedef enum
{
HAL_OK = 0x00U,
HAL_ERROR = 0x01U,
HAL_BUSY = 0x02U,
HAL_TIMEOUT = 0x03U
} HAL_StatusTypeDef;
l 中断接收回调函数
HAL_UART_RxHalfCpltCallback();一半数据接收完成时调用
HAL_UART_RxCpltCallback();数据完全接受完成后调用
函数原型
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
(三) 编程实现方法1
uint8_t my_uart1_redata=0;
//开启串口接收中断
void my_uart1_enable_inpterr(){
//开启一次中断
HAL_UART_Receive_IT(&huart1,&my_uart1_redata,1); }
//串口收到数据回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart->Instance == USART1)//判断串口号
{
//发送
HAL_UART_Transmit(&huart1,&my_uart1_redata,1,100);
//开启一次中断
HAL_UART_Receive_IT(&huart1,&my_uart1_redata,1);
}
}
存在问题:
数据发送太快之后就可能导致单片机无法再接收数据,以至于永久性损坏,通常可以在主循环里判断标志位再次启动,可以避免永久性损坏问题。
(四) 编程实现方法2
修改stm32f1xx_it.c里面的串口中断
#include <usart.h>
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
//正在接收
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
{
//NET_UART_RECV(READ_REG(huart1.Instance->RDR));
my_uart1_callback(huart1.Instance->DR);
__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_RXNE);
} //溢出-如果发生溢出需要先读SR,再读DR寄存器 则可清除不断入中断的问题
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE)== SET)
{
__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_ORE); //读SR
//READ_REG(huart1.Instance->RDR); //读DR
}
//手动关闭自带的串口中断处理
#if 0
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
#endif
/* USER CODE END USART1_IRQn 1 */
}
标准函数
//开启串口接收中断
void my_uart1_enable_inpterr(){
//开启一次中断
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能接收中断 }
//串口收到数据回调
void my_uart1_callback(uint8_t rdata){ //发送
HAL_UART_Transmit(&huart1,&rdata,1,1); }
修改了HAL自带的串口中断函数,可以有效的避免接收中断失效问题,但是你测试的时候会发现串口助手发送的数据和串口助手接收到的数据不完整,这是正常的,因为中断接收是很快的,而发送是阻塞的,而实际也不会这样使用,所以一般都会用数组做缓冲区接收串口数据。
六、 配置串口为中断接收DMA发送
l STM32可用DMA的外设:定时器、ADC、SPI、IIC、USART
l 使用DMA必须开启中断
l 串口DMA模式最大为u16个字节,则65535
(一)cubmx设置
通用配置
中断开启
DMA发送设置
Dirction : DMA传输方向
四种传输方向:
l 外设到内存 Peripheral To Memory
l 内存到外设 Memory To Peripheral
l 内存到内存 Memory To Memory
l 外设到外设 Peripheral To Peripheral
Priority: 传输速度
l 最高优先级 Very Hight
l 高优先级 Hight
l 中等优先级 Medium
l 低优先级;Low
Priority: 优先级
l 最高优先级 Very Hight
l 高优先级 Hight
l 中等优先级 Medium
l 低优先级;Low
mode:模式
l Normal:正常模式,当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次
l Circular: 循环模式,传输完成后又重新开始继续传输,不断循环永不停止
Increment Address:地址增加
l Peripheral:设置传输数据的时候外设地址是不变还是递增。如果设置 为递增,那么下一次传输的时候地址加 Data Width个字节,勾选表示递增。
l Memory:设置传输数据时候内存地址是否递增。如果设置 为递增,那么下一次传输的时候地址加 Data Width个字节,这个Src Memory一样,只不过针对的是内存。,勾选表示递增。
data width:数据宽度
byte:字节,通用8位,与u8相同
word:字长,与硬件的位数相同,STM32是32位,所以对应是u32
Half Word:半个字长,所以对应是u16
(二) 编程实现
串口DMA发送
#include "string.h"
extern DMA_HandleTypeDef hdma_usart1_tx; //发送数组数据
void my_uart1_send_data(uint8_t *tdata,uint16_t tnum){
//等待发送状态OK
while(HAL_DMA_GetState(&hdma_usart1_tx) == HAL_DMA_STATE_BUSY) HAL_Delay(1);
//发送数据
HAL_UART_Transmit_DMA(&huart1,tdata,tnum);
} //发送字符串
void my_uart1_send_string(uint8_t *tdata){
//等待发送状态OK
while(HAL_DMA_GetState(&hdma_usart1_tx) == HAL_DMA_STATE_BUSY) HAL_Delay(1);
//发送数据
HAL_UART_Transmit_DMA(&huart1,tdata,strlen(tdata));
}
串口库函数中断接收
uint8_t my_uart1_redata=0;
//开启串口接收中断
void my_uart1_enable_inpterr(){
//开启一次中断
HAL_UART_Receive_IT(&huart1,&my_uart1_redata,1); }
//串口收到数据回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart->Instance == USART1)//判断串口号
{
//发送
my_uart1_send_data(&my_uart1_redata,1);
//开启一次中断
HAL_UART_Receive_IT(&huart1,&my_uart1_redata,1);
}
}
主函数
//开启中断
my_uart1_enable_inpterr();
//发送数据
my_uart1_send_data("1dongxiaodong_DMA_1\r\n",strlen("1dongxiaodong_DMA_1\r\n"));
my_uart1_send_data("2dongxiaodong_DMA_2\r\n",strlen("2dongxiaodong_DMA_2\r\n"));
my_uart1_send_string("3dongxiaodong_DMA_3\r\n");
七、 串口DMA收和发
(一)CubeMX配置
通用配置
中断开启
DMA发送设置
DMA接收设置,要注意这里是循环
(二)编程实现
收发函数原型
#include "string.h"
extern DMA_HandleTypeDef hdma_usart1_tx; //发送数组数据
void my_uart1_send_data(uint8_t *tdata,uint16_t tnum){
//等待发送状态OK
while(HAL_DMA_GetState(&hdma_usart1_tx) == HAL_DMA_STATE_BUSY) HAL_Delay(1);
//发送数据
HAL_UART_Transmit_DMA(&huart1,tdata,tnum);
} //发送字符串
void my_uart1_send_string(uint8_t *tdata){
//等待发送状态OK
while(HAL_DMA_GetState(&hdma_usart1_tx) == HAL_DMA_STATE_BUSY) HAL_Delay(1);
//发送数据
HAL_UART_Transmit_DMA(&huart1,tdata,strlen(tdata));
} uint8_t my_uart1_redata=0;
//开启串口接收中断
void my_uart1_enable_inpterr(){
//开启一次中断
//HAL_UART_Receive_IT(&huart1,&my_uart1_redata,1);
HAL_UART_Receive_DMA(&huart1,&my_uart1_redata,1);//设置接收缓冲区 }
//串口收到数据回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart->Instance == USART1)//判断串口号
{
//发送
my_uart1_send_data(&my_uart1_redata,1);
//开启一次中断
//HAL_UART_Receive_IT(&huart1,&my_uart1_redata,1);
}
}
主函数使用
//初始化DMA接收
my_uart1_enable_inpterr();
//发送函数调用
my_uart1_send_data("1dongxiaodong_DMA_1\r\n",strlen("1dongxiaodong_DMA_1\r\n"));
my_uart1_send_data("2dongxiaodong_DMA_2\r\n",strlen("2dongxiaodong_DMA_2\r\n"));
my_uart1_send_string("3dongxiaodong_DMA_3\r\n");
八、printf实现
#include <stdio.h>
int fputc(int ch,FILE *f)
{
uint32_t temp = ch; HAL_UART_Transmit(&huart1,(uint8_t *)&temp,1,0xFFFF); //huart1是串口的句柄
HAL_Delay(2); return ch;
}
参考:
正点原子、洋桃电子
STM32 HAL库之串口详细篇的更多相关文章
- STM32 HAL库 UART 串口读写功能笔记
https://www.cnblogs.com/Mysterious/p/4804188.html STM32L0 HAL库 UART 串口读写功能 串口发送功能: uint8_t TxData[10 ...
- STM32 HAL库关于串口中断烧录程序后可以正常运行,断电重启后无法进入中断的问题分析以及解决方法
1.情景描述: 最近在做一个项目,X86的上位机通过串口控制MCU,使用串口中断接收上位机数据时,MCU在上电的情况下烧录程序,可以正常接收上位机的数据,在断电重启后,一直进入不了中断回调函数,上电的 ...
- (4)STM32使用HAL库实现串口通讯——理论讲解
一.查询模式 1. 二.中断模式 1.中断接收. 1.1先看中断接收的流程(以 USART2 为例) 在启动文件中找到中断向量 USART2_IRQHandler 找到USART2_IRQHandle ...
- 【书籍连载】《STM32 HAL 库开发实战指南—基于F7》-第一章
从今天起,每天开始连载一章<STM32 HAL 库开发实战指南—基于F7>.欢迎各位阅读.点评.学习. 第1章 如何使用本书 1.1 本书的参考资料 本书参考资料为:<STM32 ...
- 【有趣的全彩LED | 编程】用STM32 HAL库让WS2812B为你所动
一.效果展示 观看演示效果:https://www.bilibili.com/video/BV1dv411Y7x3 使用STM32 HAL库编程 PWM+DMA控制输出,CubeMX生成初始工程 实现 ...
- 【情人节选帽子】TCS34725颜色传感器和Python图形界面编程(STM32 HAL库)
截图 描述: l STM32 HAL库编程 l 使用模拟IIC通信,方便程序移植 l Python界面编写,蘑菇头的帽子是什么颜色 l STM32 HAL库串口通信 l Python界面使用 ...
- stm32 HAL库笔记(零)
最近在设计四旋翼飞行器,用stm32f407,有三种开发方式可以选择:一.寄存器开发.二:库函数开发.三:HAL库开发,考虑了一下,选择了HAL库,原因如下: 1. 寄存器开发相对较慢,寄存器很多,配 ...
- STM32 HAL库详解 及 手动移植
源: STM32 HAL库详解 及 手动移植
- 【春节歌曲回味 | STM32小音乐盒 】PWM+定时器驱动无源蜂鸣器(STM32 HAL库)
l STM32通过PWM与定时器方式控制无源蜂鸣器鸣响 l STM32小音乐盒,歌曲进度条图形显示与百分比显示,歌曲切换 l 编程使用STM32 HAL库 l IIC OLED界面编程,动画实 ...
随机推荐
- Java并发编程的艺术(八)——锁相关
锁的作用 控制多个线程访问共享资源. 线程协作 Lock接口 特点 与synchronized类似的同步功能,只是需要显式地获取和释放锁.缺少隐式获取锁的便捷性. 拥有锁获取与释放的可操作性.可中断的 ...
- MySql字符集与排序规则详解
前段时间往MySQL中存入emoji表情或生僻字.繁体字时,报错无法添加,研究后发现这是字符集编码的问题,今天就来分析一下各个字符集与排序规则 一.字符集 先说字符,字符是各种文字和符号的总称,包括各 ...
- IDEA将Java Web项目打war包
准备工作 1.IntelliJ IDEA开发工具 2.可以正常运行的Java Web项目 打war包流程 1.使用快捷键Ctrl+Alt+Shift+s 或者 鼠标点击选中项目名按F4 打开 Proj ...
- vue第二单元(webpack的配置-学习webpack的常用配置)
第二单元(webpack的配置-学习webpack的常用配置) #课程目标 掌握webpack的常用配置 掌握如何根据实际的需求修改webpack的对应配置 了解webpack-dev-server的 ...
- Git - 简单的使用与Github
Github: Following the instructions to create repo. Git on Linux(centos): download the latest GIT and ...
- 模拟sql注入实现远程桌面登录
首先用sql注入文件命令y url+一句话 into outfile 绝对路径/test.php 用蚁剑连接打开连接的终端 先看用户的权限 创建一个用户将它放入队列中 查看3389端口是否开启 0xd ...
- Getting unknown property: common\models\Teacher::auth_Key
找了一个半小时,不知道为什么会缺少这个属性,数据库里面的字段明明都是有的. 然后随后找到了原因,是因为key中的k大写了,所以无法识别这个属性.把自己坑到了,以此为戒,以后多注意细节问题
- iOS 自定义tabBarController(中间弧形)
效果图 1.在继承自UITabBarController的自定义controller中调用以下方法(LZCustomTabbar为自定义的tabbar) - (void)viewDidAppear:( ...
- C# Attribute特性 泛型<T> 方法的out ref this(扩展方法) Equals与==
out ref out和ref的使用场景其实不一样.out适合在方法内返回一个额外的结果参数,而ref适合直接改变一个在方法外面的值,方法改变外部就改变,无需重新定义接住返回值再使用. out可以在方 ...
- 【磁盘/文件系统】第五篇:CentOS7.x__btrfs文件系统详解
前言: Btrfs文件系统是CentOS7.x系列系统上的技术预览版,但是现在还是有公司在使用. btrfs 文件系统(又称B-tree.Butter FS.Better FS等文件系统) 理解b ...