查看: 5238|回复: 1

[S32] S32K144使用DMA完成串口的通用代码(基于NXP SDK2.0和PE配置)

[复制链接]
  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3301

    主题

    6548

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32040
    最后登录
    2024-4-28
    发表于 2020-5-12 13:40:04 | 显示全部楼层 |阅读模式
    S32K144使用DMA完成串口的通用代码(基于NXP SDK2.0和PE配置)


    由于项目需要,要将S32K的串口使用DMA来实现,并且设计较为安全的机制来保证串口通信。所以自己调试了一下相关代码,供参考。
    1、S32K144的DMA串口实现
    我使用的是NXP提供的DS32,其中的PE对DMA配置串口相当友好
    在uart配置模块下选择传输类型为DMA
    2.png


    在DMA模块配置下,将对应的串口绑定传输通道即可
    3.png


    通过PE生成代码,此时项目生成的通用代码中就有了dma和uart的所有配置
    4.png
    2、DMA串口发送
    这一部分NXP的SDK提供了完善的解决接口,只需要调用LPUART_DRV_SendData接口就可以完成DMA发送。FEATURE_LPUART_HAS_DMA_ENABLE宏是默认打开的,可以仿真跟踪一下代码,可以发现只要配置了DMA相关模块,实际发送时调用的就是圈出来的这部分代码。

    5.png

    3、DMA轮询实现
    为了提高发送效率,所以不能使用等待的方式来完成每次串口的发送。所以设计了轮询的方式来做。具体的做法如下:
    1、设计一个全局的buf,只要调用了串口的发送,就立刻将要发送的数据放入这个buf中
    2、设计一个发送buf,在定时器中定期轮询DMA标记,如果DMA不忙,就去检查是否有数据要发送。
    3、检查与发送buf,如果有需要发送的数据,将这段数据拷贝到发送buf中,将预发送buf标记置位。同时启动发送,DMA通道标记置位。
    代码实现如下:
    1. <font size="3" face="微软雅黑">//定义三个串口的全局预发送buf
    2. typedef struct{
    3. uint8_t sBuf[SEND_LEN]; //发送缓存
    4. uint16_t head;                    //MSG在sbuff中起始位置
    5. uint16_t tail;                    //MSG在sbuff中结束位置
    6. }uart_send_info;
    7. uart_send_info uart0_send;
    8. uart_send_info uart1_send;
    9. uart_send_info uart2_send;
    10. //定义三个发送buf
    11. uint8_t uart0_sBuf[SEND_LEN];
    12. uint8_t uart1_sBuf[SEND_LEN];
    13. uint8_t uart2_sBuf[SEND_LEN];
    14. //添加三个dma发送完成标记
    15. volatile bool uart0TransferComplete=true;
    16. volatile bool uart1TransferComplete=true;
    17. volatile bool uart2TransferComplete=true;

    18. //发送预处理,只是将要发送的数据放入预处理buf中
    19. void pre_uart_send(uart_instance_t uartinstance,uint8_t *data,uint16_t len)
    20. {
    21.     uart_send_info * uartXSend;
    22.     if(UART0_INSTANCE==uartinstance)
    23.     {
    24.         uartXSend=&uart0_send;
    25.     }
    26.     if(UART1_INSTANCE==uartinstance)
    27.     {
    28.         uartXSend=&uart1_send;
    29.     }
    30.     if(UART2_INSTANCE==uartinstance)
    31.     {
    32.         uartXSend=&uart2_send;
    33.     }
    34.     //将data存入到对应的buf中去
    35.     if(uartXSend->tail+len<SEND_LEN)
    36.     {
    37.         memcpy((uartXSend->sBuf+uartXSend->tail),data,len);
    38.         //将tail移位
    39.         uartXSend->tail=(uartXSend->tail+len)%SEND_LEN;
    40.     }else{
    41.         memcpy((uartXSend->sBuf+uartXSend->tail),data,(SEND_LEN-uartXSend->tail-1));
    42.         memcpy(uartXSend->sBuf,(data+SEND_LEN-uartXSend->tail-1),len+uartXSend->tail-SEND_LEN);
    43.         uartXSend->tail=(len+uartXSend->tail-SEND_LEN)%SEND_LEN;
    44.     }
    45. }

    46. //串口发送代码,轮询时调用,将需要用到的DMA标记置位,如果总线忙,则不作任何处理,如果不忙,则启动发送,将预发送buf中的头指针移动到尾部
    47. void uart_send(uart_instance_t uartinstance,uint8_t *data,uint16_t len)
    48. {
    49.     lpuart_state_t * lpuartState;
    50.     uart_send_info * uartXSend;
    51.    
    52.         if(UART0_INSTANCE==uartinstance)
    53.         {
    54.                 lpuartState=&lpuart2_State;
    55.             uartXSend=&uart0_send;
    56.             uart0TransferComplete=false;
    57.         }
    58.         if(UART1_INSTANCE==uartinstance)
    59.         {
    60.                 lpuartState=&lpuart1_State;
    61.             uartXSend=&uart1_send;
    62.             uart1TransferComplete=false;
    63.         }
    64.             if(UART2_INSTANCE==uartinstance)
    65.         {
    66.                 lpuartState=&lpuart3_State;
    67.             uartXSend=&uart2_send;
    68.             uart2TransferComplete=false;
    69.         }   
    70.     if(!(lpuartState->isTxBusy)){
    71.         //开始发送,置位全局预发送buf标记
    72.         LPUART_DRV_SendData(uartinstance,data,len);
    73.         uartXSend->head=uartXSend->tail;
    74.         //将sendbuf清掉
    75.         memset(uartXSend,0,sizeof(uint8_t)*SEND_LEN);
    76.     }
    77.     // 如果忙,那么这次就不做发送   
    78. }

    79. //提供一个轮询接口,在定时器中调用,如果预发送中有数据,则返回需要发送的数据长度,同时将数据拷贝到发送buf中去,这一部分功能是在get_send_data中实现的
    80. uint16_t check_uart_buf_stat(uart_instance_t uartinstance)
    81. {
    82.     uart_send_info * uartXSend;
    83.     if(UART0_INSTANCE==uartinstance)
    84.     {
    85.         uartXSend=&uart0_send;
    86.     }
    87.     if(UART1_INSTANCE==uartinstance)
    88.     {
    89.         uartXSend=&uart1_send;
    90.     }
    91.     if(UART2_INSTANCE==uartinstance)
    92.     {
    93.         uartXSend=&uart2_send;
    94.     }
    95.     return get_send_data(uartinstance);
    96. }

    97. static uint16_t get_send_data(uart_instance_t uartinstance)
    98. {
    99.     uart_send_info * uartXSend;
    100.     uint8_t * send_buf;
    101.     uint16_t send_len;
    102.     if(UART0_INSTANCE==uartinstance)
    103.     {
    104.         uartXSend=&uart0_send;
    105.         send_buf = uart0_sBuf;
    106.     }
    107.     if(UART1_INSTANCE==uartinstance)
    108.     {
    109.         uartXSend=&uart1_send;
    110.         send_buf = uart1_sBuf;
    111.     }
    112.     if(UART2_INSTANCE==uartinstance)
    113.     {
    114.         uartXSend=&uart2_send;
    115.         send_buf = uart2_sBuf;
    116.     }
    117.     if(uartXSend->tail>uartXSend->head)
    118.     {
    119.         send_len = uartXSend->tail-uartXSend->head;
    120.         memcpy(send_buf,(uartXSend->sBuf+uartXSend->head),(uartXSend->tail-uartXSend->head));
    121.     }else if(uartXSend->tail<uartXSend->head)
    122.     {
    123.         send_len = SEND_LEN-uartXSend->head+uartXSend->tail;
    124.         memcpy(send_buf,(uartXSend->sBuf+uartXSend->head),(SEND_LEN-uartXSend->head-1));
    125.         memcpy((send_buf+SEND_LEN-uartXSend->head),uartXSend->sBuf,uartXSend->tail+1);
    126.     }else{
    127.         send_len = 0;
    128.     }
    129.     return send_len;
    130. }

    131. </font>
    复制代码
    至此,串口发送所有的接口提供完毕。用户代码中只需要使用pre_uart_send接口,不断向预处理buf中放入数据即可。所有的数据处理,都是通过定时器去轮询dma标记位来完成的。这部分代码如下。
    1. <font size="3" face="微软雅黑">//首先在DMA中断函数中添加标记,对应我的串口使用通道
    2. void EDMA_DRV_IRQHandler(uint8_t virtualChannel)
    3. {
    4.     const edma_chn_state_t *chnState = s_virtEdmaState->virtChnState[virtualChannel];

    5.     EDMA_DRV_ClearIntStatus(virtualChannel);

    6.     if (chnState != NULL)
    7.     {
    8.         if (chnState->callback != NULL)
    9.         {
    10.             chnState->callback(chnState->parameter, chnState->status);
    11.         }
    12.     }
    13.     //在对应串口的DMA通道中断时,将对应的发送完成标记置位。
    14.     if(5==virtualChannel)
    15.     {
    16.         uart0TransferComplete=true;
    17.     }
    18.     if(1==virtualChannel)
    19.     {
    20.         uart1TransferComplete=true;
    21.     }
    22.     if(7==virtualChannel)
    23.     {
    24.         uart2TransferComplete=true;
    25.     }
    26. }

    27. </font>
    复制代码
    定时器中断中轮询是否启动发送,处理逻辑代码如下
    1. <font size="3" face="微软雅黑">//每QUERY_UART_BUF_TICK个TICK查询一次串口发送buf
    2.     uart_tick_count++;
    3.     if((uart_tick_count%QUERY_UART_BUF_TICK)==0)
    4.     {
    5.             //将轮询标记置位
    6.             uart_tick_count = 0;
    7.             //轮询uart发送DMA通道的状态,暂时忽略通道状态做测试
    8.             if(uart0TransferComplete==true)
    9.             {
    10.                     //如果没有要发送的数据,则返回0,此时后面的判断也不需要做
    11.                     uint16_t len=check_uart_buf_stat(UART0_INSTANCE);
    12.                     if(len)
    13.                     {
    14.                             //如果串口0有数据,则查询对应串口是否忙状态,如果忙也不需要做操作,不忙则启动发送
    15.                             uart_send(UART0_INSTANCE,uart0_sBuf,len);       
    16.                     }
    17.             }
    18.             //......其他串口的处理逻辑也是一样
    19.     }

    20. </font>
    复制代码
    上述代码通过两个buf完全规避了串口总线的判忙等待,充分提高了DMA发送的效率。需要注意的是,由于预处理buf是预先开辟的,所以如果有长协议的快速通信,需要计算一下波特率是否满足,定时器中轮询接口的时长也要考虑,避免预处理buf填满,而没有启动发送的情况发生。



    版权声明:本文为CSDN博主「大牛眼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:http://blog.csdn.net/weixin_40983190/article/details/90106560

    我知道答案 目前已有1人回答
    签到签到
    回复

    使用道具 举报

    该用户从未签到

    0

    主题

    1

    帖子

    0

    新手上路

    Rank: 1

    积分
    7
    最后登录
    2023-5-18
    发表于 2023-5-13 16:21:19 | 显示全部楼层
    大牛,请教一下 这个串口采用dma接收长通信怎么做?
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-29 05:58 , Processed in 0.130618 second(s), 23 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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