注册 登录
查看: 1642|回复: 12

[KL26] 【转】KL26通过UART的DMA方式发送数据包(Send data package through UART with DM...

[复制链接]
发表于 2014-12-3 10:36:09 | 显示全部楼层 |阅读模式
以DMA方式通过UART发送数据应该是工程应用中很常用的一种方式了,尤其是在需要频繁发送数据或者数据包长度较大的场合,如果使用传统的UART查询或者中断方式发送和接收数据,对CPU资源的占用将是极大的浪费,带操作系统的应用还好些,如果是纯粹的前后台程序有时不能容忍的,所以DMA方式是很恰当的选择。而本篇以Kinetis L系列为例介绍一下以DMA方式通过UART端口发送长数据包,当然不同于K系列复杂强大的eDMA功能,L系列的DMA模块配置起来还是比较简单的。

测试平台:IAR6.7 + KL26 FRDM
测试代码:FRDM-KL26Z_SC\FRDM-KL26Z_SC_Rev_1.0\klxx-sc-baremetal\build\iar\uart0_dma

       其实KL26的官方sample code中是自带uart0_dma例程的,但是实现的功能只是将UART口接收到的每一个字节的数据通过DMA方式再发送出去(即环形缓冲),这样用来作为一个功能演示的demo是可以,但是往往我们需要的是将某缓冲区的数据以DMA方式发送出去或者将接收到的数据以DMA方式写到某缓冲区这样的功能,为此我们就需要在原有的例程上进行修改从而达到我们的应用目的,这里给出几点需要修改的地方,并做了相关注释(整个工程见最后附件):
1)定义待发送缓冲区:
  1. /* array to be sended */

  2. uint8 testdata[]={"\nFreescale Kinetis KL26\n"};



2)设置DMA源地址:
  1. #define DMA0_DESTINATION  0x4006A007    /* the memory adress of UART0_D register */

  2. #define DMA0_SOURCE_ADDR  (uint32)testdata    /* define the source data array address */



3) 在DMA0_init()函数中修改发送数据包的长度:
  1. DMA_SAR0 = DMA0_SOURCE_ADDR;    //Set source address to UART0_D REG

  2. DMA_DSR_BCR0 = DMA_DSR_BCR_BCR(sizeof(testdata));    //Set BCR to know how many bytes to transfer

  3. DMA_DCR0 &= ~(DMA_DCR_SSIZE_MASK | DMA_DCR_DSIZE_MASK);    //Clear source size and destination size fields


4)添加源地址自动加1功能,因为之前的环形缓冲方式只是单字节数据,所以不需要源地址递增,但是由于我们这次需要发送整个数据包,所以这里我们就需要将源地址递增功能打开,而具体递增1,2还是4则取决于发送数据的最小单位(8bit,16bit or 32bit):
  1. /* Set DMA as follows: Source size is 8-bit size Destination size is 8-bit size Cycle steal mode External requests are enabled source address increments 1 automatically */

  2. DMA_DCR0 |= (DMA_DCR_SSIZE(1) | DMA_DCR_DSIZE(1) | DMA_DCR_CS_MASK | DMA_DCR_ERQ_MASK | DMA_DCR_EINT_MASK | DMA_DCR_SINC_MASK);



5)配置DMAMUX通道为UART0 TX即发送通道(通道号为3),因为我们需要的是UART0_TX触发DMA传送:
  1. DMA_DAR0 = DMA0_DESTINATION;    //Set source address to UART0_D REG

  2. DMAMUX0_CHCFG0 = DMAMUX_CHCFG_SOURCE(3);    //Select UART0 TX as channel source

  3. DMAMUX0_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;    //Enable the DMA MUX channel



6)在UART0_DMA_init()函数中修改UART0发送缓冲区为空时即触发DMA发送:
  1. void UART0_DMA_init(void)

  2. {

  3. UART0_C2 &= ~(UART0_C2_TE_MASK | UART0_C2_RE_MASK);  //Disable UART0

  4. UART0_C5 |= UART0_C5_TDMAE_MASK;                      // Turn on DMA request(Transmit) for UART0

  5. UART0_C2 |= (UART0_C2_TE_MASK | UART0_C2_RE_MASK);  //Enable UART0

  6. }



7)在DMA发送完成中断服务函数中禁掉DMA通道,实现单次发送,即每个数据包发送完成之后即停止发送,否则不禁掉的话会一直触发DMA发送,造成串口堵塞:
  1. void DMA0_IRQHandler(void)

  2. {  /* Create pointer & variable for reading DMA_DSR register */

  3. volatile uint32_t* dma_dsr_bcr0_reg = &DMA_DSR_BCR0;

  4. uint32_t dma_dsr_bcr0_val = *dma_dsr_bcr0_reg;

  5. if (((dma_dsr_bcr0_val & DMA_DSR_BCR_DONE_MASK) == DMA_DSR_BCR_DONE_MASK)

  6.      | ((dma_dsr_bcr0_val & DMA_DSR_BCR_BES_MASK) == DMA_DSR_BCR_BES_MASK)

  7.      | ((dma_dsr_bcr0_val & DMA_DSR_BCR_BED_MASK) == DMA_DSR_BCR_BED_MASK)

  8.      | ((dma_dsr_bcr0_val & DMA_DSR_BCR_CE_MASK) == DMA_DSR_BCR_CE_MASK))

  9. {

  10. DMA_DSR_BCR0 |= DMA_DSR_BCR_DONE_MASK;                //Clear Done bit

  11. DMA_DSR_BCR0 = DMA_DSR_BCR_BCR(sizeof(testdata));      //Reset BCR

  12. dma0_done = 1;

  13. }

  14. /* once the array complete the transfer, then disable the DMA channel.*/

  15. DMAMUX0_CHCFG0 &= ~DMAMUX_CHCFG_ENBL_MASK;

  16. }


       将上述代码做完相应修改即可实现单次将内存缓冲区数据以DMA方式通过UART0发送出去,效果如下。此外,如果想周期性触发或者条件性触发,则只需再相应位置添加“DMAMUX0_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;”这句代码即可打开通道,然后立即会触发UART0_TX发送数据,然后待数据包发送完之后再次停止等待下次使能。
image

另外,关于DMA的传输速度的话,因为其独立占用一条自己的总线,其搬运时钟为系统时钟(即coreclock/Systemclock),相比于总线上的传输速度,本例程中整个数据包的发送时间主要是取决于UART串口的波特率*数据包长度。

出处:https://community.freescale.com/docs/DOC-99476

本帖子中包含更多资源

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

x

本帖被以下淘专辑推荐:

回复

使用道具 举报

发表于 2014-12-3 10:53:55 | 显示全部楼层
不知道用蓝牙 + DMA 会不会可以避免减少传输时间。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-12-3 10:54:26 | 显示全部楼层
单会 发表于 2014-12-3 10:53
不知道用蓝牙 + DMA 会不会可以避免减少传输时间。

波特率不变,传输时间就不变。不过可以释放CPU,让CPU干别的活。
回复 支持 反对

使用道具 举报

发表于 2014-12-3 11:21:14 | 显示全部楼层
顶一个,好的资料。
回复 支持 反对

使用道具 举报

发表于 2014-12-3 19:16:35 | 显示全部楼层
这是怎么回事啊

本帖子中包含更多资源

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

x
回复 支持 反对

使用道具 举报

发表于 2014-12-3 19:35:38 | 显示全部楼层
755850063@qq.co 发表于 2014-12-3 19:16
这是怎么回事啊

什么问题?发帖描述一下你的问题吧。
来自安卓客户端来自安卓客户端
回复 支持 反对

使用道具 举报

发表于 2014-12-3 19:46:06 | 显示全部楼层
TPM计数不准啊,应该记1000的,却记成了500多
回复 支持 反对

使用道具 举报

发表于 2014-12-3 19:47:51 | 显示全部楼层
是不是管脚坏了,而且定义的是PC12管脚,PTC13/9/8/7/6都能计数,这又是怎么回事啊
回复 支持 反对

使用道具 举报

发表于 2015-1-1 21:41:50 | 显示全部楼层
顶一个                                                     
回复 支持 反对

使用道具 举报

发表于 2015-6-16 21:10:02 | 显示全部楼层
请问您是用什么显示发送的数据的
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-6-16 21:13:51 | 显示全部楼层
CSW孤狼 发表于 2015-6-16 21:10
请问您是用什么显示发送的数据的

随便找串口助手都行
回复 支持 反对

使用道具 举报

发表于 2015-6-16 21:27:07 | 显示全部楼层
仰逸致 发表于 2015-6-16 21:13
随便找串口助手都行

刚查了一下,可以用超级终端
回复 支持 反对

使用道具 举报

发表于 2015-11-6 15:37:49 | 显示全部楼层
"CodeForge的这个代码或许可以解答你的问题:http://www.codeforge.cn/article/244807"
回复 支持 反对

使用道具 举报

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

本版积分规则

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