STM32使用DMA接收和发送
平台:正点原子,F103ZET
本文阅读前提知识:非零基础教程,需要会cubeMX,有点灯基础,有printf重定向基础,有ITM基础。
设计思路:
1、UART中断:开启TC(传输完成)中断和IDLE(总线空闲)中断;
2、DMA中断:收发中断均不开启。
接收端设计思路:
初始化完成后,就开启DMA_RX,开启IDLE中断;
开启后CPU不用理会,DMA会在搬运串口数据到指定内存。
当收到一帧数据后(以停止位为标记),进入idle中断,在中断函数内部,清掉中断位,停止DMA收,重启DMA收。
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
开启后,从串口搬运数据到pData,一个byte一个byte的搬运,累计搬运Size后不工作。
由于我们是被动接受,远端数据会有多少个不知道,那么则按照一帧数据来处理,即IDLE中断处理。
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
搬运内存思路和接收一样。
由于我们是主动发送,知道有多少个数据,那么就用TC中断,即发送完成中断来处理最合适。
HAL_UART_DMAStop(huart)
如果收发都在进行(未达到size),则一旦调用这个函数,收发都会被停止。
因为我不建议修改库函数,所以建议对其进行自定义修改;把停止收和停止发分开。
所以我这里把他改写成void StopDMA(UART_HandleTypeDef *huart,uart_pin_t uart_pin) ;
这样使得我们可以控制停止TX或者RX。
下面是部分代码,我用cubemx生成后,就只修改了部分内容,其余部分保留原始。
在初始化的地方,开启中断,开启接收。
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//如果这里不清,程序一启动就会进入一次中断。
__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE);
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&huart1,UART_IT_TC);
if(HAL_UART_Receive_DMA(&huart1,RX_buff,sizeof(RX_buff)))
{
printf("DMA FAIL!\n");
}
在中断中,设置跳转到回调函数
void USART1_IRQHandler(void)
{
HAL_UART_RxCpltCallback(&huart1);
return;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
int i=0;
int RX_count = sizeof(RX_buff)-(int)__HAL_DMA_GET_COUNTER(huart->hdmarx);
/*用于接收IDLE中断*/
if(__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE))
{
//__HAL_UART_CLEAR_IDLEFLAG(huart);
__HAL_UART_CLEAR_FLAG(huart,UART_FLAG_IDLE);
StopDMA(huart,uart_pin_rx);
// HAL_UART_DMAStop(huart);
printf("DMA RECEICED DATA : ");
for(i=0;
iInstance->CR3, USART_CR3_DMAT);
if ((huart->gState == HAL_UART_STATE_BUSY_TX) && dmarequest)
{
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);
/* Abort the UART DMA Tx channel */
if (huart->hdmatx != NULL)
{
HAL_DMA_Abort(huart->hdmatx);
}
CLEAR_BIT(huart->Instance->CR1, (USART_CR1_TXEIE | USART_CR1_TCIE));
/* At end of Tx process, restore huart->gState to Ready */
huart->gState = HAL_UART_STATE_READY;
}
}
if(uart_pin_rx==uart_pin)
{
dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
if ((huart->RxState == HAL_UART_STATE_BUSY_RX) && dmarequest)
{
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
/* Abort the UART DMA Rx channel */
if (huart->hdmarx != NULL)
{
HAL_DMA_Abort(huart->hdmarx);
}
CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* At end of Rx process, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
}
}}
完整代码我传到码云了,有兴趣可以瞅瞅,提交信息是我自己看的,请忽略:
https://gitee.com/xixihaha_is_forbiden/Mycode/tree/master/STM32_YJP_DEMO/8_UART_DMA_RX-IDLE_TX-TC/cubeMX_TEST
注意写代码一定要看手册,HAL库封装的实在太多,导致你不知道HAL到底在干什么
一定要:看到HAL的底层,动了什么寄存器,这些寄存器这样设置有什么用!
比如IDLE在手册中的描述:
IDLE:监测到总线空闲 (IDLE line detected) 位4【STM32使用DMA接收和发送】那么我们要做的:
当检测到总线空闲时,该位被硬件置位。如果USART_CR1中的IDLEIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后读USART_DR)。
0:没有检测到空闲总线; 1:检测到空闲总线。 注意:IDLE位不会再次被置高直到RXNE位被置起(即又检测到一次空闲总线)
清掉上一次的SR数据
__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE);
设置CR1中的enable
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
推荐阅读
- 由浅入深理解AOP
- 【译】20个更有效地使用谷歌搜索的技巧
- mybatisplus如何在xml的连表查询中使用queryWrapper
- MybatisPlus|MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决
- MybatisPlus使用queryWrapper如何实现复杂查询
- iOS中的Block
- Linux下面如何查看tomcat已经使用多少线程
- 使用composer自动加载类文件
- android|android studio中ndk的使用
- 使用协程爬取网页,计算网页数据大小