搜索
bottom↓
回复: 58

STM32103 USART DMA发送完成判断方法分享

  [复制链接]

出0入0汤圆

发表于 2018-4-27 14:01:41 | 显示全部楼层 |阅读模式
本帖最后由 shuen729 于 2018-4-28 11:57 编辑

今天调代码的时候,遇到这个问题,USART在DMA模式下,进DMA发送完成中断后发现数据其实USART那边还没有真正的完成发送,论坛里面搜“DMA 发送完成”有一篇帖子,但是很遗憾不让看,要密码。
然后不得已,看参考手册,找到下面的时序图

然后在中断里面改了下,先把DMA禁止掉,然后等待TC置位,TC置位就可以确认所有数据已经送出。
void DMA1_Channel7_IRQHandler(void)
{
    if(SET==DMA_GetITStatus(DMA1_IT_TC7))
    {
        DMA_ClearITPendingBit(DMA1_IT_TC7);
        DMA_Cmd(DMA_USART2_TX, DISABLE);
        DMA_USART2_TX->CNDTR = 0;
        while(RESET == USART_GetFlagStatus(USART2,USART_FLAG_TC))
            {
            USART_ClearFlag(USART2,USART_FLAG_TC);
            }
        Usart2_TR_Sw(RS485_RX);
        return ;
    }
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

 楼主| 发表于 2018-4-27 14:32:44 | 显示全部楼层
void DMA1_Channel7_IRQHandler(void)
{
    if(SET==DMA_GetITStatus(DMA1_IT_TC7))
    {
        DMA_ClearITPendingBit(DMA1_IT_TC7);
        DMA_Cmd(DMA_USART2_TX, DISABLE);
        DMA_USART2_TX->CNDTR = 0;
        while(RESET == USART_GetFlagStatus(USART2,USART_FLAG_TC))
            {
            ;
            }
        USART_ClearFlag(USART2,USART_FLAG_TC);
        Usart2_TR_Sw(RS485_RX);
        return ;
    }
}

出0入0汤圆

发表于 2018-4-27 15:16:34 | 显示全部楼层
一般都是送出就不管了

出0入0汤圆

发表于 2018-4-27 16:14:50 | 显示全部楼层

  1.                         if(DMA_GetCurrDataCounter(DMA1_Channel4)==0) //返回当前DMA通道x剩余的待传输数据数目
  2.                         {
  3.                                 /* 关闭DMA传输 */
  4.                                 DMA_Cmd(DMA1_Channel7, DISABLE);
  5.                                
  6.                                 DMA1_Channel7->CNDTR =  USART2_tx_buf_size_DEF;   //重新设定传输数据个数  
  7.                                
  8.                                 DMA_Cmd(USART2_Tx_DMA_Channel_DEF, ENABLE);         //使能DMA channel x          再次发送
  9.                         }
复制代码




这段程序是我花了10分钟左右找到的。

电脑没找到,又到云盘,下载了2014年 2013年的备份文件找出来的。。。

出0入0汤圆

 楼主| 发表于 2018-4-27 17:33:30 | 显示全部楼层
zhongsandaoren 发表于 2018-4-27 15:16
一般都是送出就不管了

半双工的就要管,比如485

出0入0汤圆

 楼主| 发表于 2018-4-27 17:34:35 | 显示全部楼层
lyg407 发表于 2018-4-27 16:14
这段程序是我花了10分钟左右找到的。

电脑没找到,又到云盘,下载了2014年 2013年的备份文件找出来的 ...

这个只是DMA侧的数据吧,这个值为0的时候,实际上还有两个字节的数据还没有从串口送出去。

出0入0汤圆

发表于 2018-4-27 18:32:10 来自手机 | 显示全部楼层
可以开tc中断来判

出0入0汤圆

 楼主| 发表于 2018-4-28 11:25:36 | 显示全部楼层
cht-rtos 发表于 2018-4-27 18:32
可以开tc中断来判

试过,DMA模式下,USART的TC中断不进去

出0入0汤圆

发表于 2018-4-28 12:27:57 | 显示全部楼层
楼主厉害,以前我也遇到过类似的问题, 终于找到正解了, 不过还有个问题不知道楼主考虑过没有, 从单片机里出来, 给到485, 如果TC置位后立马把485改成接收,中间需要考虑485往外送的时间吗? 会不会出现单片机送出去了,485还没送完就改成接收模式的情况?

出0入4汤圆

发表于 2018-4-28 13:10:30 来自手机 | 显示全部楼层
cht-rtos 发表于 2018-4-27 18:32
可以开tc中断来判

开tc中断的话会不会每发一个字节都会中断一下?

出0入0汤圆

 楼主| 发表于 2018-5-2 08:28:57 | 显示全部楼层
右手戒指01 发表于 2018-4-28 12:27
楼主厉害,以前我也遇到过类似的问题, 终于找到正解了, 不过还有个问题不知道楼主考虑过没有, 从单片机 ...

我试过,TC置位后立即切换,所有的数据全部从485差分线上送出。不会有问题

出0入0汤圆

 楼主| 发表于 2018-5-2 08:29:37 | 显示全部楼层
xiaoergao 发表于 2018-4-28 13:10
开tc中断的话会不会每发一个字节都会中断一下?

不会,我试过,根本就不产生中断

出0入0汤圆

 楼主| 发表于 2018-5-2 08:31:10 | 显示全部楼层
右手戒指01 发表于 2018-4-28 12:27
楼主厉害,以前我也遇到过类似的问题, 终于找到正解了, 不过还有个问题不知道楼主考虑过没有, 从单片机 ...

但是,最好不要在中断里面等待,等待的时间差不多2ms多,时间太长了,做验证可以,做产品不行。

出0入0汤圆

发表于 2018-5-2 08:39:21 | 显示全部楼层
mark一下,dma和usart的使用。

出1310入193汤圆

发表于 2018-5-2 08:46:44 | 显示全部楼层
鼓掌  期待更多分享dma和usart的故事

出0入0汤圆

发表于 2018-5-2 08:59:08 来自手机 | 显示全部楼层
shuen729 发表于 2018-4-28 11:25
试过,DMA模式下,USART的TC中断不进去

我一直这么用,在tc中断中切换rs485收发方向控制,对你这个产生不了tc中断持怀疑态度

出0入8汤圆

发表于 2018-5-2 09:44:38 | 显示全部楼层

mark一下, 以后可以参考

出0入0汤圆

发表于 2018-5-2 09:52:39 来自手机 | 显示全部楼层
实际上就是这样,dma只是把数据送到了usart的dr,dr还要转移到sr。dr到sr的过程产生txe来触发dma,sr到端口输出完才产生tc。

出0入0汤圆

发表于 2018-5-2 09:56:37 | 显示全部楼层
IDLE中断可以。

出0入0汤圆

发表于 2018-5-2 10:03:23 | 显示全部楼层
空闲中断应该可以的

出0入0汤圆

 楼主| 发表于 2018-5-2 17:01:26 | 显示全部楼层

接收我是用的idle,发送还没有试过。

出0入0汤圆

发表于 2018-5-2 17:59:59 | 显示全部楼层
shuen729 发表于 2018-5-2 08:31
但是,最好不要在中断里面等待,等待的时间差不多2ms多,时间太长了,做验证可以,做产品不行。 ...

好的,谢谢楼主分享

出0入0汤圆

发表于 2018-5-2 18:09:30 来自手机 | 显示全部楼层
shuen729 发表于 2018-5-2 17:01
接收我是用的idle,发送还没有试过。

发送没有idle吧  只有空和发送完成

出0入0汤圆

发表于 2018-5-2 20:16:03 | 显示全部楼层
485通信,从DMA中断中尽快退出来,在非中断状态中,查询TC标志,再切换接收状态。
这是比较正统的做法。

还有一种方法是:(本论坛中可以查到电路图)
从硬件着手,485通信的接收与发送状态切换,不需要CPU的引脚干预,编程省心。
那个电路我测试过,某宝0.2x元1片的MAX3485芯片,最高速率256K,短距通信无误码。

出0入31汤圆

发表于 2018-5-2 20:28:25 | 显示全部楼层
开启TC中断,DMA传输完成(移位寄存器全部移出)前TC不会置位,当所有数据从串口的管脚移出后TC才会置位,这时候产生中断,在TC中断中可以设置数据发送完成标志、设置RS485方向控制,几乎是完美的方案。

出0入0汤圆

发表于 2018-5-2 20:36:30 | 显示全部楼层
zchong 发表于 2018-5-2 20:28
开启TC中断,DMA传输完成(移位寄存器全部移出)前TC不会置位,当所有数据从串口的管脚移出后TC才会置位, ...

同意,这种做法。

出0入0汤圆

发表于 2018-5-2 21:16:42 来自手机 | 显示全部楼层
半双工硬件可以设计自动切换

出0入0汤圆

发表于 2018-5-2 21:17:11 | 显示全部楼层
之前的做法是dma完成中断开tc中断,现在直接开tc中断

出0入0汤圆

发表于 2018-5-3 08:41:50 | 显示全部楼层
mark谢谢楼主分享

出0入0汤圆

发表于 2018-5-3 12:11:24 | 显示全部楼层
开启TC中断是个好方法

出20入62汤圆

发表于 2018-5-3 15:12:48 | 显示全部楼层
接收:空闲中断+dma
发送:TC中断+dma
可能需求比较简单,这样配置可用

出0入0汤圆

发表于 2018-5-3 17:12:29 | 显示全部楼层
无需额外电平转换的485电路图:

出0入0汤圆

 楼主| 发表于 2018-5-18 15:47:42 | 显示全部楼层
cht-rtos 发表于 2018-5-2 08:59
我一直这么用,在tc中断中切换rs485收发方向控制,对你这个产生不了tc中断持怀疑态度 ...

今天抽空试了一下,开中断的方法是没有问题的。之前不行是因为有个宏用错了

出0入0汤圆

 楼主| 发表于 2018-5-18 15:48:26 | 显示全部楼层
zchong 发表于 2018-5-2 20:28
开启TC中断,DMA传输完成(移位寄存器全部移出)前TC不会置位,当所有数据从串口的管脚移出后TC才会置位, ...

是这样的

出0入0汤圆

发表于 2018-5-18 16:53:37 | 显示全部楼层
RS485 DMA uart

出140入115汤圆

发表于 2018-9-20 17:10:44 | 显示全部楼层
多谢分享!

出1310入193汤圆

发表于 2018-10-19 16:42:32 | 显示全部楼层
zchong 发表于 2018-5-2 20:28
开启TC中断,DMA传输完成(移位寄存器全部移出)前TC不会置位,当所有数据从串口的管脚移出后TC才会置位, ...

开启TC中断,DMA传输完成(移位寄存器全部移出)前TC不会置位,当所有数据从串口的管脚移出后TC才会置位,这时候产生中断,在TC中断中可以设置数据发送完成标志、设置RS485方向控制,几乎是完美的方案。
最佳最可靠的方法 谢谢

出0入0汤圆

发表于 2018-10-19 18:00:25 | 显示全部楼层
标记一下,我也要用DMA了

出0入17汤圆

发表于 2018-10-23 10:06:18 | 显示全部楼层
zchong 发表于 2018-5-2 20:28
开启TC中断,DMA传输完成(移位寄存器全部移出)前TC不会置位,当所有数据从串口的管脚移出后TC才会置位, ...

同意,其它接口的DMA发送,我也是这么操作的

出0入10汤圆

发表于 2018-10-23 10:36:06 | 显示全部楼层
学习一下。。。

出0入4汤圆

发表于 2018-11-22 23:07:56 | 显示全部楼层
本帖最后由 SCREA 于 2018-11-22 23:40 编辑

请教个问题,我的在DMA中断发送完成中 添加
  1. while ((USART2->SR & USART_SR_TC) == 0);
复制代码

不行,反倒是在DMA启动后添加这一句起作用,有没有人知道中断添加这句发送不起作用的原因?

PS:DMA中断添加这一局
  1. while ((uart_base->SR & USART_SR_TC) == 0);
复制代码
CNDTR写一个字符还输出。若是CNDTR发送多个字符,则字符都不输出了,貌似DMA后续请求都失败了

==============
若是如我所说,每次启动发送DMA就等while ((uart_base->SR & USART_SR_TC) == 0); 那么这个发送DMA 意义在哪里?(CPU不干其他事,就等这个结束)

出0入0汤圆

 楼主| 发表于 2018-11-23 10:04:19 | 显示全部楼层
SCREA 发表于 2018-11-22 23:07
请教个问题,我的在DMA中断发送完成中 添加

不行,反倒是在DMA启动后添加这一句起作用,有没有人知道中断 ...

首先,对你的问题我有个疑问USART2->SR和uart_base->SR到底是不是指向同一个地址?
第二,刚刚在f103上面测试,DMA中断中加入等待不影响DMA的后续发送,代码在下面:
/*usart1 TX DMA handle*/
void DMA1_Channel4_IRQHandler(void)
{
    if(RESET != DMA_GetITStatus(DMA1_IT_TC4))
    {
    while(0==(USART1->SR&USART_FLAG_TC));//**********这里加入等待(注意:实际工程中绝对不允许这么做)
    DMA_ClearITPendingBit(DMA1_IT_TC4);
    /*disable TC IT*/
    DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,DISABLE);
    /*stop translation*/
    DMA_Cmd(DMA1_Channel4,DISABLE);
    }
}
第三,实际工程中绝对不允许在中断中做耗时的操作,更不要说等待外设
最后,你说的“DMA 意义在哪里”---------启动DMA之前肯定要看之前的操作是否已经完成,就跟写flash操作一样,write之后要等待操作完成。DMA的意义在于,假如你发1000个字节,那么你只需启动DMA就可以了,不需要你去往USART1->DR里面写入1000次数据,可以把节约的时间用来做别的操作。如果启动之前不检查上次的发送是否完成,而此时直接停掉DMA修改参数再次启动,那么前面发出去的数据就是不完整的,而我们不希望发生这种情况。
*************************************************************************
如果不希望等待,那么建议你这么做:
1.创建一个环形队列,
2.需要往串口打印数据的时候先写入队列里面,
3.真正发送的流程从队列获取数据启动DMA从USART发出去,
4.软件流程是这样:我要往串口打印数据---》先写入环形队列---》检查DMA是否正在用USART往外发送---》如果是则return。
                                                                                                                                  |
                                                                                                                                  \/
                                                                                                                                 如果不是,则启动一次发送---》发送完成后进入USART的TC中断,中断中从队列获取数据再次启动发送。

出0入4汤圆

发表于 2018-11-23 11:04:18 来自手机 | 显示全部楼层
本帖最后由 SCREA 于 2018-11-23 11:09 编辑
shuen729 发表于 2018-11-23 10:04
首先,对你的问题我有个疑问USART2->SR和uart_base->SR到底是不是指向同一个地址?
第二,刚刚在f103上面 ...


谢谢回复,不是对你生气。别误会朋友。
1:那俩个地址一样的。
2:今晚测试下串口中断中的TC完成中断,以判断DMA发送完成。看帖子下面大家都成功了,在程序逻辑没错的情况下没有理由我的不成功。
3:DMA中断加了等到TC完成,确实没有发后续字节,不知道哪里出错了,o(╯□╰)o

出0入0汤圆

 楼主| 发表于 2018-11-23 12:00:26 | 显示全部楼层
SCREA 发表于 2018-11-23 11:04
谢谢回复,不是对你生气。别误会朋友。
1:那俩个地址一样的。
2:今晚测试下串口中断中的TC完成中断,以判 ...

没有觉得你对我生气了,难道我说话的语气不对?哈哈
祝你早日搞定

出105入79汤圆

发表于 2018-11-23 12:45:30 | 显示全部楼层
DMA有专门的中断

出0入0汤圆

发表于 2018-11-23 13:33:56 | 显示全部楼层
zhongsandaoren 发表于 2018-4-27 15:16
一般都是送出就不管了

我也是,DMA足够快了,而且用DMA的目的就是为了不用再管它了,可以去做别的事,如果用while结构实在不是很好的方法

出0入4汤圆

发表于 2018-11-24 16:39:49 | 显示全部楼层
shuen729 发表于 2018-11-23 12:00
没有觉得你对我生气了,难道我说话的语气不对?哈哈
祝你早日搞定

请教下,我测试稳定性时候。当每隔小于150ms发送一次50字节的数据,单片机就死了。若是不用DMA反倒没问题。
你的稳定吗?
我DMA1的所有通道全用上了

出0入0汤圆

 楼主| 发表于 2018-11-26 13:32:48 | 显示全部楼层
SCREA 发表于 2018-11-24 16:39
请教下,我测试稳定性时候。当每隔小于150ms发送一次50字节的数据,单片机就死了。若是不用DMA反倒没问题 ...

不好意思,这几天没上论坛。你可能已经把问题找出来了,哈哈。不过我还是回复下。
我都是先放进队列,然后打印的流程从队列里面取,满负荷的情况下也没有问题,是稳定的。
150ms一次发送50字节,就算是9600波特率也是可以发完的。
我重新看了下规格书,DMA1里面USART2的优先级是最低的,你排查下是不是优先级高的流程在作怪。
还有,你说的死了,具体你看下是死在那里了(进调试模式看下),是不是触发了硬件故障进入hard_fault了(如果DMA工作的时候地址非法了,很大可能进这里),那里面是个while(1)。找bug靠耐心,一步一步的来,最差的情况也只是多花点时间,最后一定搞得定。这种东西,搞清楚后再就不会犯,终生受益。祝早日搞清楚
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

出190入0汤圆

发表于 2018-11-26 13:57:09 | 显示全部楼层
我使用DMA发送完成中断,然后在中断中检测发送队列是否有数据,有的话继续开启中断...
/**
* 运行在DMA ISR中
* @param user_data
*/
static void _usart_tx_dma_cb(void *user_data) {
    usart_t *instance = (usart_t *) user_data;

    instance->tx.dma.ops->disable(&instance->tx.dma);

    queue_advance_rd(&instance->tx.queue, instance->tx.prev_trans_bytes);

    if (queue_used(&instance->tx.queue)) {
        queue_chunk_t chunk = queue_get_rd_chunk_no_wrap(&instance->tx.queue);
        instance->tx.prev_trans_bytes = chunk.size;
        instance->tx.dma.ops->set_trans_bytes(&instance->tx.dma, chunk.p_buf,
                chunk.size);
    }
}

出0入4汤圆

发表于 2018-11-26 21:33:03 | 显示全部楼层
本帖最后由 SCREA 于 2018-11-26 21:39 编辑
shuen729 发表于 2018-11-26 13:32
不好意思,这几天没上论坛。你可能已经把问题找出来了,哈哈。不过我还是回复下。
我都是先放进队列,然 ...


是的,找到原因了。现在只能做到最快18ms,把接到的50字节数据解析出来执行相应命令动作然后返回结果帧。
还可以快,没时间去做了。
好奇为何发的太快了CPU没死,倒是串口死了 ,但看寄存器都很正常。有时间去分析吧

对了,DMA全部用掉时会降低DMA速度。以前用1~3个通道没感觉,现在用了9个通道,压力测试还是很明显体现出来DMA快到极限了

出0入0汤圆

 楼主| 发表于 2018-11-27 08:38:48 | 显示全部楼层
解决了就好。谢谢分享过程和经验,有空了我再试一试你说的发的太快串口会死的现象,看看能不能出现

出100入113汤圆

发表于 2018-11-27 08:57:26 | 显示全部楼层
发快了会死?我一直把USART发送允许打开,DMA只负责写数据,不管发送,DMA完毕就关闭DMA,用USART1跑3000000bps,压力测试一点问题都没有。不知道你说的假死是什么,多半程序那里时序有点问题。

出0入0汤圆

发表于 2018-11-27 11:52:09 来自手机 | 显示全部楼层
关注这些细节问题,提高程序稳定性及可靠性

出0入4汤圆

发表于 2018-11-28 09:32:35 来自手机 | 显示全部楼层
saccapanna 发表于 2018-11-27 08:57
发快了会死?我一直把USART发送允许打开,DMA只负责写数据,不管发送,DMA完毕就关闭DMA,用USART1跑300000 ...

只负责写,不管发送,是什么意思。是DMA只负责接收,发送不用DMA?

出100入113汤圆

发表于 2018-11-28 10:05:50 | 显示全部楼层
SCREA 发表于 2018-11-28 09:32
只负责写,不管发送,是什么意思。是DMA只负责接收,发送不用DMA?

写数据到DR寄存器,和数据发送是两个步骤。DMA只负责把数据写到DR寄存器,写完数据就不管了,数据写到DR寄存器后,是USART接口负责发送出去的。只要一直开着发送允许位,写入DR寄存器的值是一定能发送出去的。有问题的是,DMA数据写完,有的觉得是USART发送完了,就把USART发送允许位关闭了。或者一次DMA没有完毕,要再次开启DMA,是要等上次DMA结束的。等等,这些细节不把握好,都会有问题。

我没用官方的库,都是自己定义寄存器,进行的寄存器操作,编写自己的库函数,所以对底层细节熟悉一些。就算调用库函数,也要熟悉底层操作,不然也容易出问题。

出100入113汤圆

发表于 2018-11-28 10:09:16 | 显示全部楼层
SCREA 发表于 2018-11-28 09:32
只负责写,不管发送,是什么意思。是DMA只负责接收,发送不用DMA?

DMA接收,我是一直开启的。DMA使用循环队列功能,只负责把DR寄存器的数据往内存中写,后台直接处理数据。这样接收中断都不用了,在高速通用的时候很有用。

出0入4汤圆

发表于 2018-12-3 20:25:36 | 显示全部楼层
本帖最后由 SCREA 于 2018-12-4 21:05 编辑
shuen729 发表于 2018-11-26 13:32
不好意思,这几天没上论坛。你可能已经把问题找出来了,哈哈。不过我还是回复下。
我都是先放进队列,然 ...


485半双工的定要注意切换,保险起见,准备发送数据时,把串口接收使能关闭。TCIE中断触发后再打开接收使能。 否则在DMA模式下更容易出现内存溢出
一个可怕的事情是非内存溢出,而是DMA模式下会把矩阵总线搞乱,真是学习了
全双工随便搞,反正接收和发送是俩个独立的影子寄存器
=============
为了不跑出地址,建议接收DMA开启循环模式。
现在1ms 收发无压力

出0入0汤圆

发表于 2018-12-4 07:26:09 来自手机 | 显示全部楼层
zchong 发表于 2018-5-2 20:28
开启TC中断,DMA传输完成(移位寄存器全部移出)前TC不会置位,当所有数据从串口的管脚移出后TC才会置位, ...

这里的TC是指dma的还是uart的tc?

出0入0汤圆

发表于 2018-12-4 17:24:18 | 显示全部楼层
谢谢楼主分享
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-19 00:31

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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