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用起來就會遊刃有餘,而不至於出現很多問題!

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章