RTThread使用DMA串口接收数据不连续的问题
Posted Rabbit-susu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RTThread使用DMA串口接收数据不连续的问题相关的知识,希望对你有一定的参考价值。
RTThread使用DMA接收串口数据的问题
问题/现象
使用RTThread的DMA接收串口数据,数据不连续,即IDLE中断没有起到作为一个frame的判定.
经过对serial和drv_uarts源码的分析,得出原因:
graph LR
RX_INT[USART1_IRQHandler] -->process1(...)
process1 -->rx_isr1[dma_recv_isr]
rx_isr1 -->flag1isr_flag
flag1 -->|UART_RX_DMA_IT_IDLE_FLAG| serial_isr1[rt_hw_serial_isr]
serial_isr1 -->eventevent
DMA_INT[UART1_DMA_RX_IRQHandler] -->process2(...)
process2 -->rx_isr2[dma_recv_isr]
rx_isr2 -->flag2isr_flag
flag2 -->|UART_RX_DMA_IT_HT_FLAG| serial_isr2[rt_hw_serial_isr]
flag2 -->|UART_RX_DMA_IT_TC_FLAG| serial_isr2[rt_hw_serial_isr]
serial_isr2 -->eventevent
event -->|RT_SERIAL_EVENT_RX_DMADONE|do_something(...)
从上图可知,发生IDLE中断时,USART1_IRQHandler
调用的是和UART1_DMA_RX_IRQHandler
相同的接口 —— rt_hw_serial_isr
.
这就造成无法区分是IDLE中断还是DMA中断.
不改变源码的情况下,仍使用DMA+IDLE中断,目前这两种方式是比较好的
解决方式①
- 接收数据
/* 接收数据回调函数 */
static rt_err_t uart2_input(rt_device_t dev, rt_size_t size)
rt_err_t result;
result = rt_sem_release(&serial2_sem);/*通知serial_thread_entry线程,有数据了*/
if ( result == RT_EOK)
rt_kprintf("sem release error!\\n");
return result;
static void serial_thread_entry(void *parameter)
rt_err_t result;
rt_uint8_t c = 0;
rt_uint8_t i = 0, rx_state = SERIAL2_STATE_WAIT_FRAME;
rt_device_t serial2 = rt_device_find("uart2");
struct frame_msg msg;
while (1)
switch(rx_state)
case SERIAL2_STATE_WAIT_FRAME:
result = rt_sem_take(&serial2_sem, RT_WAITING_FOREVER); /* 等待新的一帧数据 */
if (result == RT_EOK)
rt_kprintf("%s: frame start\\n", __func__);
rx_state = SERIAL2_STATE_RECV_DATA;
i = 0;
break;
case SERIAL2_STATE_RECV_DATA:
/* 读取一段数据 —— 数个字节(不足一帧) */
while(rt_device_read(serial2, 0, &c, 1) != 0)
framebuf[i] = c;
i++;
result = rt_sem_take(&serial2_sem, 10); /* 将信号量设置为带有超时的信号量,等待下一段数据 */
if(result == -RT_ETIMEOUT) /* 超过10个OSTicks没有读到数据,判定该帧结束 */
framebuf[i] = \'\\0\';
msg.data = framebuf;
msg.size = i;
result = rt_mq_send(&rtc_rx_mq, (void*)&msg, sizeof(struct frame_msg)); /* 给其它线程通信 */
if(result != RT_EOK)
rt_kprintf("%s: msgqueue send error-[%d]\\n", __func__, result);
rx_state = SERIAL2_STATE_WAIT_FRAME; /* 一帧结束 */
rt_kprintf("%s: frame end\\n", __func__);
else
/*接收下一段数据*/
rt_kprintf("%s: frame recv ing...\\n", __func__);
break;
default:
break;
解决方式②
把信号量换成消息队列.
其它问题
代码中的几个局部变量在线程中具有了和线程一样长的生成周期
life_circle(serial_thread_entry) = life_circle(variable(i))
= life_circle(variable(rx_state))
= life_circle(variable(serial2))
= ...
这样是不是意味着thread的stack一直被占用?
嗯,是的.
本文来自博客园,作者:当最后一片树叶落下,转载请注明原文链接:https://www.cnblogs.com/Rabbit-susu/p/17369923.html
DMA接收串口数据(定长与不定长)
效果如图
串口配置(可以直接拷贝拿去用)
#include "sys.h"
#include "usart.h"
//
//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
int handle;
;
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
x = x;
//重定义fputc函数
int fputc(int ch, FILE *f)
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
#endif
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
void uart_init(u32 bound)
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//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);//初始化GPIOA.9
//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);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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); //初始化串口1
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); //使能串口1的DMA发送
void USART1_IRQHandler(void) //串口1中断服务程序
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
Res =USART_ReceiveData(USART1); //读取接收到的数据
#endif
DMA配置
#include "dma.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK精英STM32开发板
//DMA 代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/8
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//
DMA_InitTypeDef DMA_InitStructure;
u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA_CHx); //将DMA的通道1寄存器重设为缺省值
DMA1_MEM_LEN=cndtr;
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,从内存读取发送到外设
DMA_InitStructure.DMA_BufferSize = cndtr; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA_CHx, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Rx_DMA 所标识的寄存器
USART_Cmd(USART1, ENABLE); //使能串口1
DMA_Cmd(DMA_CHx, ENABLE); //使能USART1 TX DMA1 所指示的通道
MYDMA_Enable(DMA1_Channel5);//开始一次DMA传输!
//开启一次DMA传输
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
DMA_Cmd(DMA_CHx, DISABLE ); //关闭USART1 TX DMA1 所指示的通道
DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
DMA_Cmd(DMA_CHx, ENABLE); //使能USART1 TX DMA1 所指示的通道
主函数(定长)
#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
#include "dma.h"
#include "string.h"
int main(void)
u8 USART_RX_BUF1[50];
delay_init();
uart_init(115200);
MYDMA_Config(DMA1_Channel5,(u32)&USART1->DR,(u32)USART_RX_BUF1,50);//DMA1通道5,外设为串口1,存储器为SendBuff,长度35,
while(1)
if(DMA_GetFlagStatus(DMA1_FLAG_TC5)!=RESET) //判断通道5传输完成
DMA_ClearFlag(DMA1_FLAG_TC5);//清除通道4传输完成标志
delay_ms(10); //延时10ms,让DMA继续接收后面数据的同时,也能跑跑其它进程
printf("1:%s\\r\\n",USART_RX_BUF1); //打印
memset(USART_RX_BUF1,0,35); //清空数组
MYDMA_Enable(DMA1_Channel5);//开始一次DMA传输!
主函数(不定长)
#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
#include "dma.h"
#include "string.h"
int main(void)
u8 USART_RX_BUF1[50];
delay_init();
uart_init(115200);
MYDMA_Config(DMA1_Channel5,(u32)&USART1->DR,(u32)USART_RX_BUF1,50);//DMA1通道5,外设为串口1,存储器为SendBuff,长度35,
while(1)
if(USART_RX_BUF1[0]!=0) //判断通道4传输完成
delay_ms(10); //延时10ms,让DMA继续接收后面数据的同时,也能跑跑其它进程
printf("1:%s\\r\\n",USART_RX_BUF1); //打印
memset(USART_RX_BUF1,0,35); //清空数组
MYDMA_Enable(DMA1_Channel5);//开始一次DMA传输!
标志位说明
以上是关于RTThread使用DMA串口接收数据不连续的问题的主要内容,如果未能解决你的问题,请参考以下文章
STM32F4 DMA接收串口定长数据,串口每秒来1000个数据,使用DMA-Normal模式