(4)STM32使用HAL库实现串口通讯——理论讲解
一、查询模式
1.
二、中断模式
1.中断接收。
1.1先看中断接收的流程(以 USART2 为例)
在启动文件中找到中断向量

USART2_IRQHandler
找到USART2_IRQHandler的函数定义

可以看到这里又转到另一个函数里去了,再找下去:

该函数的源码:
/**
* @brief This function handles UART interrupt request.
* @param huart: pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @retval None
*/
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t isrflags = READ_REG(huart->Instance->SR);
uint32_t cr1its = READ_REG(huart->Instance->CR1);
uint32_t cr3its = READ_REG(huart->Instance->CR3);
uint32_t errorflags = 0x00U;
uint32_t dmarequest = 0x00U; /* If no error occurs */
errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
if(errorflags == RESET)
{
/* UART in mode Receiver -------------------------------------------------*/
if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
return;
}
} /* If some errors occur */
if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
{
/* UART parity error interrupt occurred ----------------------------------*/
if(((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_PE;
} /* UART noise error interrupt occurred -----------------------------------*/
if(((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_NE;
} /* UART frame error interrupt occurred -----------------------------------*/
if(((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_FE;
} /* UART Over-Run interrupt occurred --------------------------------------*/
if(((isrflags & USART_SR_ORE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_ORE;
} /* Call UART Error Call back function if need be --------------------------*/
if(huart->ErrorCode != HAL_UART_ERROR_NONE)
{
/* UART in mode Receiver -----------------------------------------------*/
if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
} /* If Overrun error occurs, or if any error occurs in DMA mode reception,
consider error as blocking */
dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
if(((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)
{
/* Blocking error : transfer is aborted
Set the UART state ready to be able to start again the process,
Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
UART_EndRxTransfer(huart); /* Disable the UART DMA Rx request if enabled */
if(HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
{
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR); /* Abort the UART DMA Rx channel */
if(huart->hdmarx != NULL)
{
/* Set the UART DMA Abort callback :
will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;
if(HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
{
/* Call Directly XferAbortCallback function in case of error */
huart->hdmarx->XferAbortCallback(huart->hdmarx);
}
}
else
{
/* Call user error callback */
HAL_UART_ErrorCallback(huart);
}
}
else
{
/* Call user error callback */
HAL_UART_ErrorCallback(huart);
}
}
else
{
/* Non Blocking error : transfer could go on.
Error is notified to user through user error callback */
HAL_UART_ErrorCallback(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
}
}
return;
} /* End if some error occurs */ /* UART in mode Transmitter ------------------------------------------------*/
if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
} /* UART in mode Transmitter end --------------------------------------------*/
if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
{
UART_EndTransmit_IT(huart);
return;
}
}
也就是说,当串口中断触发以后,几经周转到了这里,该函数功能是读取寄存器的几个状态,判断无误后再转到另一个函数,就是上图小矩形框出来的UART_Receive_IT(huart);
然后我们再去看UART_Receive_IT(huart)这个函数原型:

回调函数就在这个UART_Receive_IT(huart)函数里:

在回调函数上边有两行很重要的代码:

这两行代码的作用是关闭串口接收中断,也就是说,在一次串口中断接收过程的最后,即串口接收完一组数据之后会关闭串口接收中断。(这个后面还会再讲,先记住)。
总结一下,串口中断接收的流程:
USART2_IRQHandler(void) -> HAL_UART_IRQHandler(UART_HandleTypeDef *huart) -> UART_Receive_IT(UART_HandleTypeDef *huart) -> HAL_UART_RxCpltCallback(huart);
Callback函数就是用户要重写在main.c里的回调函数。
再说明一下一个很重要的问题:STM32的每个串口中断有好几个(发送接收等),但是只要是与串口相关的中断发生系统都会先调用同一个函数,也就是中断向量表中的那个,比如usart2的话就是USART2_IRQHandler(void),然后这个函数再调用HAL_UART_IRQHandler,在HAL_UART_IRQHandler中去读取寄存器判断究竟是那几个位被置为1,确定好是哪个中断之后(接收还是发送)再调用不同的回调函数。
1.2如何使用接收中断。
在cube中配置完了之后并没有使能串口中断(有一个串口初始化函数,但是在这个函数中并未使能串口中断)需要用户手动使能。使能代码如下:
HAL_UART_Receive_IT(&huart2, (uint8_t *)kRxBuffer, );
什么意思呢?
HAL库的串口接收思路是这样的:用户你可以随便定义一个缓存区,大小随意,然后通过上边这个函数把这个缓存区对应到串口的接收,上面函数的意思就是把kRxBuffer(这是一个数组)作为缓存区,指定大小为10。然后usart2接收数据的时候就防到kRxBuffer这个数组中,只有当接收到10个数据之后才调用一次callback函数(回调函数)。当然不要忘了该函数的使能串口接收中断功能, 在:二、中断模式 的1.1节中说到了串口接收完数据后会关闭使能,所以,在回调函数中一定要再写一次HAL_UART_Receive_IT(&huart2, (uint8_t *)kRxBuffer, 10),使能接收中断。
小小的总结下串口中断接收怎么用:
(1)指定一个缓存区(串口接收到的数据会全部堆到这个缓存区)
(2)使能串口接收中断,并把缓存区对应到串口
(3)在回调函数中实现接收到数据之后的操作(比如处理数据)并再次使能串口接收中断。
所以更具体一下串口接收的流程就是这样的:
(1)串口一个接一个的接收到数据填充到缓存区
(2)缓存区满(大小是用户定义的)程序几经辗转最后会调用到回调函数。
(3)执行用户在回调函数中实现的功能。
2.中断发送。
2.1发送中断的触发流程。
由于在STM32中usart2的入口中断只有一个:

就是上图的中断向量表中红框标出来的。其他的所有中断其实都是从这里出发的,我们再和捋接收一样捋一遍发送。
首先是USART2_IRQHandler,找到这个函数原型(这一步和接收完全一样):

再找HAL_UART_IRQHandler(&huart2);原型:

到这里还是和接收完全一样,注意是完全一样,源码也就是上面接收贴出来的一样。这次我们主要注意该函数最后几行(可以翻上去看源码):

把中间代码收起来以后看最后红框,这就很明显了,这里触发了发送中断(软件触发)
接着去找这个UART_EndTransmit_IT(huart)的函数原型:

第一个红框里清除了发送中断使能(同接收一样,在用完之后就关掉,但是不同于接收,发送完成就不用再在回调函数中使能了,因为在中断发送的时候就会使能),第二个红框调用回调函数。
2.2如何使用发送中断。
中断发送的意思,非常类似于中断接收,但其中有一些不同,看下面这个函数:
HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
一个非常类似于中断接收使能的函数。接收中断使能函数的作用是绑定接收缓存区并使能接收中断,但是对于发送,该函数的作用是发送指定长度的指定数据并使能发送中断。
比如有一个unsigned char 数组a[10],HAL_UART_Transmit_IT(&huart2, a, 10),这一句的意思是用usart2(串口2)发送a数组中的10个数据,然后使能发送中断。
当发送完成之后(或者发送一半,发送一半也有个中断)就会执行回调函数。
总结一下发送中断:
使用HAL_UART_Transmit_IT函数发送指定长度的数据,并使能发送中断,发送到一半和发送结束会触发中断(相关的回调函数是HAL_UART_TxHalfCpltCallback()和HAL_UART_TxCpltCallback())中断触发后发送中断使能会被清除,然后调用回调函数,回调函数执行完成之后结束本次发送。
(4)STM32使用HAL库实现串口通讯——理论讲解的更多相关文章
- (2)STM32使用HAL库操作外部中断——理论讲解
1.中断触发过程 对主程序压栈--把中断服务函数的地址写入到程序计数器(PC)--执行中断服务函数 2.中断向量表 中断服务函数的地址在STM32的手册上的中断向量表中(如下是一部分): 如上表所示, ...
- (5)STM32使用HAL库实现串口通讯——实战操作
功能需求: (1)对接收的字符串原样返回(每10个字符一次). (2)发送一个字符串完成后改变LED的状态. 1.创建工程 使用的是F407Discovery,4个LED对应PD12-PD14. (1 ...
- (7)STM32使用HAL库实现RS485通讯(全双工串口)
一.硬件 如下图所示,485芯片链接到单片机的USART2上,但是默认的USART2并不是在PD5和PD6上,这里是需要重映射的.另外PG4作为485收发的控制(在485协议中,RE.DE同时为高电平 ...
- STM32 HAL库 UART 串口读写功能笔记
https://www.cnblogs.com/Mysterious/p/4804188.html STM32L0 HAL库 UART 串口读写功能 串口发送功能: uint8_t TxData[10 ...
- STM32 之 HAL库(固件库) _
1 STM32的三种开发方式 通常新手在入门STM32的时候,首先都要先选择一种要用的开发方式,不同的开发方式会导致你编程的架构是完全不一样的.一般大多数都会选用标准库和HAL库,而极少部分人会通过直 ...
- STM32 之 HAL库(固件库)
1 STM32的三种开发方式 通常新手在入门STM32的时候,首先都要先选择一种要用的开发方式,不同的开发方式会导致你编程的架构是完全不一样的.一般大多数都会选用标准库和HAL库,而极少部分人会通过直 ...
- STM32F072从零配置工程-基于HAL库的串口UART中断配置
先上一个采用串口直接传输的Demo: 此处的思路是完全采用HAL库来实现的,核心是运用HAL_UART_Transmit_IT和HAL_UART_Receive_IT两个函数来实现的,可以作为一个De ...
- STM32之HAL库、标准外设库、LL库(STM32 Embedded Software)-(转载)
STM32 Embedded Software 工作以来一直使用ST的STM32系列芯片,ST为开发者提供了非常方便的开发库.到目前为止,有标准外设库(STD库).HAL库.LL库 三种.前两者都是 ...
- STM32基于HAL库通过DMA读写SDIO
通过STM32CUBEMX生成DMA读写sdio的工程,再读写过程中总会卡死在DMA中断等待读写完成的while中,最终发现while等待的标志在SDIO的中断里置位的,而SDIO中断优先级如果小于或 ...
随机推荐
- 修改flume源码,使其HTTPSource具备访问路径功能
目前有一个需求,就是Flume可以作为一个类似于tomcat的服务器,可以通过post请求进行访问,并且路径需要:ip:port/contextPath格式. 经过一些资料获悉,httpSource只 ...
- python 之路,200行Python代码写了个打飞机游戏!
早就知道pygame模块,就是没怎么深入研究过,恰逢这周未没约到妹子,只能自己在家玩自己啦,一时兴起,花了几个小时写了个打飞机程序. 很有意思,跟大家分享下. 先看一下项目结构 "" ...
- RESTful小拓展
RESTful 即Resource Representation State Transfer 相对应Resource 资源层,Representation 表现层,State Transfer状态转 ...
- CSS后代选择器“空格”和“>”的使用辨析
要点: 1. "空格":包含子孙 2. ">":含子不含孙 举个栗子: html代码如下 <body> <div class=" ...
- Android 路由框架ARouter最佳实践
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/76165252 本文出自[赵彦军的博客] 一:什么是路由? 说简单点就是映射页面跳转 ...
- golang使用通道模仿实现valatile语义
golang团队在sync中提供了很多的原子操作函数,将原子操作转向由单独一个包提供,而不是像Java那样提供各种累,确实上手得更加简单.但是golang原生提供的并发操作没有Java来得丰富 ...
- java之jsp页面语法
jsp页面相比静态页面html来说,就是多了一些脚本,利用这些脚本来动态地改变页面内容的显示. 1.JSP脚本写法 <% 这里写java代码; %> <%! JSP声明,用来声明变量 ...
- 大数据与Mapreduce
第十五章 大数据与Maprudece 一.引言 实际生活中的数据量是非常庞大的,采用单机运行的方式可能需要若干天才能出结果,这显然不符合我们的预期,为了尽快的获得结果,我们将采用分布式的方式,将计算分 ...
- 对cordova插件配置文件plugin.xml的理解
1.配置文件表头包括了插件id,是用于唯一标识插件的.同时插件配置了一个插件名称. 2.这个文件从工作机制,也就是js代码一直到native的java插件代码工作分成两个流程.第一个流程是从代码到插件 ...
- seek()对中文偏移测试
当前目录下创建"中文测试.txt"文件,写入: 我是大好人aaa我是大坏人bbb f = open('中文测试.txt', 'r+', encoding='utf-8') # f. ...