-
利用串口传输结构体数据
2021-01-06 09:23:20结构体是一种数据的归类方式,相比数组或变量更具有整体全面性,例如一个数组只可以放一些按照元素顺序存放的单元变量,即 buffer = {x, x, x, x, x…},i 有多大,数组内元素就有多少。那么我们这时候如果我们用这... -
stm32串口传输数据第一个数据被吞_STM32 DMA串口发送模式配置及使用简单分享
2020-12-28 12:07:05我为什么要建这个帖子呢,因为我不想看到还有其他人跟我一样,为了调通DMA串口,而花上大半天的时间,这很不利于大家高效率的开发,网上的经验都是从头教到尾的,内容极多,对于有点小经验的人,或者是想很快达到...赠人玫瑰,手有余香;很感谢网上其他楼主的分享;我这个贴子是关于STM32 DMA USART 发送模式的调试分享,我为什么要建这个帖子呢,因为我不想看到还有其他人跟我一样,为了调通DMA串口,而花上大半天的时间,这很不利于大家高效率的开发,网上的经验都是从头教到尾的,内容极多,对于有点小经验的人,或者是想很快达到目的的人,这个很不适合他们;我的这个很简单(本帖不适合不熟悉配置STM32串口的玩家),他只是说串口的DMA怎么配置,还有我用的是DMA1_通道4,因为我的是串口1的TX长话短说:直接po代码:
DMA_InitTypeDef DMA_InitStruct; //DMA类型声明 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); /打开RCC时钟, DMA_InitStruct.DMA_PeripheralBaseAddr= USART1_BASE+0x4; //串口外设基础地址,好像只能是这个数值,至于为啥是0x4,我还没找到相关资料 DMA_InitStruct.DMA_MemoryBaseAddr= (u32)TEST;//这个我是一个字符串数组的首字母也就是u8 TEST[] ="" DMA_InitStruct.DMA_DIR= DMA_DIR_PeripheralDST;//这个是发送模式 DMA_InitStruct.DMA_BufferSize=0; //缓冲大小,实际我们使用可以改变,现在可以随便填 DMA_InitStruct.DMA_PeripheralInc= DMA_PeripheralInc_Disable ;//这个是外设偏移,是外设的地址偏移 DMA_InitStruct.DMA_MemoryInc= DMA_MemoryInc_Enable;//内存偏移,是保存在内存中的数据偏移 DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_MemoryDataSize= DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_Mode= DMA_Mode_Normal;//因为不需要循环发送所以使用这个模式就好 DMA_InitStruct.DMA_Priority=DMA_Priority_High;//优先级,有最高,高,中,低的哥档位 DMA_InitStruct.DMA_M2M= DMA_M2M_Disable; //关闭就好 DMA_Init(DMA1_Channel4, & DMA_InitStruct); DMA_Cmd(DMA1_Channel4, DISABLE); //先关闭,要使用的时候才使能 USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);//串口DMA使能,在串口的.h文档中可以找到。
上面是我的DMA配置,接下来是实际在主函数中是怎么使用的:
DMA_Cmd(DMA1_Channel4, DISABLE); //首先要关闭使能; DMA1_Channel4->CNDTR =13; //给这个寄存器设定要发送的缓存大小,这个就是之前DMA配置的DMA_InitStruct.DMA_BufferSize=0;具体语句可以在dma.c中可以找到; DMA_Cmd(DMA1_Channel4, ENABLE);//设定好了缓冲长度之后使能DMA,这个时候串口就会自动发送 DMA_InitStruct.DMA_MemoryBaseAddr=&TEST这个地址的内容; while(!DMA_GetFlagStatus(DMA1_FLAG_TC4)) ;//等待完成标志 DMA_CleaRFlag(DMA1_FLAG_TC4); //清除完成标志
可以我的代码就这两部分,最后说一说我花了大半天才得到的调试记录:
1)使用DMA串口发送和单纯的串口发送方法不一样,传统的是printf(),DMA就是上面我的第二段代码的操作;
2)DMA_PeripheralInc:要DIsable,因为这个是设定外设指向下个地址,而不是数据下个地址,设为Enable的话,系统会死机;
3)DMA_MemoryInc:要Enable,这个才是移动到下个字节的标志;不然的话,即使串口通了,他只会发送第一个字节的内容,而不是发送你整个字符串;通过这个设定我想到了,说明数据是会存入内存中的;
4)DMA_BufferSize:会自动识别,填写0也没问题;因为实际缓存多大取决于你实际使用时DMA1_Channel4->CNDTR的设定;
5)USART_DMACmd():这个函数记得要打开,他在串口的.h文档中;
6使用的方法就是我的第二段代码,先关闭DMA使能,再设定缓冲大小,再开启DMA使能;
相信这些点你都注意了,串口就可以正常发送数据啦;希望能够帮助你。
DMA的相关资料供大家参考
- 深度剖析STM32:DMA专题讲解 -
- 深度剖析STM32第四讲:DMA专题讲解 -
stm32 如何用DMA搬运数据
- 通信协议 - UART串口协议 -
- STM32 USART串口的应用 -
-
串口数据使用DMA通道传输
2020-07-29 12:04:58第一次写 希望各位博友指正 本次使用的是STM32F1的主控芯片,资料可在ST官网下载 DMA使用的目的:不用DMA发送...本次为了减少CPU的占用,在配置串口的时候使用DMA进行数据的收发。 不多说 直接上干货!!! 本次使用的第一次写 希望各位博友指正
本次使用的是STM32F1的主控芯片,资料可在ST官网下载
DMA使用的目的:不用DMA发送是需要单片机实时参du与,由单片机一个一个地发送数据并进执行监控。但是如果用DMA,设置了起始地址,数据大小等参数后,就直接由专门的一个DMA模块进行数据发送,发送过程中单片机无需参与。发送完后会产生中断告知单片机。由此可知用DMA可以节省单片机资源,让单片可以在同一时间里干更多事。本次为了减少CPU的占用,在配置串口的时候使用DMA进行数据的收发。
不多说 直接上干货!!!
本次使用的DMA1的通道4和通道5,图中已标注。
干货代码直接上:串口配置:
unsigned char USART1_Tx_Buffer[USART_DMA_BUFFER_SIZE]; unsigned char USART1_Rx_Buffer[USART_DMA_BUFFER_SIZE]; unsigned char USART2_Tx_Buffer[USART_DMA_BUFFER_SIZE]; unsigned char USART2_Rx_Buffer[USART_DMA_BUFFER_SIZE]; #define USART1_TX_PORT GPIOA #define USART1_TX_PIN GPIO_Pin_9 #define USART1_RX_PORT GPIOA #define USART1_RX_PIN GPIO_Pin_10 //语音模块通讯通道 void USART1_Init(u16 BAUD) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); USART_DeInit(USART1); //UART1_TX PA9 GPIO_InitStructure.GPIO_Pin = USART1_TX_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(USART1_TX_PORT, &GPIO_InitStructure); //UART1_RX PA10 GPIO_InitStructure.GPIO_Pin = USART1_RX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(USART1_RX_PORT, &GPIO_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//中断源 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_InitStructure.USART_BaudRate = BAUD;//可直接根据使用波特率大小直接设置 一般通常用9600 USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); //IT config USART_ITConfig(USART1,USART_IT_TC,DISABLE); USART_ITConfig(USART1,USART_IT_RXNE,DISABLE); USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);//空闲中断 关系着后面的DMA配置 USART_Cmd(USART1, ENABLE); USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); }
DMA配置
void USART1_DMA_CONFIG(void) { DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel5); //uart1 RX should use dma1 ch5 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; // 初始化外设地址 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_Rx_Buffer; // 内存地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作为数据来源 DMA_InitStructure.DMA_BufferSize = USART_DMA_BUFFER_SIZE ; // 缓存容量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不递增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设字节宽度 DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; // 内存字节宽度 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //正常模式,即满了就不在接收了,而不是循环存储 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //通道优先级高 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //内存与外设通信 而非内存到内存 DMA_Init(DMA1_Channel5, &DMA_InitStructure); DMA_Cmd(DMA1_Channel5,ENABLE); DMA_DeInit(DMA1_Channel4); //uart1 TX should use dma1 ch4 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_Tx_Buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //存储器作为数据来源 DMA_InitStructure.DMA_BufferSize = USART_DMA_BUFFER_SIZE ; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel4, &DMA_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;//通道4作为中断源 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE); } ## 中断函数: void USART1_IRQHandler(void)//串口1的中断服务函数 { uint32_t length; if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) { length = USART1->SR; length = USART1->DR; length = length; DMA_Cmd(DMA1_Channel5,DISABLE); length = USART_DMA_BUFFER_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5); USART_ClearITPendingBit(USART1, USART_IT_IDLE); DMA_SetCurrDataCounter(DMA1_Channel5,USART_DMA_BUFFER_SIZE); DMA_Cmd(DMA1_Channel5,ENABLE); } } void DMA1_Channel4_IRQHandler(void)//DMA通道4的中断服务函数 { if(DMA_GetITStatus(DMA1_FLAG_TC4)) { DMA_ClearFlag(DMA1_FLAG_GL4|DMA1_FLAG_TC4|DMA1_FLAG_TE4 ); DMA_Cmd(DMA1_Channel4, DISABLE); } }
特别强调:串口的空闲中断IDLE清除中断标志位和平常的接收发送中断标志位清除方法不一样,如代码上所写,先读取SR,再读取DR才能清除空闲中断标志位,而平常的接收发送中断只需要读取DR即可。
其他辅助函数:
//usart1发送函数 void usart1_send_char(u8 data) { USART_SendData(USART1, (u8) data); while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {} } //usart1 dma发送函数 void dma_usart1_send(int length) { DMA1_Channel4->CNDTR=length;//比如一共0-5 6个字节 数据 此时length=6 需要在第五个数据赋值后加1 DMA_Cmd(DMA1_Channel4, ENABLE); }
配置完成了现在就可以使用dma_usart1_send()函数发送所需要的的数据,如果接受数据使用的是DMA1的通道5,可用中断可不用
遇到问题可随时交流,欢迎各位大佬指正,互相学习。
-
基于USB3.0和FPGA的多串口传输系统设计
2020-08-28 18:15:39该系统适合于超高速数据的传输,具有电路简单、体积小等优点。FPGA技术与USB3.0的结合有极大的灵活性和可扩展性,基于FPGA和USB3.0的突出优点,该设计方案必将应用在更广阔的领域。 -
串口通信传输10M大文件
2013-06-27 15:43:17打开串口后,就开始了自动接收数据,接收完成后,按下“停 止接收数据”;如果要发送数据,请点击“发送键”,发送完成,会弹出“发送完成”。 软件测试情况:发送功能用“超级终端多次测试”,10M内,没发生错误。... -
STM32 串口传输最佳处理方式 FreeRTOS+队列+DMA+IDLE (一)
2020-01-03 11:29:15当多个串口数据都有大量数据来时,我们如何最佳处理STM32串口通信数据? 可以通过FreeRTOS+队列的发送方式。 下面将串口DMA发送处理过程中心思想:1、建立一个大的环形数组 2、发送的数据时,将数据存入到大的数组 3...当多个串口数据都有大量数据来时,我们如何最佳处理STM32串口通信数据?
可以通过FreeRTOS+队列的发送方式。
下面将串口DMA发送处理过程
中心思想:
1、建立一个大的环形数组
2、发送的数据时,将数据存入到大的数组
3、需要发送数据的长度以及在大数组中的位置,通过队列,发送出去。
4、通过队列,等待有数据进入队列。
5、启动DMA,等待DMA传输完成。/****************************************************************** 将需要发送的数据存入一个大数组内,通过队列发送,将长度,以及数组内的地址 发送出去。 数组的大小可以是队列项目数的N倍,一般5倍就好了,队列项目数5个 已经不少了。 即:假如创建5个队列消息,每个长度为4字节。数组大小 MAX_FRAME_COMM_BUFFER_SIZE =5*每一次串口传输数据长度; static uint8_t txbuf_Blue_DMA[MAX_FRAME_COMM_BUFFER_SIZE]; /*缓存的数组大小*/ DMA_MemoryBaseAddr=txbuf_Blue_tmp; *****************************************************************/ void USART1_SendData(uint8_t *ps,uint16_t len) { uint16_t i; BufferLoopData_Typedef buffer_loop; buffer_loop.start_addr = ps_tbwr_Blue; buffer_loop.len = len; for (i=0;i<len;i++) { if (ps_tbwr_Blue >= MAX_FRAME_COMM_BUFFER_SIZE) { ps_tbwr_Blue = 0; } txbuf_Blue_DMA[ps_tbwr_Blue] = *(ps+i);//txbuf_Blue_DMA为 ps_tbwr_Blue++; } xQueueSendToBack(xQueue_Blue_tx,(void *)&buffer_loop,portMAX_DELAY); } /****************************************************************** 队列接收到数据所在数组的地址以及长度,然后通过DMA发送出去 *****************************************************************/ void USART1_SendTask(void const * argument) { uint16_t i; BufferLoopData_Typedef buffer_loop; extern uint8_t txbuf_Blue_tmp[MAX_FRAME_COMM_LEN]; for (;;) { xQueueReceive(xQueue_Blue_tx,&buffer_loop,portMAX_DELAY); //SCB_CleanDCache(); for (i=0;i<buffer_loop.len;i++) { if (buffer_loop.start_addr >= MAX_FRAME_COMM_BUFFER_SIZE) { buffer_loop.start_addr = buffer_loop.start_addr - MAX_FRAME_COMM_BUFFER_SIZE; } txbuf_Blue_tmp[i] = txbuf_Blue_DMA[buffer_loop.start_addr]; buffer_loop.start_addr += 1; } //SCB_CleanInvalidateDCache(); Uart1_DMASend_Start(&txbuf_Blue_tmp[0],buffer_loop.len);//通过DMA发送 xSemaphoreTake(BinarySem_UART1_tx_finish_Handle,portMAX_DELAY); //等待DMA发送成功 taskYIELD(); } }
串口DMA发送代码:
/********************************************************************** 串口USART1 DMA发送数据 ********************************/ uint8_t Uart1_DMASend_Start(uint8_t* buffer, u16 size) { if(!size) return 0; while (DMA_GetCurrDataCounter(DMA1_Channel4)); if(buffer) memcpy(txbuf_Blue_tmp, buffer,(size > 1024?1024:size)); //DMA发送数据,DMA DMA_Cmd(DMA1_Channel4, DISABLE); DMA1_Channel4->CNDTR = size; DMA_Cmd(DMA1_Channel4, ENABLE); return size; }
中断代码:
//中断传输完成void DMA1_Channel4_IRQHandler(void) { portBASE_TYPE xHigherPriorityTaskWoken; if(DMA_GetITStatus(DMA1_FLAG_TC4)) { DMA_ClearFlag(DMA1_FLAG_TC4); xSemaphoreGiveFromISR(BinarySem_UART1_tx_finish_Handle,&xHigherPriorityTaskWoken); } }
串口初始化:
USART_Config(); UART_DMA_Config();
串口初始化详细代码
//串口DMA初始化 void UART_DMA_Config(void){ NVIC_InitTypeDef NVIC_InitStructure; DMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)txbuf_Blue_tmp,DMA_DIR_PeripheralDST,DMA_Priority_Medium,0,DMA_Mode_Normal); //txbuf_Blue_tmp USART1 DMA发送基地址 DMA_Config(DMA1_Channel5,(u32)&USART1->DR,(u32)rxbuf_Uart1_DMA,DMA_DIR_PeripheralSRC,DMA_Priority_Medium,MaxSize_FRAME_DISP,DMA_Mode_Circular); //rxbuf_Uart1_DMADMA接收基地址 //NVIC初始化 NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=6 ; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE); USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); DMA_Cmd (USART_RX_DMA_CHANNEL,ENABLE); } //串口配置初始化 void USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA|RCC_APB2 Periph_AFIO, ENABLE); //USART1_TX GPIOA.9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART1_RX GPIOA.10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=6 ; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); USART_Cmd(USART1, ENABLE); } //DMA初始化 做成了一个封装函数 void DMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 PeripheralBaseAddr,u32 MemoryBaseAddr,u32 dma_dir,u32 priority,u16 bufsize,u32 mode) { DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA_CHx); DMA_InitStructure.DMA_PeripheralBaseAddr = PeripheralBaseAddr; DMA_InitStructure.DMA_MemoryBaseAddr = MemoryBaseAddr; DMA_InitStructure.DMA_DIR = dma_dir; DMA_InitStructure.DMA_BufferSize = bufsize; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = mode; DMA_InitStructure.DMA_Priority = priority; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA_CHx, &DMA_InitStructure); }
下一篇文章来介绍如何通过DMA+FreeRTOS+IDLE接收数据
-
STM32 串口传输最佳处理方式 FreeRTOS+队列+DMA+IDLE (二)
2020-01-03 11:37:38紧接着上一篇文章,如何合理处理多个串口接收大量数据。此种方法,很厉害,很NB,首先,利用DMA 可节省大量CUP资源。其次,利用IDLE空闲中断来接收位置个数的数据。最后利用串口DMA环形数据的偏移量、长度,入队,出...紧接着上一篇文章,如何合理处理多个串口接收大量数据。此种方法,很厉害,很NB,首先,利用DMA 可节省大量CUP资源。其次,利用IDLE空闲中断来接收位置个数的数据。最后利用串口DMA环形数据的偏移量、长度,入队,出队处理数据。保证了任务的流畅处理。
串口接收数据:
中心思想
1、开启DMA 环形接收数据模式。
2、触发接收数据中断。
3、假如数据有进来,将上面环形数据的偏移量与长度 入队。
4、出队函数,处理数据。直接上代码
两个任务完成
#define MAX_FRAME_DISP_LEN 125 #define MAX_FRAME_DISP_ITEM 5 #define MaxSize_FRAME_DISP (MAX_FRAME_DISP_LEN*MAX_FRAME_DISP_ITEM) static void RecUart1Temp_Task(void* parameter) { uint16_t len; uint16_t ndtr_last;//上次剩余个数 BufferLoopData_Typedef buffer_loop; BaseType_t xReturn = pdPASS; buffer_loop.start_addr = 0; buffer_loop.len = 0; ndtr_last = MaxSize_FRAME_DISP; while (1) { //等待有数据消息 xReturn = xSemaphoreTake(BinarySem_Handle, portMAX_DELAY); len = Reg_Usart1_CHANNEL_CNDTR;//DMA剩余个数 //环形数组 if (ndtr_last != len)//上次与这次不同,表示有新数据 { buffer_loop.start_addr = buffer_loop.start_addr + buffer_loop.len; //环形数据地址偏移量 if (buffer_loop.start_addr >= MaxSize_FRAME_DISP) { buffer_loop.start_addr = buffer_loop.start_addr - MaxSize_FRAME_DISP; } if (ndtr_last > len) { buffer_loop.len = ndtr_last - len; //接收数据长度=上次长度-这次长度 } else { buffer_loop.len = MaxSize_FRAME_DISP - len+ ndtr_last; //环形数据到头后,总数-这次剩余+上次剩余 } ndtr_last = len; if ((buffer_loop.len > 0)||(buffer_loop.len < MAX_FRAME_DISP_LEN)) { xQueueSendToBack(xQueue_Smrj_rx,(void *)&buffer_loop,0); } } taskYIELD(); } }
任务出队,处理函数
extern uint8_t rxbuf_Uart1_DMA[MaxSize_FRAME_DISP]; static void RecUart1DealTask(void* pvParameters) { uint16_t i; BufferLoopData_Typedef buffer_loop; for (;;) { xQueueReceive(xQueue_Smrj_rx,&buffer_loop,portMAX_DELAY); if ((buffer_loop.len > 0)&&(buffer_loop.len < MAX_FRAME_DISP_LEN)) { for (i=0;i<buffer_loop.len;i++) { if (buffer_loop.start_addr >= MaxSize_FRAME_DISP) { buffer_loop.start_addr = buffer_loop.start_addr - MaxSize_FRAME_DISP; } rxbuf_Uart1_tmp[i] = rxbuf_Uart1_DMA[buffer_loop.start_addr]; buffer_loop.start_addr += 1; } __nop(); /*此处处理接收的数据*/ //Smrj_RecUserHandle(&rxbuf_Uart1_tmp[0],buffer_loop.len); //SMRJ_Data_Dec(&rxbuf_Smrj_tmp[0],buffer_loop.len); } taskYIELD(); } }
中断处理函数:
void USART1_IRQHandler(void) { uint32_t ulReturn; ulReturn = taskENTER_CRITICAL_FROM_ISR(); if(USART_GetITStatus(DEBUG_USARTx,USART_IT_IDLE)!=RESET) { Uart_DMA_Rx_Data(); USART_ReceiveData(DEBUG_USARTx); taskEXIT_CRITICAL_FROM_ISR( ulReturn ); } } void Uart_DMA_Rx_Data(void) { BaseType_t pxHigherPriorityTaskWoken; DMA_Cmd(USART_RX_DMA_CHANNEL, DISABLE); DMA_ClearFlag( DMA1_FLAG_TC5 ); Reg_Usart1_CHANNEL_CNDTR= USART_RX_DMA_CHANNEL->CNDTR;//循环数组里剩余个数 DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE); xSemaphoreGiveFromISR(BinarySem_Handle,&pxHigherPriorityTaskWoken); portYIELD_FROM_ISR(pxHigherPriorityTaskWoken); }
串口初始化见上面一篇文章
总结,从上面代码我们可以看到,我们是巧妙的利用的串口DMA环形模式,利用队列来处理数据。
下一篇,我们直接利用环形缓存buf来实现数据的处理,是的更加的通用性。————————————————
版权声明:本文为CSDN博主「断雁孤鸿」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/oDuanYanGuHong/article/details/100576160 -
串口的数据协议解析
2010-03-05 15:43:00一般来说,通过串口传输的单个数据包都不会太大,否则,会导致数据被覆盖。对于通过串口传输的大数据,处理的办法是分割成多个小的数据包。可能出现的情况是因为接收和读取的不同步,接收到的数据需要分为多次读取... -
2.13 STM32 串口传输最佳处理方式 FreeRTOS+队列+DMA+IDLE (二)
2019-09-06 11:25:54紧接着上一篇文章,如何合理处理多个串口接收大量数据。此种方法,很厉害,很NB可节省大量CUP资源。并且FreeRTOS任务处理的非常合理。 中心思想: 1、开启DMA 环形接收数据模式。 2、触发接收数据中断。 3、把... -
QT多线程-应对复杂数据处理、串口数据实时显示
2019-07-31 16:55:41任何收发两端速度不一致的通讯,都需要在它们之间使用一个足够大的FIFO缓冲区。 对任何FIFO缓冲区的使用,都需要仔细考虑接收端接收时超时无数据和发送端发送时FIFO缓冲区已...◆传输的数据对应的每个字节到底的英文... -
STM32 DMA接收串口数据
2020-01-03 17:27:48当在串口多数据传输下,CPU会产生多次中断来接收串口数据,这样会大大地降低CPU效率,同时又需要CPU去做其它更重要的事情,我们应该如何来优化? 比如四轴飞行器,当在不停地获取姿态控制方向时,又要去接收串口数据.答:... -
单片机与DSP中的基于VC++ 6.0的高速串口通信数据采集系统
2020-11-11 07:04:14针对高速串口数据采集软件的设计要求,提出了基于多线程技术和自定义消息机制的异步串口通信的设计思想.结合串口通信的机理和多线程同步方式,分析了Win32系统下多线程异步串口通信程序的开发方法. 1 引 言 串行... -
虚拟串口最大传输速率_【干货】详解RS232、RS485、RS422、串口&握手
2020-11-27 07:51:26由于串行通讯方式具有使用线路少、成本低,特别是在远程传输时,避免了多条线路特性的不一致而被广泛采用。在串行通讯时,要求通讯双方都采用一个标准接口,使不同的设备可以方便地连接起来进行通讯。RS-232-C接口... -
Java串口编程:串口数据的发送与监听读取
2018-07-02 11:11:58由于串口(COM)不支持热插拔及传输速率较低,目前部分新主板和大部分便携电脑已取消该接口。现在串口多用于工业控制和测量设备以及部分通信设备中。 根据美国电子工业协会(EIA: Electronic Industry Associati... -
Unity各种传输方式转串口COM端口的数据操作SerialPort类
2017-02-09 15:07:07话不多说进入正文,去年总是发文到微博也没几个人看,想来还是CSDN比较专业(老司机云集)。以后就在此混迹了,还望各位大佬多多提携 需求: 先说下用它来干嘛吧,众所周知2016年是VR的元年,头盔各种型号各种配置像... -
LabVIEW上位机串口数据接收不完整、不正常的解决方法
2020-06-02 16:08:472一次性传输数据多、波特率高情况下,经常出现接受数据不完整、或者只能接受到很少一部分的数据。下面是针对这种情况的几种分析: (可能1):数据量太大,缓冲区需要重新设计(不常见) (可能2)接收的数据较长,... -
STM32—无需中断来实现使用DMA接收串口数据
2017-09-14 00:07:00描述:当在串口多数据传输下,CPU会产生多次中断来接收串口数据,这样会大大地降低CPU效率,同时又需要CPU去做其它更重要的事情,我们应该如何来优化?比如四轴飞行器,当在不停地获取姿态控制方向时,又要去接收串口数据.... -
基于树莓派的多串口多总线服务器设计
2021-01-19 17:27:27要将现场控制网络和信息网络相连,就需要解决串口通信协议和因特网通信协议的转换问题,即把原有设备转换为具备网络接口的外设,这样可以将传统串行链路上的数据传输到信息网络上,而无需更换原有设备。如此,可以... -
STM32—无需中断来实现使用DMA接收串口数据(原创)
2017-09-15 10:06:09当在串口多数据传输下,CPU会产生多次中断来接收串口数据,这样会大大地降低CPU效率,同时又需要CPU去做其它更重要的事情,我们应该如何来优化? 比如四轴飞行器,当在不停地获取姿态控制方向时,又要去接收串口数据. 答... -
OpenMV与STM32单片机串口通信,如何使用openmv连续发送多帧数据给单片机
2019-04-24 10:24:48首先,我使用STM 32单片机有2 年左右的时间了,但是openmv却不足一个月的时间,由于近几天问我关于两者之间...openmv与单片机通讯,大多数时候都不是只发送一两个字符或数字,一般都需要进行大量数据传输,将识别到... -
stm32数据传输
2021-02-21 11:40:25DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。它的作用就是解决大量数据转移过度消耗CPU资源的问题。 在stm32(大容量)中有两个DMA,DMA1有7个... -
STM32串口多机通信
2020-10-17 23:44:53STM32 的UART数据寄存器是9位,数据传输先传低位(LSB)--实际应用发现9位数据大时候有丢包错...利用USART可以进行多机处理器通信,其原理就是使从机处于静默模式,由主机在需要的时候发送指令唤醒从机,并传输数据。STM -
基于SC28L198的多串口服务器设计
2021-01-19 22:00:10该系统能够同时为8个串口设备提供以太网远程数据传输,为具有串行通信接口设备的网络控制提供了条件,实现了计算机远程监控。 目前工业领域中有大量的设备不具备以太网接口,但这些设备都提供RS-232串口或RS-... -
嵌入式系统/ARM技术中的基于树莓派的多串口多总线服务器设计
2020-10-19 18:31:14要将现场控制网络和信息网络相连,就需要解决串口通信协议和因特网通信协议的转换问题,即把原有设备转换为具备网络接口的外设,这样可以将传统串行链路上的数据传输到信息网络上,而无需更换原有设备。如此,可以... -
STM32F103 实例应用(11)——DMA接收不定长串口数据
2021-01-19 19:53:26DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用 CPU,即在传输数据的时候,CPU 可以干其他的事情,好像是多线程一样。 数据传输支持从外设到存储器或者... -
arduino 串口 缓冲区 大小 修改
2015-03-20 09:39:00Arduino串口缓冲区默认为64字节,如果你单次传输的数据较多可以将arduino-1.0.5-r2\hardware\arduino\cores\arduino\HardwareSerial.cpp中的 #define SERIAL_BUFFER_SIZE 64 修改为 #define SERIAL_BUFFER_... -
数据转换/信号处理中的一种232串口转红外通讯的电路设计
2020-10-20 09:33:14摘要:在现代电子行业中无线通讯技术的应用越来越广泛,红外通讯作为无线通讯的一种技术备受各个领域的欢迎。...红外通讯以红外线作为通讯载体,通过红外光在空中的传播来传输数据,它由红外发射器和红外接收器来完