4组串口UART使用DMA收发 @32G系列,易用,高效,稳定 !精品实战代码
4组串口UART使用DMA收发, 精品实战代码, 易用,高效,稳定 !源自实际系统 @32G系列
非常容易使用, 将C文件添加至项目, 初始化后, 即可用.
仅3个函数, 包含一切UART串口操作, 适用90%以上场景, 从此告别串口驱动代码开发.
void UART1_Init(u32 btl); //初始化串口
u8 UART1_Send(void *pt, u16 Size); //发送数据
u16 UART1_Receive(u8 *buf, u16 Size); //接收数据
main() 函数演示 4个串口同时使用 DMA 收发数据, 收到数据后原路返回, 不限数据长度, 持续收发.
主要收发函数使用说明:
/**
* 原型: u8 UART1_Send( void *pt, u16 Size);
* @功能串口发送数据. 写数据至发送缓冲区(循环池), 写完立即返回, 由DMA管理数据流向串口,
用户无须关心. 只要缓冲区有足够的空间, 可持续写入数据
* @参数pt: 发送数据指针
* @参数Size: 发送数量(字节)
* @返回值 当缓冲区没有足够的空间装入数据时返回1, 其它时候返回0
*/
/**
* 原型: u16 UART1_Receive(u8 *buf, u16 Size);
* @功能读串口数据, 从缓冲区内读取数据. (DMA接收数据后存放至接收缓冲区,
应用代码必须定时查询读取, 否则循环池发生数据覆盖, 会丢失一部分数据, 没有提示, 但不影响后续收发)
* @参数buf: 接收数据指针
* @参数Size: Size期盼接收的字节数
* @返回值 实际接收字节数. 缓冲区空时(没有数据可读)返回0, 返回值<Size说明本次读取完成后,缓冲区已空.
返回值==Size说明本次读取完成后,缓冲区仍有数据可读. 任何时候,返回值不会大于Size
*/
还没有用过串口UART使用DMA收发,下载尝试一下。谢谢楼主! 本帖最后由 QQ624353765 于 2023-11-27 09:11 编辑
你写的好臃肿,我就11行代码,实现串口不定长自动收发功能,下面两个函数是DMA中断函数,这是最高效的用法,整个收发过程只产生三次中断
向设备发送数据时,第一个数据使用串口中断接收,然后在中断中开启DMA准备接收接下来的数据,接收完所有数据DMA中断标记以接收完成
发送串口数据函数只需要两个参数,数据结构体的首地址和结构体的长度,数据发送完成DMA中断标记可接收下一次串口数据
而且效率极高,几乎不占用CPU时间,不需要查询串口,只需要处理接收完成标志位和串口数据发送函数,其他都是全自动的
本帖最后由 tzz1983 于 2023-11-27 10:06 编辑
QQ624353765 发表于 2023-11-27 09:07
你写的好臃肿,我就11行代码,实现串口不定长自动收发功能,下面两个函数是DMA中断函数,这是最高效的用 ...
你喜欢是你自己的事情
当你做过大程序以后,你就会明白实验室的代码和产品应用的代码之间的区别了. 实验室的代码在于讲清楚怎么使用一个设备, 应用代码要综合考虑整体的性能.
我可以持续写入数据,上一帧没发送完就可以接着写下一帧数据,不需要标记数据, 不包含任何协议, 这是底层, 不是针对哪一个应用, 这就是区别, 串口是个低速设备, 产品级代码一般不会等它做完一个动作然后下一个动作. 写入缓存后立即返回, 可以接着写,也可以做别的事情.
接收也是这样, 不管有没有收到数据, 都是立即返回, 绝不会等.
查询? 你做的标志不用查询? 当然了,如果你用了RTOS可以发送信号量, 但是你没有. tzz1983 发表于 2023-11-27 09:49
你喜欢是你自己的事情
当你做过大程序以后,你就会明白实验室的代码和产品应用的代码之间的区别了. 实验室 ...
就拿你这个发送函数来说,发个数据这么多代码不知道干什么用的,发送数据包每个包一个字节,你这代码一秒能发几次
还要把数据包复制到指定内存,不知道这一步有什么意义,还要判断有没有数据长度,你发数据时你不知道有没有数据吗。
把数据包准备好,我的只要取结构体地址和长度
有了这两个参数我就能发送数据了,我这里的地址只有一个字节,DMA的高地址也就没用了
哪像你发个数据那么多if else if else ,这还不够还要memcpy来memcpy去的
就这样的代码还敢说是高效,跟我的没法比,我这项目都是高动态的,如果用你的代码发送一次数据就要几百毫秒,等上位机收到数据这一贞数据都报废了
还有别跟我说实验不实验的,我这串口函数已经写了好多个项目了,没有任何问题,我这代码也是旧项目上复制过来的
本帖最后由 tzz1983 于 2023-11-27 15:51 编辑
QQ624353765 发表于 2023-11-27 14:19
就拿你这个发送函数来说,发个数据这么多代码不知道干什么用的,发送数据包每个包一个字节,你这代码 ...
再说就是和你争了,没有意思, 我知道你做了什么.
你说的几个问题我可以回答你:
1.这是一个无协议循环容池, 和你那东西不一样, 这个容池的数据是不做收到多少字节标记的.(你自己的代码, 自已做个标记, 当然是整得明明白白的.) 但这不通用, 你后级链接一个协议, 是不是要重新做一下代码?
2.为什么要做长度判断?你不明白为什么我自己不知道我自己发多少数据, 说实话, 我也不知道, 因为我发的不尽有我要发的数据, 还有别人发给我的. 或者说如果这里不判断,那就要在应用层上判断. 这里判断就最保险的.
3.通常都是发数据包, 而不是一个字节一个字节的发. 你说的效率问题是没有的. 又或者说你不会整个包然后再一起发?还是说你发送数据前不用准备数据?
4.为什么要用一个循环容池?应用层的数据应该要和底层驱动的数据隔离的, (而不是像你那样直接发一个应用层的数据包, 然后告诉对方我发了多少字节)
我现在发了一帧数据, 就是写入容池, 然后就不用管了, 下一段代码(或另一个任务)在上一帧数据没发完前, 仍然是是可以送数据的.比如我可以发送一个包给设备B, 紧接着又发一个包给设备C. 这对于通信效率来说是提高的. 而不是降低.
5. 你说的那个电机控制, 也就是说, AB 两设备通信,你的高效是狭义的, 在特定的条件下, 谁都可以优化一些. 比如说你不需要用地址高字节或数量高字节, 是指在特定的条件下, 是你在特定的情况下不需要用, 而不是所有人都不需要用, 这区别很大. 请问你规定了我不允许发大于256字节的数据包了吗
QQ624353765 发表于 2023-11-27 14:19
就拿你这个发送函数来说,发个数据这么多代码不知道干什么用的,发送数据包每个包一个字节,你这代码 ...
你的代码有问题,我说说, 你看看:
1.你定义的那个结构体局部变量Q,是个局部变量哦, 当退出函数时生命周期就结束了,然面你的DMA还在使用这个变量.这有两种可能, 一是编绎器的变量覆盖刚好放过了你, 运气,另一种可能是你禁用了变是覆盖.
第一种可能是BUG, 对于第二种可能来说没有必要禁用变是覆盖, 前面加个static啥都解决.
2. 这张你看看, 你用收到的第一个字节映射一个包长度, 这种做法非常危险, 如果收到的数据受到干扰, 那会给DMA一个错误的长度,(你不会告诉我你是固定长度吧{:lol:}) 然后DMA的写会超出范围, 和指针越界差不多.
不要说你实际运行一点问题也没有, 硬件直接对连出干扰的情况确实比较少, 但不代表没有.而且指针越界危害无穷.也就是说你一个串口代码可能影响整机.
如果你这个代码放在无线模块上, 收不到头字节都很正常 ,何况是收到一个错的数据, 那再正常不过了. 那些成熟的通信代码, 比如IP报文, USB报文等, 不但整包有CRC, 连报头都有CRC,见过用头字节变象用作接收长度的吗?
本帖最后由 QQ624353765 于 2023-11-28 19:59 编辑
tzz1983 发表于 2023-11-28 10:28
你的代码有问题,我说说, 你看看:
1.你定义的那个结构体局部变量Q,是个局部变量哦, 当退出函数 ...
这些情况我都考虑全了,没有问题
首先发送的数据包是在pdata中,pdata只用来存零时数据包,数据包长度不超过256字节,退出函数是不影响pdata中的数据
接收的数据在xdata中,接收数据包的地址是固定的,是互不影响的
data,bdata,idata,pdata,xdata这些内存我都有很合理的分配,这么多年的编程经验可以说能做到互不影响
接收的首字节不是长度,是指令号,每个指令都是能查询到长度的,这个项目对数据传输有要求,我这里用的是校验和,能快速的校验指令是否是错误的,当然有一定的机率是无效的,一般自然形成的错误都能过滤掉,除非你非要发送错误的指令,那就没办法了
再说发送错误的指令CRC也检测不出来。我倒是觉得这里使用校验和以足够
如果发送的数据包超出长度通过校验和验证后舍弃,如果发送的数据短了通过定时器监控退出DMA并作废本次传输
我这里没用定时器监控,也没去处理,因为接收到长度比指令短的数据直接让他一直等待,等下一批数据到来时DMA就能重置,这里就当线路上有干扰误触发的,直接忽略
向高手学习{:4_196:} 两位大师, 帮忙看看, 如何解决不同类型的数据, 统一转换成 long类型.
对 char, int, long, 指针类 p, 都可以前面加 (long)强制转换成 long 类型.
例如:long dat = (long) a;// a 可为 char, int, long 变量 和 各种 指针类 数据, 同时支持 整型常数.
怎样将 浮点数 转换成 long 类型?
目前已知的方法是 用指针, 强制转换,
例如:long dat =*(long*) &f; // 仅支持浮点数变量, 不支持 浮点数常数.
现在的问题是, 希望能用一种相同的 强制转换 方式, 可将 各种 不同类型的数据, 统一转换成 long类型.