找回密码
 立即注册
查看: 21757|回复: 87

4组串口UART使用DMA收发 @32G系列,易用,高效,稳定 !精品实战代码

  [复制链接]

37

主题

1169

回帖

3078

积分

荣誉版主

积分
3078
发表于 2023-11-24 12:00:52 | 显示全部楼层 |阅读模式
4组串口UART使用DMA收发, 精品实战代码, 易用,高效,稳定 !
源自实际系统 @STC32G12K128 系列
非常容易使用, 将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
  */
4串口 DMA 实例代码 STC32G.rar (101.49 KB, 下载次数: 1034)








1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复

使用道具 举报 送花

24

主题

553

回帖

2533

积分

荣誉版主

积分
2533
发表于 2023-11-27 07:46:10 | 显示全部楼层

截图202604170828188340.jpg

还没有用过串口UART使用DMA收发,下载尝试一下。谢谢楼主!
回复

使用道具 举报 送花

已绑定手机

284

主题

490

回帖

2564

积分

金牌会员

积分
2564
发表于 2023-11-27 09:07:25 | 显示全部楼层
本帖最后由 QQ624353765 于 2023-11-27 09:11 编辑

S%5]OQ[41`G@LTQ0ZE%G]7L.png


你写的好臃肿,我就11行代码,实现串口不定长自动收发功能,下面两个函数是DMA中断函数,这是最高效的用法,整个收发过程只产生三次中断

向设备发送数据时,第一个数据使用串口中断接收,然后在中断中开启DMA准备接收接下来的数据,接收完所有数据DMA中断标记以接收完成

发送串口数据函数只需要两个参数,数据结构体的首地址和结构体的长度,数据发送完成DMA中断标记可接收下一次串口数据


而且效率极高,几乎不占用CPU时间,不需要查询串口,只需要处理接收完成标志位和串口数据发送函数,其他都是全自动的

点评

你喜欢是你自己的事情 当你做过大程序以后,你就会明白实验室的代码和产品应用的代码之间的区别了. 实验室的代码在于讲清楚怎么使用一个设备, 应用代码要综合考虑整体的性能. 我可以持续写入数据,上一帧没发送完就可  详情 回复 发表于 2023-11-27 09:49
回复

使用道具 举报 送花

37

主题

1169

回帖

3078

积分

荣誉版主

积分
3078
 楼主| 发表于 2023-11-27 09:49:33 | 显示全部楼层
本帖最后由 tzz1983 于 2023-11-27 10:06 编辑
QQ6243*** 发表于 2023-11-27 09:07
你写的好臃肿,我就11行代码,实现串口不定长自动收发功能,下面两个函数是DMA中断函数,这是最高效的用 ...

你喜欢是你自己的事情
当你做过大程序以后,你就会明白实验室的代码和产品应用的代码之间的区别了. 实验室的代码在于讲清楚怎么使用一个设备, 应用代码要综合考虑整体的性能.
我可以持续写入数据,上一帧没发送完就可以接着写下一帧数据,不需要标记数据, 不包含任何协议, 这是底层, 不是针对哪一个应用, 这就是区别, 串口是个低速设备, 产品级代码一般不会等它做完一个动作然后下一个动作. 写入缓存后立即返回, 可以接着写,也可以做别的事情.
接收也是这样, 不管有没有收到数据, 都是立即返回, 绝不会等.
查询? 你做的标志不用查询? 当然了,如果你用了RTOS可以发送信号量, 但是你没有.

点评

支持分享源码的大神  发表于 2023-12-30 22:36
回复

使用道具 举报 送花

已绑定手机

284

主题

490

回帖

2564

积分

金牌会员

积分
2564
发表于 2023-11-27 14:19:23 | 显示全部楼层
tzz1*** 发表于 2023-11-27 09:49
你喜欢是你自己的事情
当你做过大程序以后,你就会明白实验室的代码和产品应用的代码之间的区别了. 实验室 ...

$_7}D]LD3K$X(6$YL]`~QIC.png


就拿你这个发送函数来说,发个数据这么多代码不知道干什么用的,发送数据包每个包一个字节,你这代码一秒能发几次

还要把数据包复制到指定内存,不知道这一步有什么意义,还要判断有没有数据长度,你发数据时你不知道有没有数据吗。

K1$LSR5F5O$DD)%C{J]$I(H.png


把数据包准备好,我的只要取结构体地址和长度

}6IKI93HEQR)NN0TS1HCSUT.png

有了这两个参数我就能发送数据了,我这里的地址只有一个字节,DMA的高地址也就没用了

哪像你发个数据那么多if else if else ,这还不够还要memcpy来memcpy去的

就这样的代码还敢说是高效,跟我的没法比,我这项目都是高动态的,如果用你的代码发送一次数据就要几百毫秒,等上位机收到数据这一贞数据都报废了

还有别跟我说实验不实验的,我这串口函数已经写了好多个项目了,没有任何问题,我这代码也是旧项目上复制过来的

点评

[attachimg]28151[/attachimg] [attachimg]28152[/attachimg] 你的代码有问题, 我说说, 你看看: 1.你定义的那个结构体局部变量Q , 是个局部变量哦, 当退出函数时生命周期就结束了, 然面你的DMA还在使用这个变  详情 回复 发表于 2023-11-28 10:28
再说就是和你争了,没有意思, 我知道你做了什么. 你说的几个问题我可以回答你: 1.这是一个无协议循环容池, 和你那东西不一样, 这个容池的数据是不做收到多少字节标记的. (你自己的代码, 自已做个标记, 当然是整得明  详情 回复 发表于 2023-11-27 15:46
回复

使用道具 举报 送花

37

主题

1169

回帖

3078

积分

荣誉版主

积分
3078
 楼主| 发表于 2023-11-27 15:46:58 | 显示全部楼层
本帖最后由 tzz1983 于 2023-11-27 15:51 编辑
QQ6243*** 发表于 2023-11-27 14:19
就拿你这个发送函数来说,发个数据这么多代码不知道干什么用的,发送数据包每个包一个字节,你这代码 ...

再说就是和你争了,没有意思, 我知道你做了什么.

你说的几个问题我可以回答你:
1.这是一个无协议循环容池, 和你那东西不一样, 这个容池的数据是不做收到多少字节标记的.  (你自己的代码, 自已做个标记, 当然是整得明明白白的.) 但这不通用, 你后级链接一个协议, 是不是要重新做一下代码?
2.为什么要做长度判断?你不明白为什么我自己不知道我自己发多少数据, 说实话, 我也不知道, 因为我发的不尽有我要发的数据, 还有别人发给我的. 或者说如果这里不判断,那就要在应用层上判断. 这里判断就最保险的.
3.通常都是发数据包, 而不是一个字节一个字节的发. 你说的效率问题是没有的. 又或者说你不会整个包然后再一起发?  还是说你发送数据前不用准备数据?  
4.为什么要用一个循环容池?  应用层的数据应该要和底层驱动的数据隔离的, (而不是像你那样直接发一个应用层的数据包, 然后告诉对方我发了多少字节)  
我现在发了一帧数据, 就是写入容池, 然后就不用管了, 下一段代码(或另一个任务)在上一帧数据没发完前, 仍然是是可以送数据的.  比如我可以发送一个包给设备B, 紧接着又发一个包给设备C. 这对于通信效率来说是提高的. 而不是降低.
5. 你说的那个电机控制, 也就是说, A  B 两设备通信,  你的高效是狭义的, 在特定的条件下, 谁都可以优化一些. 比如说你不需要用地址高字节或数量高字节, 是指在特定的条件下, 是你在特定的情况下不需要用, 而不是所有人都不需要用, 这区别很大. 请问你规定了我不允许发大于256字节的数据包了吗

回复

使用道具 举报 送花

37

主题

1169

回帖

3078

积分

荣誉版主

积分
3078
 楼主| 发表于 2023-11-28 10:28:00 | 显示全部楼层
QQ6243*** 发表于 2023-11-27 14:19
就拿你这个发送函数来说,发个数据这么多代码不知道干什么用的,发送数据包每个包一个字节,你这代码 ...

截图202311281017255157.jpg
截图202311281017539895.jpg

你的代码有问题,  我说说, 你看看:

1.你定义的那个结构体局部变量Q  ,  是个局部变量哦, 当退出函数时生命周期就结束了,  然面你的DMA还在使用这个变量.  这有两种可能, 一是编绎器的变量覆盖刚好放过了你, 运气,  另一种可能是你禁用了变是覆盖.
第一种可能是BUG, 对于第二种可能来说没有必要禁用变是覆盖, 前面加个static啥都解决.



截图202311281018417266.jpg


2. 这张你看看, 你用收到的第一个字节映射一个包长度, 这种做法非常危险, 如果收到的数据受到干扰, 那会给DMA一个错误的长度,(你不会告诉我你是固定长度吧) 然后DMA的写会超出范围, 和指针越界差不多.

不要说你实际运行一点问题也没有, 硬件直接对连出干扰的情况确实比较少, 但不代表没有.  而且指针越界危害无穷.  也就是说你一个串口代码可能影响整机.

如果你这个代码放在无线模块上, 收不到头字节都很正常 ,何况是收到一个错的数据, 那再正常不过了.   那些成熟的通信代码, 比如IP报文, USB报文等, 不但整包有CRC, 连报头都有CRC,  见过用头字节变象用作接收长度的吗?

回复

使用道具 举报 送花

已绑定手机

284

主题

490

回帖

2564

积分

金牌会员

积分
2564
发表于 2023-11-28 19:39:34 | 显示全部楼层
本帖最后由 QQ624353765 于 2023-11-28 19:59 编辑
tzz1*** 发表于 2023-11-28 10:28
你的代码有问题,  我说说, 你看看:

1.你定义的那个结构体局部变量Q  ,  是个局部变量哦, 当退出函数 ...

这些情况我都考虑全了,没有问题
首先发送的数据包是在pdata中,pdata只用来存零时数据包,数据包长度不超过256字节,退出函数是不影响pdata中的数据
接收的数据在xdata中,接收数据包的地址是固定的,是互不影响的

data,bdata,idata,pdata,xdata这些内存我都有很合理的分配,这么多年的编程经验可以说能做到互不影响

接收的首字节不是长度,是指令号,每个指令都是能查询到长度的,这个项目对数据传输有要求,我这里用的是校验和,能快速的校验指令是否是错误的,当然有一定的机率是无效的,一般自然形成的错误都能过滤掉,除非你非要发送错误的指令,那就没办法了
再说发送错误的指令CRC也检测不出来。我倒是觉得这里使用校验和以足够

如果发送的数据包超出长度通过校验和验证后舍弃,如果发送的数据短了通过定时器监控退出DMA并作废本次传输

我这里没用定时器监控,也没去处理,因为接收到长度比指令短的数据直接让他一直等待,等下一批数据到来时DMA就能重置,这里就当线路上有干扰误触发的,直接忽略
回复

使用道具 举报 送花

0

主题

82

回帖

1864

积分

金牌会员

积分
1864
发表于 2023-11-30 11:06:11 | 显示全部楼层
向高手学习
回复

使用道具 举报 送花

50

主题

1276

回帖

5513

积分

荣誉版主

积分
5513
发表于 2023-11-30 14:49:05 | 显示全部楼层
两位大师, 帮忙看看, 如何解决不同类型的数据, 统一转换成 long类型.

对 char, int, long, 指针类 p, 都可以前面加 (long)强制转换成 long 类型.

例如:  long dat = (long) a;  // a 可为 char, int, long 变量 和 各种 指针类 数据, 同时支持 整型常数.



怎样将 浮点数 转换成 long 类型?

目前已知的方法是 用指针, 强制转换,

例如:  long dat =  *(long*) &f; // 仅支持浮点数变量, 不支持 浮点数常数.



现在的问题是, 希望能用一种相同的 强制转换 方式, 可将 各种 不同类型的数据, 统一转换成 long类型.

点评

直接赋值即可,例如: long a=11.0592; 这样的语句是合法的, 可显式转换,也可隐式转换, 舍去小数部分, 整数部分赋值给a, 赋值后a=11; 但是float可表示的数值范围比long大, 这是一种不安全的转换, 当数值大到无法用l  详情 回复 发表于 2023-11-30 18:53
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-9-22 09:42 , Processed in 0.085462 second(s), 96 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表