问题:类似K线与蓝牙接收部门模块,要求由原来的接收串口中断改为DMA接收。据说要用到空闲中断与DMA中断,但是经仿真发现DMA每完成传输一个数据(比如1BYTE)就会进入空闲中断(k线发现这种情况),考虑到这样进入中断的频率和以前串口接收中断的频率差不多,所以放弃此方案,听说有的DMA具有超时中断机制(具体有没有我也没考证),但是我手上的板子经过研读芯片手册发现只有传输一半中断,传输完成中断,传输越界错误中断,所以也没法用此方案。
网上有很多理解DMA接收机制的帖子,这里我就不在赘述,我个人认为其中最要紧的就是判断接收数据长度的问题因为配置DMA的传输数据量大多都是接收缓存区的最大值,对于不定长数据无法预测什么时间传输结束。
话不多说先上干货(拙见)
这里具体根据对应的芯片手册写出需要的DMA接收函数,并在K线或者蓝牙初始化模块时调用此函数
(注意点: 在函数内使能DMA中断,但是不要使能对应串口DMA接收通道。使能对应串口DMA接收通道可以在对应串口初始化时自行按需调用)
在此处取消使能串口接收中断(视具体情况而定,蓝牙可以在开始接收时再取消),并在此调用DMA接收函数进行初始化配置,并使能DMA通道(视具体情况而定)。
此处为实时操作系统中定时器中断里的一个检查串口状态的函数,在此处加入衔尾法。
uint8_t now, last, before; //+++// 10个字节的数据逐个赋值循环检查DMA通道余量 if(num >= 10) { num = 0; } RecRem[num++]=Get_Transfer_Number_Remain (); if(num >= 3) { now = num - 1; last = num - 2; before = num - 3; } else if(num == 2) { now = 1; last = 0; before = 9; } else if(num == 1) { now = 0; last = 9; before = 8; } else if(num == 0) { now = 9; last = 8; before = 7; } if((RecRem[now] == RecRem[last]) && (RecRem[now] != MAXBUFFSIZE) )//这样下来处理时就像每次盯着循环数组什么时候露出一个尾巴处理一样,一旦数组中连着两个余量相等了就表示DMA传输结束(也可以三个数据一样,这就是before的作用,这里我只用了两个),可以开始处理数据了 { Channel_Enable(); //DMA通道FALSE Reclen = MAXBUFFSIZE - Get_Transfer_Number_Remain ();//获取本次DMA传输帧长 Address_Config ();//重新配置传输地址(还是原来的地址,这里需要把通道余量充满) Transfer_Number_Config (MAXBUFFSIZE);//配置传输数量 Channel_Enable(); //DMA通道TRUE USARTBusy = 0;//所有原接收中断里需要处理的串口状态位都要在这里变更 }
此方法的大概思路就是在串口开始接收数据之前开启DMA传输并关闭接收中断,并在串口检查函数里加入检查通道余量的内容,如果通道余量两次(或者三次)相等并且不等于最大值,那么就判断接收结束,此时关闭DMA通道获取此次传输帧长,重新配置DMA传输地址和通道余量,再打开DMA通道(一般如果不重新使能DMA通道,只配置是没有效果的,这个要看具体的芯片),处理串口状态位。
这就是全部内容啦,在遇到问题时我除了请教前辈之外还在网上查询了相关的资料,虽然关于如何DMA接收发送的方法有很多但是都很笼统,具体实现比较困难,这里是我自己想出的不用中断的方法(像k线可以直接取消接收中断,蓝牙串口也可以用但是蓝牙一般需要保留接收中断用以初始化配对,除此之外可以把关于具体指令内容的传输全改为DMA),可能网上也有只是我没有找到,也可能我这样方法是有一些问题的,希望各位大佬可以指导一下。
需要转载请告诉我一下让我高兴高兴哈哈哈~