1. 程式人生 > >CC2530學習筆記2——Flash讀寫程式設計

CC2530學習筆記2——Flash讀寫程式設計

在做CC2530 Flash讀寫時,對該晶片的Flash儲存器典型操作進行了一些學習,總結在這裡。理解不當甚至錯誤之處,請高手賜教,謝謝。
CC2530的Flash按頁組織,每頁2048位元組。與其它Flash儲存器一樣,被擦除為1,被寫為 0。另外,在後面與邏輯空間對映時,還有個BANK的概念,是32KB。
對Flash儲存器有3種操作,簡介如下:
    擦除:最小單元是頁,即2048位元組;
    寫入:最小單元是32位字,即4位元組,這也是後面的程式碼中寫入地址必須為4的倍數的原因;
    讀取:資料手冊中說,當被CPU訪問讀取程式碼或資料時,是位元組可定址。當被Flash控制器訪問時,是字可定址,其中一個字由32位組成。我們要做的從Flash讀取資料是前一種情況,可位元組定址,也就是可以逐位元組讀取。後一種情況我還沒用過。(⊙o⊙)…
三種操作的程式碼如下。
一、    擦除
擦除沒啥好說的,這是寫入之前必須進行的操作。在資料手冊中描述的比較詳細,也有程式碼,直接用即可。

/**************************************************************************************************
 * @fn          FLASH_PageErase
 *
 * @brief       This function erases flash page of the 'pageNum'th page.
 *
 * input parameters
 *
 * @param       pageNum - Valid flash page num.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
void FLASH_PageErase(uint8_t pageNum)
{
 
  EA=0;
  while(FCTL & 0x80);//等待快閃記憶體控制器整備好
  FADDRH = pageNum << 1;//選擇擦除頁面的序號,共128頁
  FCTL |= 0x01;//啟動頁面擦除
  while(FCTL & 0x80);//等待擦除完成
  EA=1;
}

二、    寫入
資料手冊對寫入描述的也比較詳細。其中有一點比較重要,手冊中說Flash寫操作正在進行的時候,CPU不能訪問快閃記憶體,即讀取程式程式碼。也就是說如果直接寫Flash,程式就沒法執行了。所以,提供了兩種寫入方式:使用DMA傳輸(首選方法),或者使用CPU執行來自SRAM的程式碼。我選擇推薦的DMA方法。所以,在真正寫入之前要初始化DMA控制器,詳見程式碼。

/**************************************************************************************************
 * @fn          FLASH_Write
 *
 * @brief       This function writes 'num_bytes' bytes to the uint16_ternal flash.
 *
 * input parameters
 *
 * @param       addr - Valid flash write address: actual addr / 4 and quad-aligned.
 * @param       data - Valid buffer space at least as big as 'cnt' X 4.
 * @param       num_bytes - Number of bytes to write, MUST be Divisible by 4.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
void FLASH_Write(uint16_t addr,uint8_t *data, uint16_t num_bytes)
{
  //配置DMA通道每次傳送一個位元組
  DMADesc_t dmaConfig0;//定義DMA通道
  dmaConfig0.SRCADDRH  = ((uint16_t)data >> 8) & 0x00FF; //XData - To Be Written to Flash - Gets Incremented;儲存data的高8位;
  dmaConfig0.SRCADDRL  = (uint16_t)data & 0x00FF;//儲存data地址的低8位; 
  dmaConfig0.DESTADDRH = (((uint16_t)&FWDATA) >> 8) & 0x00FF; //Flash Controller Data Address - Flash Controller Writes Data//儲存寫暫存器的地址的高8位
  dmaConfig0.DESTADDRL = ((uint16_t)&FWDATA) & 0x00FF;//儲存寫暫存器的低8位;
  dmaConfig0.VLEN      = 0; //Variable num_bytes Transfer - 0=Fixed LEN Transfer//採用LEN作為傳送長度
  dmaConfig0.LENH      = (num_bytes>>8) & 0x00FF; //Number of WORDSZIE in Transfer - Must be Divisible by 4 - NET_ADDR_SIZE=4//儲存傳送長度高5位
  dmaConfig0.LENL      = num_bytes & 0x00FF;//儲存長度低8位;
  dmaConfig0.WORDSIZE  = 0; //Size of Each Transfer - 0=8 Bit;每個DMA傳送採用8位傳送
  dmaConfig0.TMODE     = 0; //Transfer Mode - 1=Block, 0=Single,傳送模式為單一模式
  dmaConfig0.TRIG      = 18; //DMA Trigger - 0=Manual Via DMAREQ, 18=Flash;flash觸發
  dmaConfig0.SRCINC    = 1; //Source Address Increment - 1=1 Byte//源地址增量模式為1位元組/字
  dmaConfig0.DESTINC   = 0; //Destination Address Increment - 0=0 Bytes (Always Write to FWDATA, No Need to Increment)目標地址增量模式0位元組/字
  dmaConfig0.IRQMASK   = 0; //uint16_terrupt Mask - 0=Disable uint16_terrupts//禁止通道完成中斷產生
  dmaConfig0.M8        = 0; //8th Bit Mode - 0=Use All 8 Bits使用全部8位作為傳送長度
  dmaConfig0.PRIORITY  = 2; //Priority - 10(2)=High Priority優先順序為DMA優先
  //DMA模式寫
  while (FCTL & 0x80); //Wait Until DMA Controller is Available - Busy Bit 7//等待寫或擦除狀態被啟用
  /********* 儲存寫入flash頁地址 ***********************************************/
  FADDRH =(addr >> 10) & 0x00FF; // page size: 2048; select the flash page via FADDRH[7:1] bits//由於寫入flash時是字(4位元組)定址的,所以儲存高位需要右移2位;
  FADDRL =(addr >> 2) & 0x00FF;  //4位元組定址,儲存要寫入flash地址的低位元組需要右移2位
  
  //通道0配置地址
  DMA0CFGH = (((uint16_t)&dmaConfig0) >> 8) & 0x00FF; //Pass DmaConfig0
  DMA0CFGL = ((uint16_t)&dmaConfig0) & 0x00FF;
   
  DMAARM |= 0x01; //Arm the DMA Channel//通道0進去工作狀態
  FCTL |= 0x02; //Start Write
    //while (!(DMAIRQ & 0x01)); //Wait Until Write Complete
    //DMAIRQ &= 0xFE; //Clear Any DMA IRQ on Channel 0 - Bit 0
  while (FCTL & (0x80)); //Wait Until Flash Controller is Not Busy - Busy Bit 7//等待或者擦除狀態啟用
  
  return;
}


三、    讀取
這個比較關鍵,資料手冊裡寫的不是很詳細,我在這裡也費了些功夫。需要結合第2章和第6章一起理解。
首先,在CC2530中有CODE、DATA、XDATA、SFR等邏輯儲存空間,參考這個部落格,不同儲存空間的特性總結如下:
1.    CODE :程式儲存器, 用處存放程式程式碼和一些常量
有16根地址匯流排,所以CODE的定址範圍是 0000H~FFFFH 共64KB
2.  DATA    資料儲存器,用於存放程式執行過程中的資料
           有8根地址匯流排,所以DATA的定址空間為 00H~FFH 共256 byte.低128位可以直接定址,高128位只能間接定址。
3.  XDATA  外部資料儲存器(只能間接定址,訪問速度比較慢) DMA是在XDATA上定址的,這一點很重要
           有16根地址匯流排,所以 XDATA 的定址空間為 0000H ~ FFFFH 共64K
      4.  SFR  特殊功能暫存器  就是那些T1CTL, EA, P0 等配置暫存器儲存的地方 共128K。因為CC2530的配置暫存器比較多,所以一些多餘的暫存器就放到了XREG 裡面。XREG的大小為1K XREG的訪問速度比 SFR慢。

這些儲存空間實際是儲存在3個物理儲存器(Flash程式儲存器、SRAM和儲存對映儲存器)中的。這裡很明顯有個問題,CODE定址範圍只有64KB,但CC2530的Flash最大卻有256KB。那怎麼保證Flash的空間都能被定址呢?
所以,晶片搞了個對映機制,CODE空間的對映如下:
 
也就是說,CODE空間中前32KB是固定的,而後32KB是可設定的(從Flash的BANK0~BANK7),具體配置為哪個Flash,可通過暫存器FMAP設定。
類似地,XDATA的儲存空間如下:

 
由於XDATA是可以讀/寫的資料儲存空間,而它的XBANK區域(0x8000~0xFFFF範圍)可由暫存器MEMCTR設定為Flash的任一BANK。因此,可以通過設定MEMCTR選擇需要讀取的BANK,然後從XDATA資料儲存器中讀取出來。讀取部分程式碼如下。若想檢視讀取時儲存器中的值,可以在MEMCTR = old_map;語句處打斷點,按照XDATA的儲存空間對映,計算得出儲存器在XDATA空間中的實際地址(即offset的值),檢視即可。

/**************************************************************************************************
 * @fn          FLASH_Read
 *
 * @brief       This function reads 'num_bytes' bytes from flash address addr to array data.
 *
 * input parameters
 *
 * @param       addr - Valid flash read address: .
 * @param       data - Valid buffer space to store the data.
 * @param       num_bytes - Number of bytes to read.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
void FLASH_Read(uint16_t addr,uint8_t* data,uint8_t num_bytes)
{
  uint8_t old_map;
  uint8_t bank_num;
  uint16_t offset;
  uint8_t old_ea;
  
  old_ea = EA;
  EA = 0; // close global interrupt
  
  bank_num = (addr>>FLASH_BANK_SHIFT)&FLASH_BANK_MAP_MASK;
  offset = (addr & (FLASH_BANK_SIZE-1))+FLASH_BASE_ADDR;
  
  old_map = MEMCTR;
  MEMCTR = (old_map&~0X7)|bank_num; //map the correct flash bank to XBANK
  
  memcpy(data,(uint8_t*)offset,num_bytes);
  
  MEMCTR = old_map;
  EA = old_ea;
  return;
}

 完整的IAR工程可以在如下連結下載:https://download.csdn.net/download/hnxyxiaomeng/10643545