当前位置: 首页 > news >正文

串口DMA + 环形缓冲,共用缓冲区

        串口DMA + 环形缓冲,最直接的做法是DMA 一个buffer, ringbuffer再搞个buffer, 接收和发送时再把数据倒来倒去。但这样会有个问题,两个缓冲本质上是重复的,如果都很大,那就相当于浪费了一半内存!!!DMA应该是可以和ringbuffer共用一个缓冲的...

  • 串口发送时,不管DMA有没有在工作,都先将数据写入环形缓冲(避免阻塞主程序,在中断程序里面也能打印!)
  • 如果DMA空闲就用DMA发送,DMA发送完成产生中断后,如果缓冲区还有数据则继续启动DMA发送,直到发完数据。
  • 只要缓冲区合理,就可以一次性写入大量数据,DMA再自己慢慢的发送出去。
  • 串口接收时,如果有串口空闲中断就更新接收计数,如果有DMA完成中断就重新开始接收
  • DMA会自己一直往缓冲区写入数据,只要在串口空闲中断和DMA完成中断中更新接收计数即可。
  • 发送和接收过程全部由DMA完成,不占用CPU时间
/** @Author: LVGRAPE* @LastEditors: LVGRAPE*/
#include "drv_serial.h"
#include <string.h>
#include <stdbool.h>
#include "drv_pin.h"
#include "drv_led.h"#define USART1_TX_BUFFER_SIZE 1024
#define USART1_RX_BUFFER_SIZE 64uint16_t tx1_size;
volatile bool tx1_busy = 0;uint8_t usart1_tx_buffer[USART1_TX_BUFFER_SIZE];//DMA, ringbuffer 共用缓冲。 DMA 读出,ringbuffer 写入
uint8_t usart1_rx_buffer[USART1_RX_BUFFER_SIZE];//DMA, ringbuffer 共用缓冲。 DMA 写入,ringbuffer 读出
struct rt_ringbuffer rx1_ringbuffer = {.buffer_ptr = usart1_rx_buffer,.read_mirror = 0,.read_index = 0,.read_mirror = 0,.write_index = 0,.buffer_size = USART1_RX_BUFFER_SIZE
};
struct rt_ringbuffer tx1_ringbuffer = {.buffer_ptr = usart1_tx_buffer,.read_mirror = 0,.read_index = 0,.write_mirror = 0,.write_index = 0,.buffer_size = USART1_TX_BUFFER_SIZE
};/*** @brief 计算缓冲区中没有发送的数据,如果缓冲区中有数据,则启动DMA发送** @return int*/
int tx1_send_buffer(void)
{if (tx1_ringbuffer.read_index <= tx1_ringbuffer.write_index){/**缓冲未满,全部发送 *//** |---------Read----[data]-----Write-----------| */tx1_size = tx1_ringbuffer.write_index - tx1_ringbuffer.read_index;}else{/**缓冲已满,先发一部分,DMA中断接手剩余部分 *//**发送完成后,Read就回到Write左边了,也就是未满状态!环形缓冲转起来了!*//** |----[data]-----Write-----Read----[data]-----| */tx1_size = tx1_ringbuffer.buffer_size - tx1_ringbuffer.read_index;}if (tx1_size > 0){/**DMA发送 */tx1_busy = 1;DMA1_CHANNEL1->maddr = (uint32_t)tx1_ringbuffer.buffer_ptr + tx1_ringbuffer.read_index;//start address + read indexDMA1_CHANNEL1->dtcnt = tx1_size;dma_channel_enable(DMA1_CHANNEL1, TRUE);}return tx1_size;
}
/*** @brief  把数据写入到缓冲区,如果DMA没有在忙,则启动DMA发送,* DMA会连续在发送,直到缓冲区为空** @param buf* @param size* @return int*/
int uart1_write(uint8_t *buf, rt_size_t size)
{/**先把数据写入到缓冲区 */rt_ringbuffer_put(&tx1_ringbuffer, buf, size);/**如果DMA没有在忙,则启动DMA发送 */if (!tx1_busy){return tx1_send_buffer();}return 0;
}
/*** @brief  从缓冲区中读取数据** @param buf* @param size* @return int*/
int uart1_read(uint8_t *buf, rt_size_t size)
{return rt_ringbuffer_get(&rx1_ringbuffer, buf, size);
}
/*** @brief 缓冲区中有多少数据未读取** @return int16_t*/
int16_t usart1_get_rx_data_len(void)
{return rt_ringbuffer_data_len(&rx1_ringbuffer);
}/*** @brief  DMA发送完成中断。* @param  none* @retval none*/
void DMA1_Channel1_IRQHandler(void)
{rt_enter_critical();if (dma_interrupt_flag_get(DMA1_FDT1_FLAG)){tx1_busy = 0;dma_flag_clear(DMA1_FDT1_FLAG);dma_channel_enable(DMA1_CHANNEL1, FALSE);tx1_ringbuffer.read_index += tx1_size;if (tx1_ringbuffer.read_index >= tx1_ringbuffer.buffer_size){//下一圈tx1_ringbuffer.read_mirror = ~tx1_ringbuffer.read_mirror;tx1_ringbuffer.read_index -= tx1_ringbuffer.buffer_size;}/**NOTE 如果缓冲区中还有数据,继续发送 */tx1_send_buffer();}rt_exit_critical();
}/*** @brief  DMA接收完成中断* @param  none* @retval none*/
void DMA1_Channel2_IRQHandler(void)
{rt_enter_critical();if (dma_interrupt_flag_get(DMA1_FDT2_FLAG)){dma_flag_clear(DMA1_FDT2_FLAG);dma_channel_enable(DMA1_CHANNEL2, false);/**NOTE DMA不重置的话,计数就不会重置,接收内存地址也会继续增加 *//**NOTE DMA传输完成中断,说明缓冲已满,从头开始 */rx1_ringbuffer.write_index = 0;rx1_ringbuffer.write_mirror = ~rx1_ringbuffer.write_mirror;/**NOTE 重新接收 */DMA1_CHANNEL2->maddr = (uint32_t)usart1_rx_buffer;dma_data_number_set(DMA1_CHANNEL2, USART1_RX_BUFFER_SIZE);dma_channel_enable(DMA1_CHANNEL2, true);}rt_exit_critical();
}
/*** @brief  USART1 空闲中断,主要是为了避免DMA完全接收完的时间太久,* 所以使用空闲中断来及时更新接收到的数据长度**/
void USART1_IRQHandler(void)
{rt_enter_critical();if (usart_interrupt_flag_get(USART1, USART_IDLEF_FLAG)){usart_flag_clear(USART1, USART_IDLEF_FLAG);usart_data_receive(USART1);/**NOTE 使用IDLE中断来更新接收到的数据长度 *//**NOTE DMA向下计数,剩余多少即是接收了多少 *//**NOTE DMA完成中断会必须要优先于空闲中断,并先将DMA计数重置 *//**NOTE |MAX------[DATA]---DMA_CNT-------0| *//**NOTE |0--------[DATA]---Write-------MAX| */uint16_t rxCnt = USART1_RX_BUFFER_SIZE - dma_data_number_get(DMA1_CHANNEL2);rx1_ringbuffer.write_index = rxCnt;}rt_exit_critical();
}int rt_hw_console_init(void)
{rt_hw_uart1_init();return 0;
}void rt_hw_console_output(const char *str)
{uint16_t len = rt_strlen(str);uart1_write((uint8_t *)str, len);
}
int rt_hw_console_getchar(void)
{rt_uint8_t ch = -1;if (rt_ringbuffer_getchar(&rx1_ringbuffer, &ch) == 1)return ch;return -1;
}
/*** @brief  config usart* @param  none* @retval none*/
void usart_configuration(void)
{gpio_init_type gpio_init_struct;/* enable the usart1 and gpio clock */crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE);crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);gpio_default_para_init(&gpio_init_struct);/* configure the usart2 tx pin */gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;gpio_init_struct.gpio_mode = GPIO_MODE_MUX;gpio_init_struct.gpio_pins = GPIO_PINS_9;gpio_init_struct.gpio_pull = GPIO_PULL_NONE;gpio_init(GPIOA, &gpio_init_struct);/* configure the usart2 rx pin */gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;gpio_init_struct.gpio_pins = GPIO_PINS_10;gpio_init_struct.gpio_pull = GPIO_PULL_UP;gpio_init(GPIOA, &gpio_init_struct);/* configure usart2 param */usart_init(USART1, 921600, USART_DATA_8BITS, USART_STOP_1_BIT);usart_transmitter_enable(USART1, TRUE);usart_receiver_enable(USART1, TRUE);usart_dma_transmitter_enable(USART1, TRUE);usart_dma_receiver_enable(USART1, TRUE);usart_interrupt_enable(USART1, USART_IDLE_INT, TRUE);nvic_irq_enable(USART1_IRQn, 1, 1);usart_enable(USART1, TRUE);
}/*** @brief  config dma for usart2 and usart3* @param  none* @retval none*/
void dma_configuration(void)
{dma_init_type dma_init_struct;/* enable dma1 clock */crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);/* dma1 channel1 for usart1 tx configuration */dma_reset(DMA1_CHANNEL1);dma_default_para_init(&dma_init_struct);dma_init_struct.buffer_size = USART1_TX_BUFFER_SIZE;dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;dma_init_struct.memory_base_addr = (uint32_t)usart1_tx_buffer;dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;dma_init_struct.memory_inc_enable = TRUE;dma_init_struct.peripheral_base_addr = (uint32_t)&USART1->dt;dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;dma_init_struct.peripheral_inc_enable = FALSE;dma_init_struct.priority = DMA_PRIORITY_MEDIUM;dma_init_struct.loop_mode_enable = FALSE;dma_init(DMA1_CHANNEL1, &dma_init_struct);/* enable transfer full data interrupt */dma_interrupt_enable(DMA1_CHANNEL1, DMA_FDT_INT, TRUE);/* dma1 channel1 interrupt nvic init */nvic_irq_enable(DMA1_Channel1_IRQn, 0, 0);/* config flexible dma for usart2 tx */dma_flexible_config(DMA1, FLEX_CHANNEL1, DMA_FLEXIBLE_UART1_TX);/* dma1 channel2 for usart1 rx configuration */dma_reset(DMA1_CHANNEL2);dma_default_para_init(&dma_init_struct);dma_init_struct.buffer_size = USART1_RX_BUFFER_SIZE;dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;dma_init_struct.memory_base_addr = (uint32_t)usart1_rx_buffer;dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;dma_init_struct.memory_inc_enable = TRUE;dma_init_struct.peripheral_base_addr = (uint32_t)&USART1->dt;dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;dma_init_struct.peripheral_inc_enable = FALSE;dma_init_struct.priority = DMA_PRIORITY_MEDIUM;dma_init_struct.loop_mode_enable = FALSE;dma_init(DMA1_CHANNEL2, &dma_init_struct);/* enable transfer full data interrupt */dma_interrupt_enable(DMA1_CHANNEL2, DMA_FDT_INT, TRUE);/* dma1 channel2 interrupt nvic init */nvic_irq_enable(DMA1_Channel2_IRQn, 0, 0);/* config flexible dma for usart2 rx */dma_flexible_config(DMA1, FLEX_CHANNEL2, DMA_FLEXIBLE_UART1_RX);dma_channel_enable(DMA1_CHANNEL2, TRUE); /* usart1 rx begin dma receiving */
}
void rt_hw_uart1_init(void)
{usart_configuration();dma_configuration();led_pins_init();
}

相关文章:

  • 使用Trace分析Android方法用时
  • Pyenv 跟 Conda 还有 Poetry 有什么区别?各有什么不同?
  • 在使用 HTML5 的 <video> 标签嵌入视频时,有时会遇到无法播放 MP4 文件的问题
  • python打卡day54@浙大疏锦行
  • 黑马教程强化day5-2
  • 【Node.js 的底层实现机制】从事件驱动到异步 I/O
  • 什么是Flink
  • 红队攻防渗透技术实战流程:信息打点-主机架构蜜罐识别WAF识别端口扫描协议识别服务安全
  • Uniapp H5端SEO优化全攻略:提升搜索引擎排名与流量
  • JVM 内存模型与垃圾回收机制全解析:架构、算法、调优实践
  • Minio 基于 bearer_token 监控
  • 【AI作画】用comfy ui生成漫画风图画
  • python调用 powershell 执行dir 并获取每行的length列属性值
  • 【数据分析九:Association Rule】关联分析
  • 【前端AI实践】DeepSeek:开源大模型的使用让开发过程不再抓头发
  • 打造高效工作环境:技术方案助力文件整理提速
  • C++ 进阶:深入理解虚函数、继承与多态
  • Java项目:基于SSM框架实现的学生二手书籍交易平台管理系统【ssm+B/S架构+源码+数据库+毕业论文+答辩PPT+任务书+开题】
  • 使用Blender调整unity3d中的Fbx模型
  • Electron(01)
  • 在网站做登记表备案 如果修改/女生学网络营销这个专业好吗
  • 网站302跳转/国外网页模板
  • wordpress 图片库/什么是搜索引擎优化
  • 上海做兼职哪个网站靠谱/网络营销师培训费用是多少
  • 母婴类网站怎么建设/南京网站设计优化公司
  • 怎么做中英文版网站/网站推广的方式有哪些?