打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
<div>精读OSAL--DMA方式串行通信</div>

跳过初始化的内容.

先讲接收操作:

DMA方式的具体操作可以查看DMA的相关内容.

这里有意思的是缓冲区算法的实现,和中断方式还是有些区别.

               |BAUD|DXBUF|

               |..........|............|

               |..........|............|

               |..........|............|

               |..........|............|

               |..........|............|

               |..........|............|

               |..........|............|

    Tail--> |..........|............|

               |..........|............|

               |..........|............|

               |..........|............|

               |..........|............|<--Head

               |..........|............|

 

读函数里只对Head操作,每读一字节就上移一字节,清BAUD,读到tail就停止了.

那么为啥要记录波特率呢?我想了好久也没明白.在DMA里会具体说.

代码不上了.

 

这里说Tail怎么变化,其实Tail指明的就是数据尾在哪里.

来自串口的数据通过DMA直接传到Tail所指的内容.不过因为

DMA所以Tail不能递增.那么Tail什么时候改呢?

是在每个OSAL的循环里:HalUARTPollDMA()里 ,调用HalUARTRxAvailDMA()里.

那么怎么知道数据写到哪呢?这就是为什么要存BAUD的原因.

算法里取反存起来作为没数据的标记,当DMA传输时,直接将BAUD和DxBUF一起

传过来存起来,于是BAUD就变成正常的BAUD值,而不是取反值.

然后POLL里就查找那个还是反的BAUD就是没数据.Tail就改到前一个..

都在HalUARTRxAvailDMA().

 

 

到这读操作完成了,下面是DMA的写..

里面有说写操作推荐 用 中断 方式,这和上篇没区别就不提了.

这里说DMA方式:

                     |SEL=0|SEL=1|

                     |IDX=n |IDX=m|

                     |...........|...........|

                     |...........|...........|

                     |...........|...........|

                     |...........|...........|

    (IDX=n)-->|...........|...........|

                     |...........|...........|

                     |...........|...........|<--(IDX=m)

                     |...........|...........|

                     |...........|...........|

                     |...........|...........|

                     |...........|...........|

                     |...........|...........|

                     |...........|...........|

                     |...........|...........|

算法理角了也很简单,IDX是记录侍发数据长度,发送完一列就清IDX,同时改SEL指向另一列..

不过看代码还是有点难..所以来注释:

  1. static void HalUARTArmTxDMA(void)
  2. {
  3. halDMADesc_t *ch = HAL_DMA_GET_DESC1234(HAL_DMA_CH_TX);
  4. HAL_DMA_SET_SOURCE(ch, dmaCfg.txBuf[dmaCfg.txSel]);
  5. HAL_DMA_SET_LEN(ch, dmaCfg.txIdx[dmaCfg.txSel]);

  6. dmaCfg.txSel ^= 1; //注意这里取反了txSEL.
  7. dmaCfg.txTrig = 1;
  8. HAL_DMA_ARM_CH(HAL_DMA_CH_TX);

  9. HalUARTPollTxTrigDMA();

  10. if (DMA_PM)
  11. {
  12. HAL_UART_DMA_SET_RDY_OUT();
  13. }
  14. }

每次调用完,SEL都改成另一个.这是要注意.

static void HalUARTPollTxTrigDMA(void)  //这个也是在POLL里调用.
  1. {
  2. if ((UxCSR & CSR_TX_BYTE) == 0) // If no TXBUF to shift register transfer, then TXBUF may be MT.
  3. {
  4. if ((dmaCfg.txTick == 0) || ((uint8)(ST0 - dmaCfg.txTick) > HAL_UART_TX_TICK_MIN))
  5. {
  6. dmaCfg.txTick = 0;

  7. if (dmaCfg.txTrig && HAL_DMA_CH_ARMED(HAL_DMA_CH_TX)) //这里判断是否要人工触发DMA传输
  8. {
  9. HAL_DMA_MAN_TRIGGER(HAL_DMA_CH_TX);
  10. }
  11. dmaCfg.txTrig = 0;
  12. }
  13. }
  14. else
  15. {
  16. UxCSR = (CSR_MODE | CSR_RE); // Clear the CSR_TX_BYTE flag.
  17. dmaCfg.txTick = ST0;

  18. if (dmaCfg.txTick == 0) // Reserve zero to signify that the minimum delay has been met.
  19. {
  20. dmaCfg.txTick = 0xFF;
  21. }
  22. }
  23. }


 

  1. void HalUART_DMAIsrDMA(void)
  2. {
  3. if (dmaCfg.txIdx[dmaCfg.txSel]) //这里判断是否还有数据要传输
  4. {
  5. // If there is more Tx data ready to go, re-arm the DMA immediately on it.
  6. HalUARTArmTxDMA();

  7. // Indicate that the Tx buffer just finished is now free (re-arming did a ^= toggle of txSel).
  8. dmaCfg.txIdx[dmaCfg.txSel] = 0;//因为这前ARM的时候,SEL取反了,
  9. //刚刚完成DMA才进中断,在上面ARM中SEL又取反了,那么


  10. //此列已发送所有数据,IDX清零.
  11. }
  12. else
  13. {
  14. dmaCfg.txIdx[(dmaCfg.txSel ^ 1)] = 0; // Indicate that the Tx buffer just finished is now free.
  15. // 当前没数据,因为没有再ARM,所以要取反清零.
  16. // Clear the CSR_TX_BYTE flag & start the txTick to allow the possibility of an immediate
  17. // manual trigger from the next Write(), if it occurs more than one character time later.
  18. HalUARTPollTxTrigDMA();
  19. }

  20. dmaCfg.txMT = TRUE; // Notify CB that at least one Tx buffer is now free to use.
  21. }


 

  1. static uint16 HalUARTWriteDMA(uint8 *buf, uint16 len)
  2. {
  3. txIdx_t txIdx;
  4. uint8 txSel;
  5. halIntState_t his;

  6. HAL_ENTER_CRITICAL_SECTION(his);
  7. txSel = dmaCfg.txSel;
  8. txIdx = dmaCfg.txIdx[txSel];
  9. HAL_EXIT_CRITICAL_SECTION(his);

  10. // Enforce all or none.
  11. if ((len + txIdx) > HAL_UART_DMA_TX_MAX)
  12. {
  13. return 0;
  14. }

  15. (void)memcpy(&(dmaCfg.txBuf[txSel][txIdx]), buf, len);

  16. HAL_ENTER_CRITICAL_SECTION(his);
  17. /* If an ongoing DMA Tx finished while this buffer was being *appended*, then another DMA Tx
  18. * will have already been started on this buffer, but it did not include the bytes just appended.
  19. * Therefore these bytes have to be re-copied to the start of the new working buffer.
  20. */
  21. if (txSel != dmaCfg.txSel)//这里是判断这个过程是否有中断发生,如果有SEL不同,要重要整理过数据.
  22. {
  23. HAL_EXIT_CRITICAL_SECTION(his);
  24. txSel ^= 1;

  25. (void)memcpy(&(dmaCfg.txBuf[txSel][0]), buf, len);
  26. HAL_ENTER_CRITICAL_SECTION(his);
  27. dmaCfg.txIdx[txSel] = len;
  28. }
  29. else
  30. {
  31. dmaCfg.txIdx[txSel] = txIdx + len;
  32. }

  33. // If there is no ongoing DMA Tx, then the channel must be armed here.
  34. if (dmaCfg.txIdx[(txSel ^ 1)] == 0)
  35. {
  36. HAL_EXIT_CRITICAL_SECTION(his);
  37. HalUARTArmTxDMA();
  38. }
  39. else
  40. {
  41. dmaCfg.txMT = FALSE;
  42. HAL_EXIT_CRITICAL_SECTION(his);
  43. }
  44. return len;
  45. }


到此对DMA的UART就没什么疑问了.

 

 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Z-Stack串口DMA方式
Stm32cubeMx配置ADC多通道采集 – 电子创客营
协议中UART的两种模式
Robomaster电控入门(2)DR16&DT7接收与解码
第21章 DMA
GD32使用ST的HAL库和GD官方库的一些体会
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服