cubemx f103c8t6 串口一 DMA 简单配置和实现
1、基础配置
2、串口配置
生成代码。
3、代码部分
这样就是dma接收了。会将接收到的信息原封不动发回串口去。
4、printf重定向到dma上(拓展)
在上面代码的基础上,仅仅在usart.c文件里面添加以下ai写的部分就可以了。别的地方包含stdio.h头文件用printf就是通过dma发送了。
/* USER CODE BEGIN 1 */
// 新增:计算环形缓冲区下一个索引(静态函数,仅usart.c内调用)
static inline uint16_t uart_tx_next(uint16_t idx)
{return (idx + 1) % UART_TX_DMA_BUFFER_SIZE; // 环形溢出时回到0
}// 新增:启动DMA发送(从环形缓冲区取数据,触发DMA传输)
static void uart_start_dma_transmit(void)
{// 1. 检查UART是否就绪(避免重复触发DMA)if (huart1.gState != HAL_UART_STATE_READY)return;// 2. 读取当前读写索引(用临时变量避免中断/主程序竞争)uint16_t head = uart_tx_head;uint16_t tail = uart_tx_tail;// 3. 缓冲区为空,无需发送if (head == tail)return;// 4. 计算本次发送长度(环形缓冲区分“非跨区”和“跨区”两种情况)uint16_t send_len = (head > tail) ? (head - tail) : (UART_TX_DMA_BUFFER_SIZE - tail);// 5. 触发DMA发送(用你已配置的hdma_usart1_tx通道)HAL_UART_Transmit_DMA(&huart1, &uart_tx_dma_buffer[tail], send_len);
}// 新增:重定向printf到DMA发送(标准库fputc函数实现)
int fputc(int ch, FILE *f)
{// 1. 计算下一个“写索引”,检查缓冲区是否已满uint16_t next_head = uart_tx_next(uart_tx_head);while (next_head == uart_tx_tail) // 缓冲区满时,等待DMA腾出空间{__WFI(); // 进入低功耗睡眠(降低CPU占用,比忙等待更优)}// 2. 将printf的字符存入环形缓冲区uart_tx_dma_buffer[uart_tx_head] = (uint8_t)ch;uart_tx_head = next_head; // 更新“写索引”// 3. 启动DMA发送(若当前无DMA传输,会立即触发)uart_start_dma_transmit();return ch; // 标准库要求返回写入的字符
}// 新增:DMA发送完成回调函数(HAL库弱函数,覆盖实现)
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{// 仅处理USART1的DMA发送完成事件(避免影响其他UART)if (huart->Instance != USART1)return;// 1. 关闭DMA(避免重复触发)__HAL_DMA_DISABLE(huart->hdmatx);// 2. 更新“读索引”(已发送的数据从缓冲区中移除)uart_tx_tail = (uart_tx_tail + huart->TxXferCount) % UART_TX_DMA_BUFFER_SIZE;// 3. 清除DMA完成标志(防止残留标志导致异常)__HAL_DMA_CLEAR_FLAG(huart->hdmatx, __HAL_DMA_GET_TC_FLAG_INDEX(huart->hdmatx)); // 传输完成标志__HAL_DMA_CLEAR_FLAG(huart->hdmatx, __HAL_DMA_GET_HT_FLAG_INDEX(huart->hdmatx)); // 半传输标志// 4. 检查缓冲区是否还有剩余数据,继续发送(实现“连续DMA发送”)uart_start_dma_transmit();
}
/* USER CODE END 1 */
5、测试代码(一行)和测试现象:
效果还可以,个别来不及换行,但是串口一直开着的话发送是不会停顿的。