• SAM4E单片机之旅——11、UART之PDC收发


    使用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的接收后,就可以进行数据的接收了。

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

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

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

      void UART0_Handler(void)
      {
      	/*判断是否是由“接收缓冲区满”引发的中断 */
      	if (UART0->UART_SR & UART_SR_RXBUFF)
      	{
      		TransferRxBufAndRec(BUF_SIZE);
      	}
      }

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

    三、 TC的配置

    使用的通道为通道0:

    #define gUseTc TC0->TC_CHANNEL[0]

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

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

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

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

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

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

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

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

      void TC0_Handler(void)
      {
      	uint32_t status = gUseTc.TC_SR;
      
      	/* 判断中断是否为RC比较触发的 */
      	if (status & TC_SR_CPCS)
      	{
      		PDC_UART0->PERIPH_PTCR = PERIPH_PTCR_RXTDIS;
      		/* 计算PDC中接收到的数据的大小 */
      		const int rec_size = BUF_SIZE - (PDC_UART0->PERIPH_RCR);
      		if (rec_size != 0)
      		{
      			TransferRxBufAndRec(rec_size);
      		}
      		PDC_UART0->PERIPH_PTCR = PERIPH_PTCR_RXTEN;
      	}
      }
  • 相关阅读:
    leetcode刷题 650~
    leetcode刷题 633~
    发送udp报文
    SIP (Session Initiation Protocol) 协议
    你所不知道的replace
    uni-app(六)生成海报图片路径问题
    uni-app(五)小程序的一些注意事项,踩坑
    uni-app(四)小程序里的vuex
    uni-app(三)组件、插件使用,引入字体
    uni-app(二)接口请求封装,全局输出api
  • 原文地址:https://www.cnblogs.com/h46incon/p/3438351.html
Copyright © 2020-2023  润新知