You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
7.7 KiB
227 lines
7.7 KiB
2 years ago
|
## DMA
|
||
|
F103ZET usart2 DMA
|
||
|
|
||
|
|
||
|
***RX引脚配置为上拉 Pull-up***
|
||
|
|
||
|
### CubeMX配置DMA
|
||
|
#### dma.h dma.c 初始化
|
||
|
```C
|
||
|
void MX_DMA_Init(void)
|
||
|
{
|
||
|
|
||
|
/* DMA controller clock enable */
|
||
|
__HAL_RCC_DMA1_CLK_ENABLE();
|
||
|
|
||
|
/* DMA interrupt init */
|
||
|
/* DMA1_Channel6_IRQn interrupt configuration */
|
||
|
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 2, 2);
|
||
|
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
|
||
|
|
||
|
/* DMA1_Channel7_IRQn interrupt configuration */
|
||
|
HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 2, 2);
|
||
|
HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
|
||
|
|
||
|
}
|
||
|
|
||
|
```
|
||
|
|
||
|
#### usart.c
|
||
|
定义DMA 结构体
|
||
|
DMA_HandleTypeDef hdma_usart2_rx;
|
||
|
DMA_HandleTypeDef hdma_usart2_tx;
|
||
|
|
||
|
|
||
|
对应串口 HAL_UART_MspInit
|
||
|
```c
|
||
|
/* USART2 DMA Init */
|
||
|
/* USART2_RX Init */
|
||
|
hdma_usart2_rx.Instance = DMA1_Channel6;
|
||
|
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||
|
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
|
||
|
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
|
||
|
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
|
||
|
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
|
||
|
hdma_usart2_rx.Init.Mode = DMA_NORMAL;
|
||
|
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
|
||
|
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
|
||
|
{
|
||
|
Error_Handler();
|
||
|
}
|
||
|
|
||
|
__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);
|
||
|
|
||
|
/* USART2_TX Init */
|
||
|
hdma_usart2_tx.Instance = DMA1_Channel7;
|
||
|
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
|
||
|
hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
|
||
|
hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
|
||
|
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
|
||
|
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
|
||
|
hdma_usart2_tx.Init.Mode = DMA_NORMAL;
|
||
|
hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;
|
||
|
if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
|
||
|
{
|
||
|
Error_Handler();
|
||
|
}
|
||
|
|
||
|
__HAL_LINKDMA(uartHandle,hdmatx,hdma_usart2_tx);
|
||
|
|
||
|
/* USART2 interrupt Init */
|
||
|
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
|
||
|
HAL_NVIC_EnableIRQ(USART2_IRQn);
|
||
|
```
|
||
|
|
||
|
对应的 HAL_UART_MspDeInit
|
||
|
HAL_DMA_DeInit(uartHandle->hdmarx);
|
||
|
HAL_DMA_DeInit(uartHandle->hdmatx);
|
||
|
|
||
|
### stm32f1xx_it.h .c
|
||
|
extern DMA_HandleTypeDef 引入DMA定义
|
||
|
void DMA1_Channel6_IRQHandler(void);
|
||
|
void DMA1_Channel7_IRQHandler(void);
|
||
|
|
||
|
void DMA1_Channel6_IRQHandler(void)
|
||
|
{
|
||
|
HAL_DMA_IRQHandler(&hdma_usart2_rx);
|
||
|
}
|
||
|
|
||
|
|
||
|
void DMA1_Channel7_IRQHandler(void)
|
||
|
{
|
||
|
HAL_DMA_IRQHandler(&hdma_usart2_tx);
|
||
|
}
|
||
|
|
||
|
|
||
|
### main.h main.c
|
||
|
#include "dma.h"
|
||
|
MX_DMA_Init(); 在串口之前初始化,否则不能输出
|
||
|
|
||
|
|
||
|
### usart dma 发送接收
|
||
|
HAL_UART_Transmit_DMA();串口DMA模式发送
|
||
|
HAL_UART_Transmit_DMA();串口DMA模式接收
|
||
|
|
||
|
HAL_UART_Receive_DMA(&huart2, Rxbuf, sizeof(Rxbuf));
|
||
|
UART一旦开启DMA之后,DMA收发中断都是强制开启的,所以DMA收发函数也可以编写回调函数。
|
||
|
|
||
|
```c
|
||
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
||
|
{
|
||
|
if(huart->Instance==USART2)
|
||
|
{
|
||
|
HAL_UART_Transmit_DMA(&huart2, ACKbuf, sizeof(ACKbuf));
|
||
|
// HAL_UART_Receive_IT(&huart2, Rxbuf, sizeof(Rxbuf)); // 重新使用串口接收中断
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### DMA 相关函数
|
||
|
__HAL_DMA_GET_COUNTER 获取DMA剩余未接收数据
|
||
|
|
||
|
HAL_UART_Transmit 串口阻塞方式发送函数
|
||
|
|
||
|
HAL_UART_Transmit_IT 串口中断方式发送函数
|
||
|
|
||
|
HAL_UART_Receive_IT 串口中断方式接收数据
|
||
|
|
||
|
HAL_UART_Transmit_DMA 串口DMA方式发送函数
|
||
|
|
||
|
HAL_UART_Receive_DMA 串口DMA方式接收函数
|
||
|
|
||
|
HAL_UART_TxCpltCallback 串口发送完成回调函数
|
||
|
|
||
|
HAL_UART_RxCpltCallback 串口接收完成回调函数-- 和中断模式函数一样
|
||
|
|
||
|
HAL_UART_RxHalfCpltCallback 串口接收过半回调函数
|
||
|
|
||
|
### DMA 模式
|
||
|
普通模式:当传输数据量为0时,需要程序中手动将传输数据量重新设置满才能开启下一次的DMA数据传输。
|
||
|
循环模式:则当传输数据量为0时,会自动将传输数据量设置为满的,这样数据就能不断的传输。双缓冲
|
||
|
|
||
|
|
||
|
但有时为了安全,当我们设置的DMA缓冲区大小大于一次完整的数据时,当接收完一次数据后,Counter值还没有变为0,这时不会自动指向另一个缓冲区,所以这时候我们就需要手动更改指向下一个缓冲区。(也就是当第一帧数据传输完成后,Counter值不会自动填满,且内存区域还是指向Memory0,然后我们将剩余数据量保存下来,再将Counter值填满,接着把DMA指向Memory1,最后通过判断剩余数据量来决定是否对数据进行处理)
|
||
|
|
||
|
双缓冲的数据存储格式
|
||
|
uint8_t sbus_rx_buffer[2][18u];
|
||
|
DMA_InitStruct.DMA_BufferSize = 18;
|
||
|
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)&sbus_rx_buffer[0][0];
|
||
|
DMA_DoubleBufferModeConfig(DMA1_Stream1,(uint32_t)&sbus_rx_buffer[1][0],DMA_Memory_0); //first used memory configuration
|
||
|
DMA_DoubleBufferModeCmd(DMA1_Stream1, ENABLE);
|
||
|
|
||
|
|
||
|
空闲中断计算 长度,及中间数据处理方法参考
|
||
|
```c
|
||
|
uint32_t temp;
|
||
|
if(USART1 == huart1.Instance)//判断是否为串口1中断
|
||
|
{
|
||
|
if(RESET != __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))//如果为串口1空闲
|
||
|
{
|
||
|
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除中断标志
|
||
|
HAL_UART_DMAStop(&huart1);//停止DMA接收
|
||
|
temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);//获取DMA当前还有多少未填充
|
||
|
Rx_len_1 = BUFFERSIZE_1 - temp; //计算串口接收到的数据个数
|
||
|
HAL_UART_Transmit_DMA(&huart1,ReceiveBuff_1,Rx_len_1);//发送数据
|
||
|
Rx_len_1=0;//接收数据长度清零
|
||
|
HAL_UART_Receive_DMA(&huart1,ReceiveBuff_1,BUFFERSIZE_1);//开启下一次接收
|
||
|
}
|
||
|
}
|
||
|
|
||
|
```
|
||
|
|
||
|
#### DMA 调试
|
||
|
监控半满,满状态。监控满后,指针是否重回开头接收
|
||
|
可以串口监控
|
||
|
huart.Init.BaudRate
|
||
|
huart->RxXferSize 为Rx需要接收的大小
|
||
|
__HAL_DMA_GET_COUNTER(huart2.hdmarx) 可以获取剩余的空间
|
||
|
|
||
|
const uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
|
||
|
uint16_t TxXferSize; /*!< UART Tx Transfer size */
|
||
|
__IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
|
||
|
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
|
||
|
uint16_t RxXferSize; /*!< UART Rx Transfer size */
|
||
|
__IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
|
||
|
|
||
|
|
||
|
__HAL_DMA_GET_COUNTER(huart2.hdmarx) 可以获取剩余的空间
|
||
|
获得 dma-init-CNDTR值
|
||
|
|
||
|
定义接收数据的结构
|
||
|
```C
|
||
|
typedef struct
|
||
|
{
|
||
|
uint8_t RxBuffer[RCV_BUF_LENGTH]; /* 接收缓冲区 */
|
||
|
uint16_t RxEndIndex; /* 尾索引值 */
|
||
|
FlagStatus RxEndFlag ; /* 接收结束标识 */
|
||
|
}Usart_BufTypeDef;
|
||
|
```
|
||
|
|
||
|
半满中断标志:
|
||
|
标准库 :DMA_ITConfig(DMA1_Channel4, DMA_IT_HT, ENABLE);
|
||
|
HAL库: __HAL_UART_ENABLE_IT(&huart2, DMA_IT_HT);
|
||
|
可以查DMA中断位置,中断函数要清除半满标记
|
||
|
|
||
|
开启半满中断后,可以看到半满的LED 翻转
|
||
|
如何解决 后续出现的错误?? 获得空余数
|
||
|
|
||
|
// __HAL_DMA_CLEAR_FLAG(&hdma_usart2_rx, DMA_IT_HT);
|
||
|
__HAL_DMA_ENABLE_IT(&hdma_usart2_rx, DMA_IT_HT );
|
||
|
__HAL_UART_CLEAR_FLAG(&huart2, DMA_IT_HT);
|
||
|
|
||
|
解决串口发送DMA只能发送一次的问题
|
||
|
注意,在DMA传输过程中,会将串口从READY状态改为发送BUSY状态(软件修改):huart->gState = HAL_UART_STATE_BUSY_TX;但是在DMA完成本次传输工作以后,并没有将串口从发送BUSY状态改回READY状态
|
||
|
|
||
|
DMA 中断函数可以如下处理一些错误
|
||
|
|
||
|
hdma->XferHalfCpltCallback
|
||
|
|
||
|
if (hdma->XferErrorCallback != NULL)
|
||
|
{
|
||
|
/* Transfer error callback */
|
||
|
hdma->XferErrorCallback(hdma);
|
||
|
}
|
||
|
|
||
|
|
||
|
### 思路:
|
||
|
DMA 采集, 采集立刻复制数据, 轮询处理数据, 判断是否收集完成
|