STM32HAL库串口和DMA库函数调用关系

STM32HAL库串口和DMA库函数调用关系

一、串口中断接收(串口3为例)

stm32f1xx.it.c——USART3_IRQHandler(void)

该文件里是中断向量表的函数,当串口3接收完数据后,产生中断,会先调用USART3_IRQHandler(void)函数,函数里再调用HAL_UART_IRQHandler(&huart3);

点击跳转到HAL_UART_IRQHandler函数的具体实现

在这里插入图片描述

stm32f1xx_hal_uart.c——HAL_UART_IRQHandler(&huart3)

该函数里通过判断标志位来确定是哪个中断请求,进而再调用不同的中断函数,如UART_Receive_IT(huart);是接收中断

函数中间部分是一些错误判断,以及错误回调函数,这里没贴出

点击进入UART_Receive_IT函数

在这里插入图片描述

stm32f1xx_hal_uart.c——UART_Receive_IT(UART_HandleTypeDef *huart)

UART_Receive_IT函数最后会调用HAL_UART_RxCpltCallback接收中断回调函数,在调用中断回调函数之前,前面三句__HAL_UART_DISABLE_IT():作用是关闭串口接收中断,也就是说,在一次串口中断接收过程的最后,即串口接收完一组数据之后会关闭串口接收中断,然后将接收状态重新设为READY,再调用回调函数

在这里插入图片描述

stm32f1xx_hal_uart.c——HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

接收中断回调函数就是用_weak修饰的虚函数,用户可以在自己的文件中重写该函数,实现接收数据的处理

在这里插入图片描述

总结一下,串口中断接收的流程:

USART3_IRQHandler(void) -> HAL_UART_IRQHandler(&huart3) -> UART_Receive_IT(huart) -> HAL_UART_RxCpltCallback(huart);

Callback函数就是用户要重写在main.c里的回调函数。

再说明一下一个很重要的问题:STM32的每个串口中断有好几个(发送接收等),但只要是与串口相关的中断发生系统都会先调用个函数USART3_IRQHandler,也就是中断向量表中的那个,比如usart3的话就是USART3_IRQHandler(void),然后这个函数再调用
HAL_UART_IRQHandler,在HAL_UART_IRQHandler中去读取寄存器判断究竟是那几个位被置为1,确定好是哪个中断之后,(接收还是发送)再调用不同的回调函数。

如何使用接收中断

在CubeMX中配置完了之后并没有使能串口中断(有一个串口初始化函数,但是在这个函数中并未使能串口中断)需要用户手动使能。使能代码如下:

HAL_UART_Receive_IT(&huart3,(uint8_t *)kRxBuffer,10);

什么意思呢?

HAL库的串口接收思路是这样的:用户你可以随便定义一个缓存区,大小随意,然后通过上边这个函数把这个缓存区对应到串口的接收,上面函数的意思就是把kRxBuffer(这是一个数组)作为缓存区,指定大小为10。然后usart3接收数据的时候就放到kRxBuffer这个数组中,只有当接收到10个数据之后才调用一次callback函数(回调函数)。当然不要忘了该函数的使能串口接收中断功能,在介绍UART_Receive_IT(UART_HandleTypeDef *huart)函数时说到了串口接收完数据后会关闭使能,所以,在回调函数中一定要再写一次HAL_UART_Receive_IT(&huart3,(uint8 *)kRxBuffer,10),使能接收中断。

所以更具体一下串口接收的流程就是这样的:

(1)串口一个接一个的接收到数据填充到缓存区
(2)缓存区满(大小是用户定义的)程序几经辗转最后会调用到回调函数。
(3)执行用户在回调函数中实现的功能。

在这里插入图片描述

二、串口中断发送(串口3为例)

stm32f1xx.it.c——USART3_IRQHandler(void)

因为串口中断的入口函数就只有一个,就是USART3_IRQHandler(void)函数,所以这里与接收的一样,都是从这个函数进入

点击跳转到HAL_UART_IRQHandler函数的具体实现

在这里插入图片描述

stm32f1xx_hal_uart.c——HAL_UART_IRQHandler(&huart3)

该函数与接收的代码一样,函数体的前部分就是接收模式的

在这里插入图片描述

一直往下拉,到函数的最后,就可以看到发送模式了

函数的最后有发送中断函数UART_Transmit_IT(huart);以及发送结束中断函数UART_EndTransmit_IT(huart);

在这里插入图片描述

在UART_Transmit_IT函数里会判断发送的数据是否为0,为0则表示发送完成,失能UART_IT_TXE位,再使能UART_IT_TC位,所以在上一张图中就判断UART_IT_TC位,进而调用UART_EndTransmit_IT(huart);发送结束中断函数

UART_Transmit_IT(huart)

在这里插入图片描述

进入UART_EndTransmit_IT(huart)函数体内部

stm32f1xx_hal_uart.c——UART_EndTransmit_IT(huart)

第一个红线处清除了发送中断使能(同接收一样,在用完之后就关掉,但是不同于接收,发送完成就不用再在回调函数中使能了,因为在中断发送的时候就会使能)

最后会调用HAL_UART_TxCpltCallbsck(huart)中断回调函数

在这里插入图片描述

stm32f1xx_hal_uart.c——HAL_UART_TxCpltCallbsck(huart)

在这里插入图片描述

如何使用发送中断

与接收中断一样,发送也要先调用HAL_UART_Transmit_IT函数使能中断发送

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

比如有一个unsigned char数组a[10],HAL_UART_Transmit_IT(&huart3,a,10),这一句的意思是用usart3(串口3)发送a数组10个数据,然后使能发送中断。

当发送完成之后(或者发送一半,发送一半也有个中断)就会执行回调函数。

总结一下发送中断

使用HAL_UART_Transmit_IT函数发送指定长度的数据,并使能发送中断,发送到一半和发送结束会触发中断(相关的回调函数是
HAL_UART_TxHalfCpltCallback()和HAL_UART_TxCpltCcalback())中断触发后发送中断使能会被清除,然后调用回调函数,回调函数执行完成之后结束本次发送。

串口中断与DMA中断的关系

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

串口DMA发送,以DMA方式发送指定长度的数据。

过程是,把发送缓冲区指针指向要发送的数据,设置发送长度,发送计数器初值,设置 DMA传输完成中断的回调函数,使能DMA控制器中断,使能DMA控制器传输,使能UART的DMA传输请求。

然后,UART便会发送数据,直到发送完成,触发DMA中断。

DMA中断处理,如果 DMA模式是循环模式,则直接调用 DMA传输完成中断的回调函数。
如果 DMA模式是正常模式,则先关闭DMA传输完成中断,不再触发DMA中断,再调用DMA传输完成中断的回调函数。

DMA传输完成中断的回调函数处理过程,如果 DMA模式是循环模式,则直接调用串口发送完成回调函数。

如果 DMA模式是正常模式,则先关闭UART的DMA传输请求, 再使能串口传输完成中断,直到传输完成,触发中断。
串口传输完成中断处理,关闭中断,调用串口发送完成回调函数。

在这里插入图片描述

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

串口DMA接收,以DMA方式接收指定长度的数据。

过程是,把 接收缓冲区指针 指向 要存放接收数据的数组,设置接收长度,接收计数器初值,设置 DMA传输完成中断的回调函数,使能DMA控制器中断,使能DMA控制器传输,使能UART的DMA传输请求。

然后,UART接收到数据,便会通过DMA把数据存到接收缓冲区,直到接收到指定长度数据,触发DMA中断。

DMA中断处理,如果DMA模式是循环模式,则直接调用 DMA传输完成中断的回调函数。
如果 DMA模式是正常模式,则先关闭DMA传输完成中断,不再触发DMA中断,再调用DMA传输完成中断的回调函数。

DMA传输完成中断的回调函数处理过程,如果DMA模式是循环模式,则直接调用串口接收完成回调函数。
如果 DMA模式是正常模式,则先关闭UART的DMA传输请求, 再调用串口接收完成回调函数。

参考:

https://wenku.baidu.com/view/21f7886030687e21af45b307e87101f69e31fbb1.html

https://www.cnblogs.com/JYFEI/p/15996557.html

  • 9
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32 HAL库中,使用DMA实现串口数据收发也是非常简单的,只需调用相应的HAL库函数即可。以下是一个基本的示例: ```c #include "stm32f10x.h" #include "stm32f10x_hal.h" #define USART_RX_BUFFER_SIZE 256 volatile uint8_t usart_rx_buffer[USART_RX_BUFFER_SIZE]; volatile uint16_t usart_rx_write_index = 0; volatile uint16_t usart_rx_read_index = 0; UART_HandleTypeDef huart1; void usart_init(void) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; HAL_UART_Init(&huart1); HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn); HAL_UART_Receive_DMA(&huart1, (uint8_t*)usart_rx_buffer, USART_RX_BUFFER_SIZE); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { usart_rx_write_index = USART_RX_BUFFER_SIZE - huart->hdmarx->Instance->CNDTR; while (usart_rx_read_index != usart_rx_write_index) { uint8_t data = usart_rx_buffer[usart_rx_read_index]; usart_rx_read_index = (usart_rx_read_index + 1) % USART_RX_BUFFER_SIZE; // 处理接收到的数据 // ... if (usart_rx_read_index == usart_rx_write_index) { break; } } HAL_UART_Receive_DMA(&huart1, (uint8_t*)usart_rx_buffer, USART_RX_BUFFER_SIZE); } int main(void) { usart_init(); while (1) { // 主循环中不需要处理接收到的数据 } } void DMA1_Channel5_IRQHandler(void) { HAL_DMA_IRQHandler(huart1.hdmarx); } ``` 在上述代码中,我们使用了HAL库函数 `HAL_UART_Receive_DMA` 来启动USART1的DMA接收,同时使用了HAL库函数 `HAL_UART_RxCpltCallback` 来处理接收到的数据。需要注意的是,我们需要在 `HAL_UART_RxCpltCallback` 中再次调用 `HAL_UART_Receive_DMA` 来启动下一轮DMA接收。 此外,在HAL库中,我们也需要在主函数中调用 `HAL_NVIC_EnableIRQ` 函数来使能DMA中断,并且需要在中断处理函数中调用 `HAL_DMA_IRQHandler` 函数来处理DMA中断

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值