因为这个bug,搞了一整天。串口1接收,使用DMA,如果配置为 NORMAL 模式,只能接收一次,后面数据不变。如果使用 CIRCLAR 模式,能够连续接收,但是不正确。
结合多方博客,终于解决问题,稳如老狗。
使用 CIRCLAR 模式,中断配置里解锁等操作,附上文件供参考,实现串口1 DMA收发传输。
#include "debug.h"
/*
* 外部发来的期望位置
*/
#define EX_DATA_LEN 32
struct XYZ_t des_pos_ = {0,0,0};
uint8_t get_cmd = 0; // 0:处理完数据等待获取数据 1:已获取数据待处理
uint8_t pos_vel_buf[EX_DATA_LEN] = {'\0'};
/**************************************************************************************************************/
/***************************************** PART1:串口接收底层配置 *****************************************/
UART_HandleTypeDef usart1; //UART句柄
DMA_HandleTypeDef usart1_tx_dma; //DMA句柄
DMA_HandleTypeDef usart1_rx_dma;
/*
* 初始化IO 使用串口1
* bound:波特率
*/
void DebugInit(uint32_t bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_Initure;
DEBUG_ENCLK(); // 使能时钟
GPIO_Initure.Pin = DEBUG_PIN; // Pin
GPIO_Initure.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_Initure.Pull = GPIO_PULLUP; // 上拉
GPIO_Initure.Speed = GPIO_SPEED_FAST; // 高速
GPIO_Initure.Alternate = GPIO_AF7_USART1; // 复用为USART1
HAL_GPIO_Init(DEBUG_GPIO,&GPIO_Initure); // 初始化
// UART1 TX DMA配置--DMA2 Stream7 Channel4
__HAL_RCC_DMA2_CLK_ENABLE(); //DMA2时钟使能
__HAL_LINKDMA(&usart1,hdmatx,usart1_tx_dma); //将DMA与USART1联系起来(发送DMA)
usart1_tx_dma.Instance = DMA2_Stream7; //数据流选择
usart1_tx_dma.Init.Channel = DMA_CHANNEL_4; //通道选择
usart1_tx_dma.Init.Direction = DMA_MEMORY_TO_PERIPH; //存储器到外设
usart1_tx_dma.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
usart1_tx_dma.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
usart1_tx_dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //外设数据长度:8位
usart1_tx_dma.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; //存储器数据长度:8位
usart1_tx_dma.Init.Mode = DMA_NORMAL; //外设普通模式
usart1_tx_dma.Init.Priority = DMA_PRIORITY_MEDIUM; //中等优先级
usart1_tx_dma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
usart1_tx_dma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
usart1_tx_dma.Init.MemBurst = DMA_MBURST_SINGLE; //存储器突发单次传输
usart1_tx_dma.Init.PeriphBurst = DMA_PBURST_SINGLE; //外设突发单次传输
HAL_DMA_DeInit(&usart1_tx_dma);
HAL_DMA_Init(&usart1_tx_dma);
// UAR1 RX DMA
usart1_rx_dma.Instance = DMA2_Stream2;
usart1_rx_dma.Init.Channel = DMA_CHANNEL_4;
usart1_rx_dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
usart1_rx_dma.Init.PeriphInc = DMA_PINC_DISABLE;
usart1_rx_dma.Init.MemInc = DMA_MINC_ENABLE;
usart1_rx_dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
usart1_rx_dma.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
usart1_rx_dma.Init.Mode = DMA_CIRCULAR;
usart1_rx_dma.Init.Priority = DMA_PRIORITY_LOW;
usart1_rx_dma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_DeInit(&usart1_rx_dma);
HAL_DMA_Init(&usart1_rx_dma);
__HAL_LINKDMA(&usart1,hdmarx,usart1_rx_dma);
//UART 初始化设置
usart1.Instance = USART1; // USART1
usart1.Init.BaudRate = bound; // 波特率
usart1.Init.WordLength = UART_WORDLENGTH_8B; // 字长为8位数据格式
usart1.Init.StopBits = UART_STOPBITS_1; // 一个停止位
usart1.Init.Parity = UART_PARITY_NONE; // 无奇偶校验位
usart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控
usart1.Init.Mode = UART_MODE_TX_RX; // 收发模式
usart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&usart1); // HAL_UART_Init()会使能UART1
HAL_NVIC_EnableIRQ(USART1_IRQn); // 使能USART1中断通道
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); // 优先级
printf("Usart1 init success\r\n");
}
/*
* USART1 DMA 中断函数
*/
void DMA2_Stream2_IRQHandler(void)
{
HAL_DMA_IRQHandler(&usart1_rx_dma);
}
/*
* 串口1中断服务函数
*/
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&usart1);
// 不加这几句 USART1 DMA 有问题。如果 NORMAL模式只能接收一次,CIRCLAR 模式数据不正确
usart1.State = HAL_UART_STATE_READY;
usart1_rx_dma.State = HAL_DMA_STATE_READY;
__HAL_UNLOCK(&usart1_rx_dma);
if((__HAL_UART_GET_FLAG(&usart1,UART_FLAG_IDLE) != RESET)) // 如果线路空闲(接收完一帧)
{
__HAL_UART_CLEAR_IDLEFLAG(&usart1); // 清除线路空闲标志
get_cmd = 1; // 获取数据成功
}
}
/*
* 连续打印数据任务
* 调试使用,很多数据定义为使用,不必纠结
*/
void PrintDataTask(void *arg)
{
printf("All the bugs will be killed\r\n");
DelayMs(20);
}
void GetDesirePosVelTask(void *arg)
{
uint16_t len;
memset(pos_vel_buf, '\0', EX_DATA_LEN);
__HAL_UART_ENABLE_IT(&usart1, UART_IT_IDLE); // 开启串口空闲中断
HAL_UART_Receive_DMA(&usart1, (uint8_t*)pos_vel_buf, EX_DATA_LEN);
while (1)
{
if (get_cmd == 1)
{
get_cmd = 0;
des_pos_.x = (float)Str2Double(&pos_vel_buf[2], 3) * 0.1f;
des_pos_.y = (float)Str2Double(&pos_vel_buf[5], 3) * 0.1f;
des_pos_.z = (float)Str2Double(&pos_vel_buf[8], 3) * 0.1f;
printf("recv: %s, %.1f, %.1f, %.1f", pos_vel_buf, des_pos_.x, des_pos_.y, des_pos_.z); // 启动接收
memset(pos_vel_buf, '\0', EX_DATA_LEN);
DelayMs(10);
HAL_UART_Receive_DMA(&usart1, (uint8_t*)pos_vel_buf, EX_DATA_LEN);
}
else
{
DelayMs(10);
}
}
}
struct XYZ_t GetExDesirePosApi(void)
{
return des_pos_;
}
/********************************************************************/
/* 一些暂时不用关心的函数 */
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
//注意,读取USARTx->SR能避免莫名其妙的错误
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (uint8_t) ch;
return ch;
}
#endif