Z-STACK之cc2530DMA驱动详解

        z-stack中DMA主要用于串口、FLASH控制器以及RADIO,串口中应用DMA在另外的文章讲,本章主要介绍DMA在FLASH控制器的应用。首先看cc2530的datasheet。

       The Direct Memory Access (DMA) Controller can be used to relieve the 8051 CPU core of handling data
movement operations, thus achieving high overall performance with good power efficiency. The DMA
controller can move data from a peripheral unit such as ADC or RF transceiver to memory with minimum
CPU intervention。

        DMA的作用就是为了减轻CPU的负担,它能在内存和外设单元直接传输数据而不经过CPU,这样更加提高系统效率。cc2530有5个独立的DMA通道,3个可以配置的DMA通道优先级,32个可以配置的传送触发事件,源地址和目标地址的独立控制,单独传送、数据块传送和重复传送模式,支持可变长度数据域的传送,可以工作在字或字节模式。在使用DMA通道之前先要配置DMA.我们看一下DMA如何配置的。

       DMA主要配置一下几个参数:

       Source address:The first address from which the DMA channel should read data

       Destination address: The first address to which the DMA channel should write the data read  from the
source address. The user must ensure that the destination is writable.

       VLEN setting: The DMA channel is capable of variable-length transfers, using the first byte or word to
set the transfer length. When doing this, various options are available regarding how to count the
number of bytes to transfer.

       Priority: The priority of the DMA transfers for the DMA channel with respect to the CPU and other
DMA channels and access ports.
       Trigger event:All DMA transfers are initiated by so-called DMA trigger events. This trigger either
starts a DMA block transfer or a single DMA transfer.

       Source and Destination Increment: The source and destination addresses can be controlled to
increment or decrement or not change.
       Transfer mode: The transfer mode determines whether the transfer should be a single transfer or a
block transfer, or repeated versions of these.
       Byte or word transfers: Determines whether each DMA transfer should be 8-bit (byte) or 16-bit
(word).
       Interrupt Mask: An interrupt request is generated on completion of the DMA transfer. The interrupt
mask bit controls whether the interrupt generation is enabled or disabled.
       M8: Decide whether to use seven or eight bits per byte byte for transfer length. This is only applicable
when doing byte transfers.

        看一下这个配置参数:

 

         
       

          这个是DMA配置结构中参数相应配置内容,根据不同需求采用不同配置。看hal_dma.h文件中

typedef struct {
  uint8 srcAddrH;
  uint8 srcAddrL;
  uint8 dstAddrH;
  uint8 dstAddrL;
  uint8 xferLenV;
  uint8 xferLenL;
  uint8 ctrlA;
  uint8 ctrlB;
} halDMADesc_t;       这个结构体刚好就是DMA的配置描述符,总共八个字节与上图中一一对应。至于每个配置元素的作用上面都有讲到,等会儿在FLASH应用中具体讲。配置完之后将配置结构体的地址赋值给DMA0CFGL:DMA0CFGH(对应于DMA0)或DMA1CFGL:DMA1CFGH(对应于DMA1-4)。

     在头文件中有几个宏定义HAL_DMA_SET_ADDR_DESC0(a),HAL_DMA_GET_DESC0(),HAL_DMA_ARM_CH(ch)   HAL_DMA_START_CH( ch )  HAL_DMA_SET_LEN( pDesc, len )  HAL_DMA_GET_LEN( pDesc )等,对照着配置描述符中几个很容易明白。

    hal_dma.c文件中声明量两个结构体变量dmaCh0和dmaCh1234[4],分别是通道0和1-4的配置描述符。

    DMA初始化函数

void HalDmaInit( void )
{
  HAL_DMA_SET_ADDR_DESC0( &dmaCh0 );
  HAL_DMA_SET_ADDR_DESC1234( dmaCh1234 );
  DMAIE = 1;
}

初始化就是将通道0-4的配置描述符地址赋值给寄存器DMAxCFGH和DMAxCFGH,然后使能DMA中断。

DMA中断函数

  

HAL_ISR_FUNCTION( halDmaIsr, DMA_VECTOR )
{
  extern void HalUARTIsrDMA(void);

  DMAIF = 0;

#if HAL_UART_DMA
  if (HAL_DMA_CHECK_IRQ(HAL_DMA_CH_TX))
  {
    HalUARTIsrDMA();
  }
#endif // HAL_UART_DMA

#if (defined HAL_SPI) && (HAL_SPI == TRUE)
  if ( HAL_DMA_CHECK_IRQ( HAL_DMA_CH_RX ) )
  {
    HAL_DMA_CLEAR_IRQ( HAL_DMA_CH_RX );
    npSpiRxIsr();
  }

  if ( HAL_DMA_CHECK_IRQ( HAL_DMA_CH_TX ) )
  {
    HAL_DMA_CLEAR_IRQ( HAL_DMA_CH_TX );
    npSpiTxIsr();
  }
#endif // (defined HAL_SPI) && (HAL_SPI == TRUE)

#if (defined HAL_IRGEN) && (HAL_IRGEN == TRUE)
  if ( HAL_IRGEN == TRUE && HAL_DMA_CHECK_IRQ( HAL_IRGEN_DMA_CH ) )
  {
    HAL_DMA_CLEAR_IRQ( HAL_IRGEN_DMA_CH );
    HalIrGenDmaIsr();
  }
#endif // (defined HAL_IRGEN) && (HAL_IRGEN == TRUE)
}

DMA中断函数中调用了HalUARTIsrDMA()函数,这个函数式在DMA用于UART时才有用。具体UART怎么用DMA操作在另外篇章会讲。将DMA中断标志清零之后HAL_DMA_CHECK_IRQ宏检查DMA数据是否传输完成。HAL_DMA_CH_TX为串口使用DMA的通道号为4,传输完成之后就会调用HalUARTIsrDMA()函数。

其实DMA的操作比较简单,主要是其配置的过程,即怎样配置才合理。

      下面看一下DMA在FLASH控制器中是如何配置的。打开hal_flash.c文件定位到函数HalUARTIsrDMA

void HalFlashWrite(uint16 addr, uint8 *buf, uint16 cnt)
{
  halDMADesc_t *ch = HAL_NV_DMA_GET_DESC();

  HAL_DMA_SET_SOURCE(ch, buf);
  HAL_DMA_SET_DEST(ch, &FWDATA);
  HAL_DMA_SET_VLEN(ch, HAL_DMA_VLEN_USE_LEN);
  HAL_DMA_SET_LEN(ch, (cnt * HAL_FLASH_WORD_SIZE));
  HAL_DMA_SET_WORD_SIZE(ch, HAL_DMA_WORDSIZE_BYTE);
  HAL_DMA_SET_TRIG_MODE(ch, HAL_DMA_TMODE_SINGLE);
  HAL_DMA_SET_TRIG_SRC(ch, HAL_DMA_TRIG_FLASH);
  HAL_DMA_SET_SRC_INC(ch, HAL_DMA_SRCINC_1);
  HAL_DMA_SET_DST_INC(ch, HAL_DMA_DSTINC_0);
  // The DMA is to be polled and shall not issue an IRQ upon completion.
  HAL_DMA_SET_IRQ(ch, HAL_DMA_IRQMASK_DISABLE);
  HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS);
  HAL_DMA_SET_PRIORITY(ch, HAL_DMA_PRI_HIGH);
  HAL_DMA_CLEAR_IRQ(HAL_NV_DMA_CH);
  HAL_DMA_ARM_CH(HAL_NV_DMA_CH);

  FADDRL = (uint8)addr;
  FADDRH = (uint8)(addr >> 8);
  HalFlashWriteTrigger();
}

  这个函数的作用是将buf中的cnt个字节的数据写到FLASH中,在每次写之前都要配置DMA(因为采用的是DMA传输)

首先获取了通道0配置描述符的地址,即将dmaCh0的地址复制给ch,然后设置源地址为buf的地址,目的地址为FWDATA,这个就是FLASH写数据的寄存器。再设置DMA传送长度,因为HAL_DMA_VLEN_USE_LEN为0,根据前面配置项的设置表格可知,其采用len为其传送长度。

HAL_DMA_SET_LEN(ch, (cnt * HAL_FLASH_WORD_SIZE));HAL_FLASH_WORD_SIZE为4,因为FLASH每次写入一个字节时都要在20us内写四次,故配置传送长度为cnt*4,且必须为4的整数倍,否则最后一个字节不会被写入到FLASH中。

HAL_DMA_SET_WORD_SIZE(ch, HAL_DMA_WORDSIZE_BYTE);配置传送每个字节位长度,如果是0则为8为,如果是1则为16位,这里是8位。

HAL_DMA_SET_TRIG_MODE(ch, HAL_DMA_TMODE_SINGLE);传送模式采用单一模式,即每当触发时,发生一个DMA传送,DMA等待下一个触发,完成指定传送长度之后,传送结束,通知CPU,解除DMA通道的工作状态。

HAL_DMA_SET_TRIG_SRC(ch, HAL_DMA_TRIG_FLASH);DMA触发事件为FLASH写数据完成时。

HAL_DMA_SET_SRC_INC(ch, HAL_DMA_SRCINC_1);源地址递增模式,这里是采用每次1个字节的递增方式。

HAL_DMA_SET_DST_INC(ch, HAL_DMA_DSTINC_0);目的地址递增模式,这里将目的地址固定了。因为每次写数据到FLASH中都是写在FWDATA这个寄存器中。

HAL_DMA_SET_IRQ(ch, HAL_DMA_IRQMASK_DISABLE);禁用DMA通道0中断。

HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS);采用字节的8位作用传送长度。如果为1则是字节的7位作为传送长度。
HAL_DMA_SET_PRIORITY(ch, HAL_DMA_PRI_HIGH);设置DMA通道0的优先级,这里设置高级,即比CPU优先。
HAL_DMA_CLEAR_IRQ(HAL_NV_DMA_CH);  然后将DMA中断标志位清零。

HAL_DMA_ARM_CH(HAL_NV_DMA_CH);使DMA通道0进入工作状态。

最后将要写入FLASH中的地址复制给   FADDRH: FADDRL,调用HalFlashWriteTrigger();

我们看下这个函数做了哪些事情
{
  MEMCTR |= 0x08;       // Start the Memory Arbiter running CODE from RAM.
  FCTL |= 0x02;         // Trigger the DMA writes.
  while (FCTL & 0x80);  // Wait until writing is done.
  MEMCTR &= ~0x08;      // Stop the Memory Arbiter.
}

这个结合datasheet中FLASH写数据的过程以及cc2530的存储原理来看。这个要用一章内容来描述cc2530的存储系统。

      z-stack中所用的DMA就是FLASH写、串口发送以及RADIO,RADIO的内容过于复杂,要完全弄懂要花不少时间。

清楚了DMA的原理之后,我们在写程序中对DMA用起来就会游刃有余,而不至于出现很多问题!

 

 

 

 

 

 

 

你可能感兴趣的:(z-stack)