使用PDC进行数据的收发能减少CPU的开销。这次就使用PDC进行UART数据的接收与发送,同时,也利用TC也实现了PDC的接收超时。

PDC是针对外设的DMA控制器。对比DMA控制器,它更为简便,与相应外设的结合也更为紧密。比如说,要配置PDC时,首先要启用相应的外设的时钟;同时PDC收发的状态是通过外设上的寄存器反映出来的;甚至中断也是通过相应外设产生的。

使用PDC时,只需设置好传输时内存的地址,以及传输长度,就可以在外设和内存之前进行数据传输了。而SAM4的PDC甚至还提供了一个类似FIFO的功能:可以在进行本次传输的同时指定下次传输时的地址和长度,然后在本次传输结束时开始下一次传输。

一、 实现思路

本次会使用两组缓冲区,分别用来数据的接收和发送。在接收数据完成后,就让PDC把这个缓冲区的数据发送出去,并且使用另一个缓冲区进行数据接收。

使用PDC发送数据较为简单,只需设置好需要发送的数据的地址和长度即可。

但是在使用PDC接收数据的时,如果未接收足够指定数目的数据,是不会产生中断的。在这里使用TC来进行PDC接收数据时的等待超时处理

UART的引脚在没有数据传输时,是一直保持在高电平状态的。即只在有数据传输时,才会有电平的切换。而TC可以使用外部信号进行触发以重置计数器。这样一来,就可以让UART在接收数据的同时,不断对TC的计数器进行重置。而在没有接收数据时,就会使得TC顺利步进到一个特定的值,从而产生一个中断。

二、 UART的PDC配置

UART和MCK的基本配置保持不变:MCK为120 MHz,UART波特率为11520 Hz。

在配置PDC时,需要确保已经开启了相应UART的时钟,否则配置不生效。

  1. 缓冲区和PDC的配置。配置完成,且启用UART的接收后,就可以进行数据的接收了。

    1. /* 缓冲区 */
    2. #define BUF_SIZE 8
    3. uint8_t BUF1[BUF_SIZE];
    4. uint8_t BUF2[BUF_SIZE];
    5. uint8_t* RX_BUF;
    6.  
    7. /* 先设置好接收的BUF */
    8. RX_BUF = BUF1;
    9. PDC_UART0->PERIPH_RPR = RX_BUF;
    10. PDC_UART0->PERIPH_RCR = BUF_SIZE;
    11.  
    12. /* 使能输入输出*/
    13. PDC_UART0->PERIPH_PTCR = PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN;
  2. 中断设置。PDC的中断是通过相应外设产生的,所以这里需要对UART的中断进行配置。

    1. /* 启用缓冲区满中断*/
    2. UART0->UART_IER = UART_IER_RXBUFF;
    3. /* 在NVIC中启用中断,将优先级设置为1*/
    4. NVIC_DisableIRQ(UART0_IRQn);
    5. NVIC_ClearPendingIRQ(UART0_IRQn);
    6. NVIC_SetPriority(UART0_IRQn, 1);
    7. NVIC_EnableIRQ(UART0_IRQn);
  3. 将接收缓冲区的数据通过PDC发送出去,并开始下一次数据的接收。

    1. /* 参数size: 表示接收缓冲区中需要发送的数据的长度 */
    2. void TransferRxBufAndRec(int size)
    3. {
    4. /* 等待发送完成 */
    5. while(!(UART0->UART_SR & UART_SR_TXBUFE))
    6. ;
    7.  
    8. /* 通过PDC发送 */
    9. PDC_UART0->PERIPH_TPR = RX_BUF;
    10. PDC_UART0->PERIPH_TCR = size;
    11.  
    12. /* 使用另一个缓冲区继续接收 */
    13. RX_BUF = (RX_BUF == BUF1) ? BUF2 : BUF1;
    14. PDC_UART0->PERIPH_RPR = RX_BUF;
    15. PDC_UART0->PERIPH_RCR = BUF_SIZE;
    16. }
  4. UART的中断处理函数。在中断时,只需调用上面的函数,将接收缓冲区的内容重新发送出去即可。

    1. void UART0_Handler(void)
    2. {
    3. /*判断是否是由“接收缓冲区满”引发的中断 */
    4. if (UART0->UART_SR & UART_SR_RXBUFF)
    5. {
    6. TransferRxBufAndRec(BUF_SIZE);
    7. }
    8. }

这样配置完成后,删除上一节中UART收发数据的代码,即可完成数据的收发了。

三、 TC的配置

使用的通道为通道0:

  1. #define gUseTc TC0->TC_CHANNEL[0]

使TC工作在波形输出模式下,将TIOB引脚(PA1)用做外部事件引脚,短接它和UART0接收引脚,即短接PA1和PA9引脚。在配置完成后,若500ms内没有数据接收,则强制开始数据的发送。

  1. 使能TC时钟,及GPIO设置。

    1. PMC->PMC_PCER0 = (1 << ID_TC0);
    2.  
    3. const uint32_t TIOB_PIN = PIO_PA1;
    4. PIOA->PIO_PDR = TIOB_PIN;
    5. PIOA->PIO_ABCDSR[0] |= TIOB_PIN;
    6. PIOA->PIO_ABCDSR[1] &= ~TIOB_PIN;
  2. TC模式设置。

    利用TC的RC比较时产生的中断进行超时提醒,TIOB引脚电平的下降沿TC的触发。由于进行TC触发时也会开启时钟,所以在RC比较时暂停时钟。

    由于超时时间可能较长,且精度要求不高,让TC使用慢时钟SLCK就可以了。

    1. gUseTc.TC_CMR =
    2. TC_CMR_WAVE /* 波形模式 */
    3. | TC_CMR_TCCLKS_TIMER_CLOCK5 /* 时钟5: SLCK */
    4. | TC_CMR_WAVSEL_UP_RC /* 波形仅上升,且RC比较时触发 */
    5. | TC_CMR_CPCSTOP /* RC 比较时自动停止时钟 */
    6. | TC_CMR_EEVT_TIOB /* 设置为外部事件为TIOB */
    7. | TC_CMR_EEVTEDG_FALLING /* 外部事件下降沿触发 */
    8. | TC_CMR_ENETRG /* 使能外部事件 */
    9. ;
  3. RC设置,以及TC启用。在RC比较后,计数器将暂停工作。在下次UART数据的接收时,TIOB引脚的信号会触发TC以重新开始计数。

    1. /* UART的PDC接收时等待超时时间 */
    2. #define UART_RX_WAIT_MS 500
    3.  
    4. /* 设置RC */
    5. const uint32_t rc_v = CHIP_FREQ_SLCK_RC * UART_RX_WAIT_MS / 1000;
    6. gUseTc.TC_RC = TC_RC_RC(rc_v);
    7.  
    8. /* 使能TC时钟,但不开始*/
    9. gUseTc.TC_CCR = TC_CCR_CLKEN;
  4. 中断设置。TC中断的优先级比UART的要高。

    1. /* RC 比较时产生中断 */
    2. gUseTc.TC_IER = TC_IER_CPCS;
    3.  
    4. /* NVIC , 优先级设置为0 */
    5. NVIC_DisableIRQ(TC0_IRQn);
    6. NVIC_ClearPendingIRQ(TC0_IRQn);
    7. NVIC_SetPriority(TC0_IRQn, 0);
    8. NVIC_EnableIRQ(TC0_IRQn);
  5. 中断处理。中断处理中过程中禁用PDC数据的接收,以免丢失数据。

    1. void TC0_Handler(void)
    2. {
    3. uint32_t status = gUseTc.TC_SR;
    4.  
    5. /* 判断中断是否为RC比较触发的 */
    6. if (status & TC_SR_CPCS)
    7. {
    8. PDC_UART0->PERIPH_PTCR = PERIPH_PTCR_RXTDIS;
    9. /* 计算PDC中接收到的数据的大小 */
    10. const int rec_size = BUF_SIZE - (PDC_UART0->PERIPH_RCR);
    11. if (rec_size != 0)
    12. {
    13. TransferRxBufAndRec(rec_size);
    14. }
    15. PDC_UART0->PERIPH_PTCR = PERIPH_PTCR_RXTEN;
    16. }
    17. }

SAM4E单片机之旅——11、UART之PDC收发的更多相关文章

  1. SAM4E单片机之旅——12、USART

    清楚了UART的用法之后,现在来研究一下USART的用法.和上一次差不多,这次也通过USART的串口来实现和PC的通信.和上一次不同的是,USART本身就有接收超时的功能,所以这次就不用TC了. US ...

  2. SAM4E单片机之旅——17、通过UART进行标准IO

    交互还是很有必要的,而且使用键盘和显示器的交互效率还是很高的.当然,可以直接使用UART进行字符的输入和输出.但是又何必浪费了C的标准输入输出的格式控制之类的功能呢? 这次内容就是使用scanf() ...

  3. SAM4E单片机之旅——10、UART与MCK之PLL

    为使用更更高的波特率,则需要更更高的外设时钟的频率.这个时候就需要用到锁相环(PLL)了.锁相环可以对输入的时钟进行分频.升频后进行输出.MCK可以使用的锁相环为PLLA,而PLLA的输入时钟为MAI ...

  4. SAM4E单片机之旅——9、UART与MCK之MAINCK

    为得到更高的带宽,需要使用更高的波特率.UART波特率的计算已经介绍过了,现在就尝试下调整外设的时钟频率.可以有多种方法调整外设时钟(MCK)的频率,这里先介绍先主要时钟(MAINCK)的设置,其中包 ...

  5. SAM4E单片机之旅——8、UART初步

    通信还是比让LED灯闪烁实用得多的. 这次试试使用UART,实现开发版和PC间的通信.功能比较简单,就是把PC发向开发版的内容发送回去.这次主要介绍一下UART的配置,至于通信,则使用较为简单的不断查 ...

  6. SAM4E单片机之旅——19、CAN间通信

    CAN协议具有良好的可靠性,在工业中应用广泛.这次就先熟悉CAN的基本功能. 开发板有两个CAN,每个CAN有8个信箱.这次内容是从CAN0的信箱0发送数据到CAN1的信箱0. 除本次使用的功能外,C ...

  7. SAM4E单片机之旅——18、通过AFEC(ADC)获取输入的电压

    很多时候,一个电压不仅仅需要定性(高电平或者低电平),而且要定量(了解具体电压的数值).这个时候就可以用到模数转换器(ADC)了.这次的内容是测量开发板搭载的滑动变阻器(VR1)的电压,然后把ADC转 ...

  8. SAM4E单片机之旅——24、使用DSP库求向量数量积

    DSP(Digital Signal Processing,数字信号处理)中会使用大量的数学运算.Cortex-M4中,配置了一些强大的部件,以提高DSP能力.同时CMSIS提供了一个DSP库,提供了 ...

  9. SAM4E单片机之旅——23、在AS6(GCC)中使用FPU

    浮点单元(Floating Point Unit,FPU),是用于处理浮点数运算的单元. 为使用FPU,除了需要启用FPU外,还需要对编译器进行设置,以使其针对浮点运算生成特殊的指令.虽然在Atmel ...

随机推荐

  1. Codevs 1169 == 洛谷 P1006 传纸条

    ---恢复内容开始--- 1169 传纸条 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 小渊和小轩是好朋友也是同班同学,他 ...

  2. 转 C++中不能声明为虚函数的有哪些函数

    传送门 C++中不能声明为虚函数的有哪些函数 常见的不不能声明为虚函数的有:普通函数(非成员函数):静态成员函数:内联成员函数:构造函数:友元函数. 1.为什么C++不支持普通函数为虚函数? 普通函数 ...

  3. linux信号------探步

    前言 Linux以进程为单位来执行程序.我们可以 将计算机看作一个大楼,内核(kernel)是大楼的管理员,进程是大楼的房客.每个进程拥有一个独立的房间(属于进程的内存空间),而每个房间都是不允 许该 ...

  4. 阿里云oss教程

    OSS是提供非结构化数据存取的服务.对于刚开始使用OSS的用户,非结构数据可以理解为word文档.PDF.PPT.EXCEL表格.MP3.MKV.RMVB.HTML等各种类型文件.OSS提供API去进 ...

  5. react native 添加mobx

    "babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-preset-react-n ...

  6. 通配符、正则表达式、python去重

    1.linux通配符 *:代表所有字符(0到多个); ?:代表一个字符; ;:连续不同命令之间的分隔符; #:配置文件注释; |:管道; ~:当前用户的家目录; -:上一次所在的路径; $:变量前面需 ...

  7. Codeforces A. Bear and Big Brother

    ...不行.这题之后.不做1000分以下的了.很耻辱   A. Bear and Big Brother time limit per test 1 second memory limit per t ...

  8. idea的快捷键和操作

    IntelliJ Idea 常用快捷键列表   修改方法如下: 点击 文件菜单(File) –> 点击 设置(Settings… Ctrl+Alt+S), –> 打开设置对话框. 在左侧的 ...

  9. Markdown的css样式源码

    http://www.cnblogs.com/zhangjk1993/p/5442676.html https://github.com/zhangjikai/markdown-css https:/ ...

  10. 邁向IT專家成功之路的三十則鐵律 鐵律十三:IT人理財之道-知足

    身為一位專業的IT人士,工作上不僅要做到滿足興趣與專業熱忱,當然也要做到能夠滿足荷包.現代人賺錢不是問題,但花錢卻出了很大問題,親愛的IT朋友們,請不要將您辛苦賺來的錢花在想要的東西上,實際上需要的卻 ...