2015年11月13日 星期五

ZigBee TI CC2530 程式筆記hal_board_cfg.h

hal_board_cfg.h 主要設定CC2530與ZDK開發版之間的關係,包含了pin腳位的對應、clock、PA等。如果是自家的開發版,則也是需要從這邊修改。Sample code主要是針對ZDK的。



程式版本2.5.1.a,cc2530晶片規格

/* ------------------------------------------------------------------------------------------------
 *                                          Clock Speed
 * ------------------------------------------------------------------------------------------------
 */

#define HAL_CPU_CLOCK_MHZ     32

/* This flag should be defined if the SoC uses the 32MHz crystal
 * as the main clock source (instead of DCO).
 */
#define HAL_CLOCK_CRYSTAL

/* 32 kHz clock source select in CLKCONCMD */
#if !defined (OSC32K_CRYSTAL_INSTALLED) || (defined (OSC32K_CRYSTAL_INSTALLED) && (OSC32K_CRYSTAL_INSTALLED == TRUE))
  #define OSC_32KHZ  0x00 /* external 32 KHz xosc */
#else
  #define OSC_32KHZ  0x80 /* internal 32 KHz rcosc */
#endif

#define HAL_CLOCK_STABLE()    st( while (CLKCONSTA != (CLKCONCMD_32MHZ | OSC_32KHZ)); )


/* ------------------------------------------------------------------------------------------------
 *                                       LED Configuration
 * ------------------------------------------------------------------------------------------------
 */

#if defined (HAL_BOARD_CC2530EB_REV17) && !defined (HAL_PA_LNA) && !defined (HAL_PA_LNA_CC2590)
  #define HAL_NUM_LEDS            3
#elif defined (HAL_BOARD_CC2530EB_REV13) || defined (HAL_PA_LNA) || defined (HAL_PA_LNA_CC2590)
  #define HAL_NUM_LEDS            1
#else
  #error Unknown Board Indentifier
#endif

預設好像是Run HAL_BOARD_CC2530EB_REV17的樣子,這是TI開發版的版本號,基本上是使用cc2530ZDK,cc2530ZDK才有下面多定義的LED燈。

#define HAL_LED_BLINK_DELAY()   st( { volatile uint32 i; for (i=0; i<0x5800; i++) { }; } )

/* 1 - Green */
#define LED1_BV           BV(0)
#define LED1_SBIT         P1_0
#define LED1_DDR          P1DIR
#define LED1_POLARITY     ACTIVE_HIGH

#if defined (HAL_BOARD_CC2530EB_REV17)
  /* 2 - Red */
  #define LED2_BV           BV(1)
  #define LED2_SBIT         P1_1
  #define LED2_DDR          P1DIR
  #define LED2_POLARITY     ACTIVE_HIGH

  /* 3 - Yellow */
  #define LED3_BV           BV(4)
  #define LED3_SBIT         P1_4
  #define LED3_DDR          P1DIR
  #define LED3_POLARITY     ACTIVE_HIGH
#endif

P1_0表示的是 P1.0的pin腳,P1_1表示的是P1.1,以此類推。
/* ------------------------------------------------------------------------------------------------
 *                                    Push Button Configuration
 * ------------------------------------------------------------------------------------------------
 */

#define ACTIVE_LOW        !
#define ACTIVE_HIGH       !!    /* double negation forces result to be '1' */

/* S1 */
//#define PUSH1_BV          BV(1)
//#define PUSH1_SBIT        P0_1
#define PUSH1_BV          BV(7)
#define PUSH1_SBIT        P0_7

同樣的P0_1表示的是 P0.1的pin腳,P0_7表示的是P0.7。ps:註解掉0.1,是想把他換成0.7。

#if defined (HAL_BOARD_CC2530EB_REV17)
  #define PUSH1_POLARITY    ACTIVE_HIGH
#elif defined (HAL_BOARD_CC2530EB_REV13)
  #define PUSH1_POLARITY    ACTIVE_LOW
#else
  #error Unknown Board Indentifier
#endif

/* Joystick Center Press */
/*#define PUSH2_BV          BV(0)
#define PUSH2_SBIT        P2_0
#define PUSH2_POLARITY    ACTIVE_HIGH*/

#define PUSH2_BV          BV(6)
#define PUSH2_SBIT        P0_6
#define PUSH2_POLARITY    ACTIVE_HIGH

ps:註解掉2.0,是想把他換成0.6。
/* ------------------------------------------------------------------------------------------------
 *                         OSAL NV implemented by internal flash pages.
 * ------------------------------------------------------------------------------------------------
 */
// Flash is partitioned into 8 banks of 32 KB or 16 pages.
#define HAL_FLASH_PAGE_PER_BANK    16
// Flash is constructed of 128 pages of 2 KB.
#define HAL_FLASH_PAGE_SIZE        2048
#define HAL_FLASH_WORD_SIZE        4

// CODE banks get mapped into the XDATA range 8000-FFFF.
#define HAL_FLASH_PAGE_MAP         0x8000

// The last 16 bytes of the last available page are reserved for flash lock bits.
// NV page definitions must coincide with segment declaration in project *.xcl file.
#if defined NON_BANKED
#define HAL_FLASH_LOCK_BITS        16
#define HAL_NV_PAGE_END            30
#define HAL_NV_PAGE_CNT            2
#else
#define HAL_FLASH_LOCK_BITS        16
#define HAL_NV_PAGE_END            126
#define HAL_NV_PAGE_CNT            6
#endif

// Re-defining Z_EXTADDR_LEN here so as not to include a Z-Stack .h file.
#define HAL_FLASH_IEEE_SIZE        8
#define HAL_FLASH_IEEE_PAGE       (HAL_NV_PAGE_END+1)
#define HAL_FLASH_IEEE_OSET       (HAL_FLASH_PAGE_SIZE - HAL_FLASH_LOCK_BITS - HAL_FLASH_IEEE_SIZE)
#define HAL_INFOP_IEEE_OSET        0xC

#define HAL_FLASH_DEV_PRIVATE_KEY_OSET     0x7D2
#define HAL_FLASH_CA_PUBLIC_KEY_OSET       0x7BC
#define HAL_FLASH_IMPLICIT_CERT_OSET       0x78C

#define HAL_NV_PAGE_BEG           (HAL_NV_PAGE_END-HAL_NV_PAGE_CNT+1)

// Used by DMA macros to shift 1 to create a mask for DMA registers.
#define HAL_NV_DMA_CH              0
#define HAL_DMA_CH_RX              3
#define HAL_DMA_CH_TX              4

#define HAL_NV_DMA_GET_DESC()      HAL_DMA_GET_DESC0()
#define HAL_NV_DMA_SET_ADDR(a)     HAL_DMA_SET_ADDR_DESC0((a))

待續...

2015年11月3日 星期二

ZigBee TI CC2530 程式筆記-UART 程式流程 hal_uart.c hal_uart.h _hal_uart_dma.c

1.HalUARTRead和HalUARTWrite程式碼寫在hal_uart.c內,程式如下:

---------------------------------------程式分隔--------------------------------------------------
uint16 HalUARTRead(uint8 port, uint8 *buf, uint16 len)
{
  (void)port;
  (void)buf;
  (void)len;

#if (HAL_UART_DMA == 1)
  if (port == HAL_UART_PORT_0)  return HalUARTReadDMA(buf, len);
#endif
#if (HAL_UART_DMA == 2)
  if (port == HAL_UART_PORT_1)  return HalUARTReadDMA(buf, len);
#endif
#if (HAL_UART_ISR == 1)
  if (port == HAL_UART_PORT_0)  return HalUARTReadISR(buf, len);
#endif
#if (HAL_UART_ISR == 2)
  if (port == HAL_UART_PORT_1)  return HalUARTReadISR(buf, len);
#endif

#if HAL_UART_USB
  return HalUARTRx(buf, len);
#else
  return 0;
#endif
}
---------------------------------------程式分隔--------------------------------------------------
uint16 HalUARTWrite(uint8 port, uint8 *buf, uint16 len)
{
  (void)port;
  (void)buf;
  (void)len;

#if (HAL_UART_DMA == 1)
  if (port == HAL_UART_PORT_0)  return HalUARTWriteDMA(buf, len);
#endif
#if (HAL_UART_DMA == 2)
  if (port == HAL_UART_PORT_1)  return HalUARTWriteDMA(buf, len);
#endif
#if (HAL_UART_ISR == 1)
  if (port == HAL_UART_PORT_0)  return HalUARTWriteISR(buf, len);
#endif
#if (HAL_UART_ISR == 2)
  if (port == HAL_UART_PORT_1)  return HalUARTWriteISR(buf, len);
#endif

#if HAL_UART_USB
  HalUARTTx(buf, len);
  return len;
#else
  return 0;
#endif
}
port基本上使用的是HAL_UART_PORT_0,所以會進入到HalUARTReadDMA和HalUARTWriteDMA。

---------------------------------------我是分隔線--------------------------------------------------

2.HalUARTReadDMA和HalUARTWriteDMA程式碼寫在_hal_uart_dma.c內,程式如下:

---------------------------------------程式分隔--------------------------------------------------

static uint16 HalUARTWriteDMA(uint8 *buf, uint16 len)
{
  uint16 cnt;
  halIntState_t his;
  uint8 txSel;
  txIdx_t txIdx;

  // Enforce all or none.
  if ((len + dmaCfg.txIdx[dmaCfg.txSel]) > HAL_UART_DMA_TX_MAX)
  {
    return 0;
  }
//注意一下HAL_UART_DMA_TX_MAX,預設為256,所以一次資料量最大不能超過256,否則沒辦法給UART資料。

  HAL_ENTER_CRITICAL_SECTION(his);
  txSel = dmaCfg.txSel;
  txIdx = dmaCfg.txIdx[txSel];
  HAL_EXIT_CRITICAL_SECTION(his);

  for (cnt = 0; cnt < len; cnt++)
  {
    dmaCfg.txBuf[txSel][txIdx++] = buf[cnt];
  }
//將要寫入的資料存近dmaCfg.txBuf,這是TI寫好的,別亂改。

  HAL_ENTER_CRITICAL_SECTION(his);
  if (txSel != dmaCfg.txSel)
  {
    HAL_EXIT_CRITICAL_SECTION(his);
    txSel = dmaCfg.txSel;
    txIdx = dmaCfg.txIdx[txSel];

    for (cnt = 0; cnt < len; cnt++)
    {
      dmaCfg.txBuf[txSel][txIdx++] = buf[cnt];
    }
    HAL_ENTER_CRITICAL_SECTION(his);
  }

  dmaCfg.txIdx[txSel] = txIdx;

  if (dmaCfg.txIdx[(txSel ^ 1)] == 0)
  {
    // TX DMA is expected to be fired
    dmaCfg.txDMAPending = TRUE;//TRUE表示有資料要送入UART,才會驅動到控制UART的程式碼。
  }
  HAL_EXIT_CRITICAL_SECTION(his);

  dmaCfg.txDMAPending = TRUE;
  這行是我自己加入的,因為會進入到HalUARTWriteDMA表示一定有資料,何必像上面一樣再次判斷。
 return cnt;
}

---------------------------------------程式分隔--------------------------------------------------

static uint16 HalUARTReadDMA(uint8 *buf, uint16 len)
{
  uint16 cnt;

  for (cnt = 0; cnt < len; cnt++)
  {
    if (!HAL_UART_DMA_NEW_RX_BYTE(dmaCfg.rxHead))
    {
      break;
    }
    *buf++ = HAL_UART_DMA_GET_RX_BYTE(dmaCfg.rxHead);
    HAL_UART_DMA_CLR_RX_BYTE(dmaCfg.rxHead);
//#if HAL_UART_DMA_RX_MAX == 256
//    (dmaCfg.rxHead)++;
//#else
    if (++(dmaCfg.rxHead) >= HAL_UART_DMA_RX_MAX)
    {
      dmaCfg.rxHead = 0;
    }
//#endif
  }
  PxOUT &= ~HAL_UART_Px_RTS;  // Re-enable the flow on any read.
//基本上就是將讀到的資料,寫入到你指定的buf。
//黃色的註解,是我個人會修改成使用
//if (++(dmaCfg.rxHead) >= HAL_UART_DMA_RX_MAX)
//    {
//      dmaCfg.rxHead = 0;
//    }這段進行計算,我個人認為讀的時候比較順。
  return cnt;
}

---------------------------------------我是分隔線--------------------------------------------------

3.HalUARTReadDMA和HalUARTWriteDMA並沒有寫入UART,只是寫入到TI指定的buf,實際上寫入的function是static void HalUARTPollDMA(void)。程式如下:

---------------------------------------程式分隔--------------------------------------------------

static void HalUARTPollDMA(void)
{
  uint16 cnt = 0;
  uint8 evt = 0;

  if (HAL_UART_DMA_NEW_RX_BYTE(dmaCfg.rxHead))
  {
    rxIdx_t tail = findTail();

    // If the DMA has transferred in more Rx bytes, reset the Rx idle timer.
    if (dmaCfg.rxTail != tail)
    {
      dmaCfg.rxTail = tail;

      // Re-sync the shadow on any 1st byte(s) received.
      if (dmaCfg.rxTick == 0)
      {
        dmaCfg.rxShdw = ST0;
      }
      dmaCfg.rxTick = HAL_UART_DMA_IDLE;
    }
    else if (dmaCfg.rxTick)
    {
      // Use the LSB of the sleep timer (ST0 must be read first anyway).
      uint8 decr = ST0 - dmaCfg.rxShdw;

      if (dmaCfg.rxTick > decr)
      {
        dmaCfg.rxTick -= decr;
        dmaCfg.rxShdw = ST0;
      }
      else
      {
        dmaCfg.rxTick = 0;
      }
    }
    cnt = HalUARTRxAvailDMA();
  }
  else
  {
    dmaCfg.rxTick = 0;
  }

  if ((cnt >= HAL_UART_DMA_FULL)||(cnt > 10))
  {
    evt = HAL_UART_RX_FULL;
  }
  else if (cnt >= HAL_UART_DMA_HIGH)
  {
    evt = HAL_UART_RX_ABOUT_FULL;
    PxOUT |= HAL_UART_Px_RTS;  // Disable Rx flow.
  }
  else if (cnt && !dmaCfg.rxTick)
  {
    evt = HAL_UART_RX_TIMEOUT;
  }
//這個if可以觀察到,UART事件處理是在這邊決定的。

/************************************************************/
/****************上面是Read,下面是Wirte。*******************/
/************************************************************/

  if (dmaCfg.txMT)
  {
    dmaCfg.txMT = FALSE;
    evt |= HAL_UART_TX_EMPTY;
  }

  if (dmaCfg.txShdwValid)
  {
    uint8 decr = ST0;
    decr -= dmaCfg.txShdw;
    if (decr > dmaCfg.txTick)
    {
      // No protection for txShdwValid is required
      // because while the shadow was valid, DMA ISR cannot be triggered
      // to cause concurrent access to this variable.
      dmaCfg.txShdwValid = FALSE;
    }
  }
 // dmaCfg.txShdwValid = FALSE;
  if (dmaCfg.txDMAPending && !dmaCfg.txShdwValid)
  {
    //以下就是寫入UART程式碼
    // UART TX DMA is expected to be fired and enough time has lapsed since last DMA ISR
    // to know that DBUF can be overwritten
    halDMADesc_t *ch = HAL_DMA_GET_DESC1234(HAL_DMA_CH_TX);
    halIntState_t intState;

    // Clear the DMA pending flag
    dmaCfg.txDMAPending = FALSE;
 
    HAL_DMA_SET_SOURCE(ch, dmaCfg.txBuf[dmaCfg.txSel]);
    HAL_DMA_SET_LEN(ch, dmaCfg.txIdx[dmaCfg.txSel]);
    dmaCfg.txSel ^= 1;
    HAL_ENTER_CRITICAL_SECTION(intState);
    HAL_DMA_ARM_CH(HAL_DMA_CH_TX);
    do
    {
      asm("NOP");
    } while (!HAL_DMA_CH_ARMED(HAL_DMA_CH_TX));
    HAL_DMA_CLEAR_IRQ(HAL_DMA_CH_TX);
    HAL_DMA_MAN_TRIGGER(HAL_DMA_CH_TX);
    HAL_EXIT_CRITICAL_SECTION(intState);
  }
  else
  {
    halIntState_t his;

    HAL_ENTER_CRITICAL_SECTION(his);
    if ((dmaCfg.txIdx[dmaCfg.txSel] != 0) && !HAL_DMA_CH_ARMED(HAL_DMA_CH_TX)
                                          && !HAL_DMA_CHECK_IRQ(HAL_DMA_CH_TX))
    {
      HAL_EXIT_CRITICAL_SECTION(his);
      HalUARTIsrDMA();
    }
    else
    {
      HAL_EXIT_CRITICAL_SECTION(his);
    }
  }

  if (evt && (dmaCfg.uartCB != NULL))
  {
    dmaCfg.uartCB(HAL_UART_DMA-1, evt);
  }
}

2015年7月30日 星期四

ZigBee TI CC2530 程式筆記-UART(RS-232) 使用方式 IAR

我主要開發的是SampleAPP,版本2.5.1a。

一.初始化(在SampleAPP.c內設定UART的相關參數)

#include "hal_uart.h" //include UART的function

在Init內增加UART的code。

void SampleApp_Init( uint8 task_id )
{
  halUARTCfg_t uartConfig;
  uartConfig.configured           = TRUE;
  uartConfig.baudRate             =   HAL_UART_BR_38400;  
  uartConfig.flowControl          = FALSE;
  uartConfig.flowControlThreshold = 64;
  uartConfig.rx.maxBufSize        = 128;
  uartConfig.tx.maxBufSize        = 128;
  uartConfig.idleTimeout          = 6;  
  uartConfig.intEnable            = TRUE;            
  uartConfig.callBackFunc         = SerialApp_CallBack;
  HalUARTOpen (HAL_UART_PORT_0, &uartConfig);
}

簡單介紹一下相關參數
1.uartConfig.baudRate 設定Data Rate

Data Rate的定義在hal_uart.h裡面,總共有5種可以選擇。

     #define HAL_UART_BR_9600   0x00
     #define HAL_UART_BR_19200  0x01
     #define HAL_UART_BR_38400  0x02
     #define HAL_UART_BR_57600  0x03
     #define HAL_UART_BR_115200 0x04

2.uartConfig.flowControl 流量控制,我是設定關。

3.uartConfig.rx.maxBufSize 和 uartConfig.tx.maxBufSize,就是當你收資料和送資料的時候,最大值是多少,小於等於可以送,不能超過。

4.uartConfig.callBackFunc 指定一個function給UART用,這很好用。請參考第二點。

5.HalUARTOpen 開啟Port
如果是使用TI CC2530 ZDK的開發版,都是用HAL_UART_PORT_0。
定義在hal_uart.h
/* Ports */
#define HAL_UART_PORT_0   0x00
#define HAL_UART_PORT_1   0x01
#define HAL_UART_PORT_MAX 0x02


---------------------------------------我是分隔線--------------------------------------------------
二.Call Back Function

當設備收到UART資料的時候,就會執行指定的function,做後續的處理。
SerialApp_CallBack是自定義的,也可以不用,看需求。

宣告
void SerialApp_CallBack( uint8 port, uint8 event );

程式內容
void SerialApp_CallBack( uint8 port, uint8 event ){

 if (event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT))
  {
     //UART收到資料後續處理
  }
}

注意並不是function寫上去,有資料進入UART,才會執行。
事實上它會一直執行,所以function內用 if 是判斷是否有資料進入UART,有就做後續的處理。

---------------------------------------我是分隔線--------------------------------------------------
三.Write and Read

UART收到資料後,就可以讀出資料。下面有一個簡單範例

首先新增兩個Array,一個用來收資料,另一個送資料。
其實也可以一個就好,但是分開比較清楚。
1.定義 Array
static uint8 SerialApp_TxBuf[128];
static uint8 SerialApp_RxBuf[128];
static uint8 SerialApp_RxLen;

2.Read
void SerialApp_CallBack( uint8 port, uint8 event ){

 if (event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT))
  {
     //UART收到資料後續處理
     SerialApp_RxLen=HalUARTRead(HAL_UART_PORT_0, SerialApp_RxBuf, 127);
    //讀UART資料,儲存到SerialApp_RxBuf,SerialApp_RxLen為實際資料長度。
    //設定127是因為array max 128的關係,UART傳送最好比128小。
  }
}

3.Write

SerialApp_TxBuf[0]=0;
SerialApp_TxBuf[1]=1;
SerialApp_TxBuf[2]=2;
SerialApp_TxBuf[3]=3;

HalUARTWrite(HAL_UART_PORT_0, SerialApp_TxBuf, 4);