你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

如果可能,别再用中断接收串口数据了

[复制链接]
lusonghua 提问时间:2015-6-10 16:51 /
增加一些注释吧,这个代码能帮上忙就行。

________________________________________________________

实在受不了中断了。
这里使用DMA循环方式接收串口数据,不用中断,空闲时间去查询DMA状态,然后拷贝数据即可。
F407的部分代码如下,F103的类似:

// 宏定义
#define USART3_RXBUF_SIZ   256 /* UART接收缓冲,必须为2^n,增加这个数值可以放宽处理UART接收数据的时间间隔 */
#define USART3_RX_DMA_CHANNEL            DMA_Channel_4 /* UART接收通道 */
#define USART3_RX_DMA_STREAM             DMA1_Stream1 /* UART接收DMA */

// 全局缓冲区
static uint8_t gl_Usart3Rxbuf[USART3_RXBUF_SIZ] = { 0, };

// 初始化串口和DMA..
static void MX_Uart_Init(void)
{
USART_InitTypeDef USART_InitStruct;
DMA_InitTypeDef DMA_InitStruct;

// 初始化USART3的通讯 ----------------------------------------------------------

USART_StructInit(&USART_InitStruct);
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART3, &USART_InitStruct);

/* 配置UART为DMA循环接收方式,即DMA按照RingBuffer方式写入UART接收缓冲,CPU不参与UART接收 */
DMA_StructInit(&DMA_InitStruct);
DMA_InitStruct.DMA_BufferSize = sizeof(gl_Usart3Rxbuf) ;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable ;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull ;
DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single ;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_PeripheralBaseAddr =(uint32_t) (&(USART3->DR)) ;
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
DMA_InitStruct.DMA_Channel = USART3_RX_DMA_CHANNEL;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)gl_Usart3Rxbuf;
DMA_Init(USART3_RX_DMA_STREAM, &DMA_InitStruct);

USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);
DMA_Cmd(USART3_RX_DMA_STREAM, ENABLE);
USART_Cmd(USART3, ENABLE);
}

// 处理接收的串口数据
void Idle_Proc(void)
{
static uint16_t lastDataCounter = 0; /* 保存已处理的UARt接收数据index */
static char line[256];
static int ncin = 0;

uint16_t currDataCounter;
bool newLine = false;
uint8_t ch;

// 处理USART3接收的数据 ----------------------------------------------------

/* 获取DMA中数据计数,用于计算当前所接收字符的index */
do {
  currDataCounter = DMA_GetCurrDataCounter(USART3_RX_DMA_STREAM);
} while (currDataCounter != DMA_GetCurrDataCounter(USART3_RX_DMA_STREAM));

currDataCounter = sizeof(gl_Usart3Rxbuf) - currDataCounter; /* 计算当前UART接收的index */
while (((currDataCounter - lastDataCounter) & (USART3_RXBUF_SIZ - 1)) != 0) { /* 一直处理到已接收的index */
  ch = gl_Usart3Rxbuf[lastDataCounter++ & (USART3_RXBUF_SIZ - 1)]; /* 拷贝已接收的UART数据,所保存的index增长 */
......
}
}


/* 增加主函数 */
int main(void) {
    for (;;) {
        process1();
        process2();
        process3();
        ...
        process99();

        Idle_Proc();
    }
}

其他的诸如时钟初始化、GPIO初始化等这个就不贴了。

PS:中断是个好东西,但是,...,能不用就不用吧。。。,还有别把程序写成阻塞的。




收藏 1 评论31 发布时间:2015-6-10 16:51

举报

31个回答
lusonghua 回答时间:2015-6-16 12:25:12
moyanming2013 发表于 2015-6-16 11:25
嗯,看来DMA+查询确实有使用的场景。
如果要是数据多了DMA缓冲区够用么? ...

考虑的不应该是数据多了或少了的问题,而是最“严酷”条件的问题,即如果UART带宽100%使用时,查询DMA状态的时间间隔问题。
因为UART是很慢的外设,给个一定长度的buffer,就有相当宽裕的容许间隔。只要保证在时间间隔内程序必定能够再次查询到DMA状态即可。这个对于一般的ARM都不成问题(如果这样都有问题,那真得考虑CPU选型了)。
当然,还是那句话,别阻塞。
lusonghua 回答时间:2015-6-16 10:36:15
moyanming2013 发表于 2015-6-11 16:52
那“查询DMA的状态”是否也会一直在占用CPU啊?

怎么会“一直”呢,查询一下,有就收,没有就干别的事,函数就是Idle_Proc()。
用DMA+一段缓冲,可以放宽查询的时间间隔要求,而不是来一个字符中断一下地搞。
中断还是留给重要的事情。
如果软件任务简单用几个中断当然无所谓,但任务多了,如USB芯片、FPGA等等好多地方都要中断,而公司又明确不允许使用RTOS,那么...
每个程序任务的背景都不尽相同,我的代码也只是一种思路罢了。
creep 回答时间:2015-6-11 09:19:28
本帖最后由 creep 于 2015-6-11 09:20 编辑
lusonghua 发表于 2015-6-11 08:53
串口空闲中断,什么时候串口有空闲呢。应该保证必须保证串口空闲中断发出之前buffer没满。 ...

如果设置了串口空闲中断,那么2帧发送数据之间的间隔就会产出一次串口空闲中断。
至于buff的大小就要看你发送的数据一帧有多大了,合理设置即可。
qianfan 回答时间:2015-6-10 17:04:04
中断+RingBuffer一起处理。DMA无法做成RingBuffer那样
wyxy163@126.com 回答时间:2015-6-10 17:06:44
提示: 作者被禁止或删除 内容自动屏蔽
creep 回答时间:2015-6-10 17:33:30
如果接收数据很多的话,频繁的中断的确开销很大。
既然用DMA了,为何不用串口空闲中断+DMA呢,这就不用频繁的查询了,一次能接收不定长的一帧数据。
为什么是EEFOCUS小白 回答时间:2015-6-10 17:33:53
中断接收有什么问题么
埃斯提爱慕 回答时间:2015-6-10 20:12:38
提示: 作者被禁止或删除 内容自动屏蔽
moyanming2013 回答时间:2015-6-10 22:38:25
程序一直在查询串口,实在是效率低下把?
你的理由是什么呢?
shadow丶 回答时间:2015-6-10 22:53:49
确实 中断写这方便 使用dma  毕竟没有写串口舒服
creep 回答时间:2015-6-10 23:25:45
dsjsjf 发表于 2015-6-10 20:12
这个方法貌似不错,下次试试

这应该是串口接收数据比较好的方法之一的,中断的方法和这个没法比。
lusonghua 回答时间:2015-6-11 08:51:40
moyanming2013 发表于 2015-6-10 22:38
程序一直在查询串口,实在是效率低下把?
你的理由是什么呢?

没有查询串口,是查询DMA的状态,DMA的设置自行完成了RingBuffer的功能。
串口接收的缓冲长度决定了读取RingBuffer的时间裕度。
lusonghua 回答时间:2015-6-11 08:53:21
creep 发表于 2015-6-10 17:33
如果接收数据很多的话,频繁的中断的确开销很大。
既然用DMA了,为何不用串口空闲中断+DMA呢,这就不用频繁 ...

串口空闲中断,什么时候串口有空闲呢。应该保证必须保证串口空闲中断发出之前buffer没满。
lusonghua 回答时间:2015-6-11 08:55:29
我的方法是查询DMA状态,DMA自行完成RingBuffer的功能,且无限。
当然,整个程序必须没有阻塞,或者阻塞的时间保证串口通讯肯定不会“溢出”Buffer。
lusonghua 回答时间:2015-6-11 08:58:46
条条大道通罗马,只是给出使用中断外的另一种解决方法,没有别的意思哈。
中断也没什么不好的,但一般情况下会有更需要中断的地方。
串口接收这种事情留给空闲时间即可。
废鱼 回答时间:2015-6-11 09:01:57
每个人的用法不一样,目的一样,只不过走的路不同。
qiu-368230 回答时间:2015-6-11 09:13:00
中断触发呢,
123下一页

所属标签

相似问题

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版