Serial Boot Loader for CC2530 资料整理

1 参考网址

源码参考:
C:\Texas Instruments\Z-Stack 3.0.1\Projects\zstack\Utilities\BootLoad\CC2538_UART

http://processors.wiki.ti.com/index.php/CC253x_Serial_Boot_Loader
Serial Boot Loader for CC2530 Soc
https://wenku.baidu.com/view/dfd0e8112cc58bd63086bd8b.html

C:\Texas Instruments\ZStack-CC2530-2.3.1-1.4.0\Documents\CC2530\Serial Boot Loader for CC253x.pdf

2 CC2530使用串口下载(SBL)

https://www.bbsmax.com/A/B0zqloXdvL/

2、协议栈目录: C:\Texas Instruments\ZStack-CC2530-2.3.1-1.4.0\Documents\CC2530\Serial Boot Loader for CC253x.pdf

我也参考上面的文件做了自己的实验,具体如下:

第一步:首先安装好协议栈,进入C:\Texas Instruments\ZStack-CC2530-2.3.1-1.4.0\Projects\zstack\Utilities\BootLoad\CC2530,打开Boot.eww工程

不做任何修改,直接使用仿真机将程序烧录至板子里面(如果硬件有不一样,则自己参考自己的硬件相关设置的代码,其他完全不修改),

此时,SBL的bootloader程序被下载到FLASH的第一个bank的最开始的位置(0x0000-0x2000).

第二步:进入C:\Texas Instruments\ZStack-CC2530-2.3.1-1.4.0\Projects\zstack\Samples\GenericApp\CC2530DB,打开GenericApp.eww工程

调整工程的option里面的配置(具体可以参考上面提供的参考文档,非常详细),另外工程配置完毕后,编译。

打开生成的GenericApp.map文件,如下图所示,复制checksum到onboard.c里面的const CODE uint16 _crcShdw = 0x418f;,我这里是0x418f。

更改后重新编译,这时候生成的GeneralApp.bin才是我们使用串口工具来烧录所需的文件.

第三步:进入C:\Texas Instruments\ZStack-CC2530-2.3.1-1.4.0\Tools\SBL Tool,打开SBDemo.exe,点击下载,即可开始下载,

下载具体过程如下:

首先数据格式为:

| 数据头部SOF | 数据长度LEN | 命令1 | 命令2 | 数据 | 帧校验FCS|

例如:FE 01 4D 10 00 5C,和上面一一对应

在SBL的程序中有如下定义:

// Bootloader Serial Interface Subsystem
  #define SB_RPC_SYS_BOOT 0x4D

// Commands to Bootloader
  #define SB_WRITE_CMD 0x01
  #define SB_READ_CMD 0x02
  #define SB_ENABLE_CMD 0x03
  #define SB_HANDSHAKE_CMD 0x04

1、先发送一个

FE 01 4D 10 00 5C

命令2为10,对比上面的定义发现这个在SBL程序里面没有,所以我也不知道是干什么用的,可能是给我们用户自己使用,用于扩展功能的,暂时忽略。

2、在发送

FE 01 4D 04 02 4A

命令2为04,对比上面的定义,是握手命令,PC和板子建立下载链接,如果发一段时间的握手信号,都没有程序,PC会显示超时。

3、接着是发送

FE 42 4D 01 00 00 02 2A 62 02 21 D3 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 02 28 A3 FF FF FF FF FF 02 25 F9 FF FF FF FF FF FF FF FF FF FF E3

命令2为01,对比上面的定义,是写命令,命令的第二个字节代表数据长度是42转化为十进制是66,其中2个字节是写入地址(这里是00 00),另外64个字节就是写入的数据内容。这个命令会连续发送,直到整个程序都写入完毕。

4、程序写入完毕后,再发送,

FE 02 4D 02 00 00 4D

命令2为02,对比上面的定义,是读命令,命令的第二个字节是02,即数据长度为2位,表示需要从FLASH读数据的位置(这里是00 00),读出数据进行校验。

5、所有地址的数据校验完毕后会根据你在SBL烧录软件上是否勾选No Reset Vector Verification(8051 only),选择是否发送复位命令

FE 00 4D 03 4E,

命令2位03,串口启动使能命令,复位芯片。

6、复位芯片后,会进入等待状态(LED1和LED2交替闪烁),等待状态下,有3种方式进入应用程序

1、如果等待一段时间无操作(具体多长时间可以用过跳转SBL程序static uint8 sblWait(void)里面的uint32 dlyCnt = 0x260000;的大小来调整等待时间),则程序跳转到0x2000,开始运行我们的应用程序,

2、在等待状态下,我们也可以通过S1 和 S2按键来控制进入下载模式还是正常工作模式,S1如果是高电平,则进入下载模式; 如果S1为低电平,S2为高电平,则进入正常工作模式

3、在等待状态下,也可以通过串口发送0xF8进入下载模式,发送0x07(0xF8^0xFF)进入正常工作模式。

3 相关代码:

main
while(1){
sbUartPoll()
if (sbExec()){

	}

}

4 Serial 发送使用的是 环形队列 方法 一

定义

   #define SB_BUF_HEADER_SIZE          50 
   #define SB_RW_BUF_LEN               2048 
   #define SB_BUF_SIZE                 (SB_RW_BUF_LEN + SB_BUF_HEADER_SIZE)
   static uint8 txBuf[SB_BUF_SIZE];
   static uint32 txHead, txTail;

初始化 暂时没有

放入数据:

/*************************************************************************************************
 * @fn      sbUartWrite()
 *
 * @brief   Write a buffer to the UART.
 *
 * @param   pBuf - pointer to the buffer that will be written.
 *          len  - length of buffer to write.
 *
 * @return  length of the buffer that was sent.
 */
uint32 sbUartWrite(uint8 *pBuf, uint32 len)
{
  uint32 idx = txHead;
  uint32 cnt = txTail;

  if (cnt == idx)
  {
    cnt = SB_BUF_SIZE;
  }
  else if (cnt > idx)
  {
    cnt = SB_BUF_SIZE - cnt + idx;
  }
  else // (cnt < idx)
  {
    cnt = idx - cnt;
  }

  // Accept "all-or-none" on write request.
  if (cnt < len)
  {
    return 0;
  }

  idx = txTail;

  for (cnt = 0; cnt < len; cnt++)
  {
    txBuf[idx++] = pBuf[cnt];

    if (idx >= SB_BUF_SIZE)
    {
      // txBuf is a circular buffer. When reaching the end - go back to the start.
      idx = 0;
    }
  }

  txTail = idx;
  procTx();

  return len;  // Return the number of bytes actually put into the buffer.
}

取走数据

/*************************************************************************************************
 * @fn      procTx
 *
 * @brief   Process Tx bytes.
 *
 * @param   void
 *
 * @return  void
 */
static void procTx(void)
{
  while ((txHead != txTail) && (UARTCharPutNonBlocking(HAL_UART_PORT, txBuf[txHead])))
  {
    if (++txHead >= SB_BUF_SIZE)
    {
      txHead = 0;
    }
  }
}

5 串口 发送 环形队列 方法 2

typedef void (*halUARTCBack_t) (uint8 port, uint8 event);

typedef struct
{
  // The head or tail is updated by the Tx or Rx ISR respectively, when not polled.
  volatile uint16 bufferHead;
  volatile uint16 bufferTail;
  uint16 maxBufSize;
  uint8 *pBuffer;
} halUARTBufControl_t;

typedef struct
{
  bool                configured;
  uint8               baudRate;
  bool                flowControl;
  uint16              flowControlThreshold;
  uint8               idleTimeout;
  halUARTBufControl_t rx;
  halUARTBufControl_t tx;
  bool                intEnable;
  uint32              rxChRvdTime;
  halUARTCBack_t      callBackFunc;
}halUARTCfg_t;

static halUARTCfg_t uartRecord;   

初始化


      if (((uartRecord.rx.pBuffer = osal_mem_alloc(config->rx.maxBufSize)) == NULL) ||
          ((uartRecord.tx.pBuffer = osal_mem_alloc(config->tx.maxBufSize)) == NULL))
      {
        if (uartRecord.rx.pBuffer != NULL)
        {
          osal_mem_free(uartRecord.rx.pBuffer);
          uartRecord.rx.pBuffer = NULL;
        }
    
        return HAL_UART_MEM_FAIL;
      }
  uartRecord.configured = TRUE;
  uartRecord.baudRate = config->baudRate;
  uartRecord.flowControl = config->flowControl;
  uartRecord.flowControlThreshold = (config->flowControlThreshold > config->rx.maxBufSize) ? 0 :
                                     config->flowControlThreshold;
  uartRecord.idleTimeout = config->idleTimeout;
  uartRecord.rx.maxBufSize = config->rx.maxBufSize;
  uartRecord.tx.maxBufSize = config->tx.maxBufSize;
  uartRecord.intEnable = config->intEnable;
  uartRecord.callBackFunc = config->callBackFunc;

互斥:

typedef bool halIntState_t;
  halIntState_t intState;
  HAL_ENTER_CRITICAL_SECTION(intState);
  procRx();
  procTx();
  HAL_EXIT_CRITICAL_SECTION(intState);

  

定义

extern uint32 rtosCSDepth;
extern void vPortEnterCritical(void);
extern void vPortExitCritical(void);
#define HAL_ENTER_CRITICAL_SECTION(x)  \
  do { \
    (void) (x); \
    vPortEnterCritical(); \
    rtosCSDepth++; \
  } while (0)
#else /* TIMAC_ON_RTOS */
#define HAL_ENTER_CRITICAL_SECTION(x)  \
  do { (x) = !IntMasterDisable(); } while (0)
#endif /* TIMAC_ON_RTOS */

#ifdef TIMAC_ON_RTOS
#define HAL_EXIT_CRITICAL_SECTION(x)   \
  do { \
    (void) (x); \
    rtosCSDepth--; \
    vPortExitCritical(); \
 } while (0)
#else /* TIMAC_ON_RTOS */
#define HAL_EXIT_CRITICAL_SECTION(x) \
  do { if (x) { (void) IntMasterEnable(); } } while (0)
#endif /* TIMAC_ON_RTOS */

6 串口协议栈 接收

首先数据格式为:

| 数据头部SOF | 数据长度LEN | 命令1 | 命令2 | 数据 | 帧校验FCS|

例如:FE 01 4D 10 00 5C,和上面一一对应
// Serial RX States
#define SB_SOF_STATE                0
#define SB_LEN_STATE                1
#define SB_FRAME_ID_STATE     2
#define SB_CMD_STATE               3
#define SB_DATA_STATE              4
#define SB_FCS_STATE                5
#define SB_LEN1_STATE              6
#define SB_LEN2_STATE              7
#define SB_LEN3_STATE              8
#define SB_LEN4_STATE              9
#define SB_SOF                      0xFE
// Bootloader Serial Interface Subsystem
#define SB_RPC_SYS_BOOT             0x4D

// Commands to Bootloader
#define SB_WRITE_CMD                0x01
#define SB_READ_CMD                 0x02
#define SB_ENABLE_CMD               0x03
#define SB_HANDSHAKE_CMD            0x04

// Status codes
#define SB_SUCCESS                  0
#define SB_FAILURE                  1
#define SB_INVALID_FCS              2
#define SB_INVALID_FILE             3
#define SB_FILESYSTEM_ERROR         4
#define SB_ALREADY_STARTED          5
#define SB_NO_RESPOSNE              6
#define SB_VALIDATE_FAILED          7
#define SB_CANCELED                 8




状态机解析:

/**************************************************************************************************
 * @fn          sbExec
 *
 * @brief       Boot Loader main executive processing.
 *
 * input parameters
 *
 * None.
 *
 * output parameters
 *
 * None.
 *
 * @return      TRUE if sbCmnd() returns TRUE, indicating that an SB_ENABLE_CMD succeeded;
 *              FALSE otherwise.
 */
bool sbExec(void)
{
  // Parse the incoming packet抯 header, and verify its FCS. Valid packets are transferred to 
  // sbCmnd() for further processing. For detailed packet format, please see the document "Serial 
  // Boot Loader for CC2538".
  static uint8 sbFrameId, sbCmd, sbFcs, sbSte = SB_SOF_STATE;
  static uint32 sbLen;
  static uint32 sbIdx;
  uint8 ch, rtrn = FALSE;
  
  while (SB_RX(&ch))
  {
    switch (sbSte)
    {
    case SB_SOF_STATE:
      if (SB_SOF == ch)
      {
        sbSte = SB_LEN_STATE;
        sbIdx = 0;
      }
      break;
      
    case SB_LEN_STATE: // this field is kept for backward compatibility of the protocol. 
                       // In case the length is larger than 254, this fields is set to 0xFF, 
		       // and there is an additional 32bit length, located in another location 
		       // in this header.
      sbLen = ch;
      sbFcs = 0;
      sbSte = SB_FRAME_ID_STATE;
      break;
      
    case SB_FRAME_ID_STATE:
      sbFrameId = ch;
      sbSte = SB_CMD_STATE;
      break;
      
    case SB_CMD_STATE:
      sbCmd = ch;
      
      switch (sbLen)
      {
      case 0:
        sbSte = SB_FCS_STATE;
        break;
      case 0xFF:
        sbSte = SB_LEN1_STATE;
        break;
      default:
        sbSte = SB_DATA_STATE;
        break;
      }
      break;
      
    case SB_LEN1_STATE:
      sbLen = ch;
      sbSte = SB_LEN2_STATE;
      break;
      
    case SB_LEN2_STATE:
      sbLen += ch << 8;
      sbSte = SB_LEN3_STATE;
      break;
      
    case SB_LEN3_STATE:
      sbLen += ch << 16;
      sbSte = SB_LEN4_STATE;
      break;
      
    case SB_LEN4_STATE:
      sbLen += ch << 24;
      sbSte = (sbLen) ? SB_DATA_STATE : SB_FCS_STATE;
      break;
      
    case SB_DATA_STATE:
      if (sbIdx >= sizeof(_sbBuf))
      {
        sbSte = SB_SOF_STATE; //discard this packet. the payload is too long.
      }
      else
      {
        sbBuf[sbIdx++] = ch;
        
        if (sbIdx == sbLen)
        {
          sbSte = SB_FCS_STATE;
        }
      }
      break;
      
    case SB_FCS_STATE:
      if ((sbFcs == ch) && (sbFrameId == SB_RPC_SYS_BOOT))
      {
        rtrn = sbCmnd(sbCmd, sbLen);
      }
      
      sbSte = SB_SOF_STATE;
      break;
      
    default:
      break;
    }
    sbFcs ^= ch;
  }
  
  return rtrn;
}

6 实现方式

没有用到中断
串口发送是 轮询方式
串口接收 也是轮询方式

看看 用中断的方式 应该怎么实现 。

(稍后补充)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值