Skip to content
Serge Vakulenko edited this page Mar 8, 2015 · 2 revisions

Description of UHB protocol

Sources of mikroE USB HID Bootloader protocol implementation. See file Examples/Other/USB HID Bootloader/Driver/UHB_Driver.c in the microC Pro package.

/******************************************************************************
 *                                                                            *
 *  Unit:         UHB_Driver.c                                                *
 *                                                                            *
 *  Copyright:    (c) Mikroelektronika, 2011.                                 *
 *                                                                            *
 *  Description:  mikroE USB HID Bootloader (UHB) protocol implementation.    *
 *                                                                            *
 *  Requirements: - Minor modifications can make this code can work with any  *
 *                  MCU which has internal USB module and Flash self write    *
 *                  capabilities.                                             *
 *                - Bootloader code does not implement USB and Flash handling *
 *                  code (drivers). They are properties of mikroC compiler.   *
 *                - MikroE 'USB HID Bootloader Tool' PC application.          *
 *                                                                            *
 ****************************       CHANGE LOG       **************************
 * Version | ACTION                                           |  DATE  | SIG  *
 * --------|--------------------------------------------------|--------|----- *
 *         |                                                  |        |      *
 *    0.01 | - Initial release                                | 030511 |  ST  *
 *         |                                                  |        |      *
 ******************************************************************************/

/* Protocol Description.

  UHB protocol is a typical master-slave communication protocol, where
  master (PC) sends commands and slave (bootloader equiped device) executes
  them and aknowledges execution.

  * Command format.

    <STX[0]><CMD_CODE[0]><ADDRESS[0..3]><COUNT[0..1]> <DATA[0..COUNT-1]>
    |-- 1 --|---- 1 -----|------ 4 -----|----- 2 ----|------ COUNT -----|

    STX      - Command start delimiter (for future upgrades).
               Length: 1 byte. Mandatory.
    CMD_CODE - Command index (TCmd).
               Length: 1 byte. Mandatory.
    ADDRESS  - Address field. Flash start address for
               CMD_CODE command operation.
               Length: 4 bytes. Optional (command specific).
    COUNT    - Count field. Amount of data/blocks for
               CMD_CODE command operation.
               Length: 2 bytes. Optional (command specific).
    DATA     - Data array.
               Length: COUNT bytes. Optional (command specific).

  Some commands do not utilize all of these fields.
  See 'Command Table' below for details on specific command's format.

  * Command Table.
   --------------------------+---------------------------------------------------
  |       Description        |                      Format                       |
  |--------------------------+---------------------------------------------------|
  | Synchronize with PC tool |                  <STX><cmdSYNC>                   |
  |--------------------------+---------------------------------------------------|
  | Send bootloader info     |                  <STX><cmdINFO>                   |
  |--------------------------+---------------------------------------------------|
  | Go to bootloader mode    |                  <STX><cmdBOOT>                   |
  |--------------------------+---------------------------------------------------|
  | Restart MCU              |                  <STX><cmdREBOOT>                 |
  |--------------------------+---------------------------------------------------|
  | Write to MCU flash       | <STX><cmdWRITE><START_ADDR><DATA_LEN><DATA_ARRAY> |
  |--------------------------+---------------------------------------------------|
  | Erase MCU flash.         |  <STX><cmdERASE><START_ADDR><ERASE_BLOCK_COUNT>   |
   ------------------------------------------------------------------------------

  * Acknowledge format.

    <STX[0]><CMD_CODE[0]>
    |-- 1 --|---- 1 -----|

    STX      - Response start delimiter (for future upgrades).
               Length: 1 byte. Mandatory.
    CMD_CODE - Index of command (TCmd) we want to acknowledge.
               Length: 1 byte. Mandatory.

  See 'Acknowledgement Table' below for details on specific command's
  acknowledgement process.

  * Acknowledgement Table.
   --------------------------+---------------------------------------------------
  |       Description        |                   Acknowledgement                 |
  |--------------------------+---------------------------------------------------|
  | Synchronize with PC tool |                  upon reception                   |
  |--------------------------+---------------------------------------------------|
  | Send bootloader info     |          no acknowledge, just send info           |
  |--------------------------+---------------------------------------------------|
  | Go to bootloader mode    |                  upon reception                   |
  |--------------------------+---------------------------------------------------|
  | Restart MCU              |                  no acknowledge                   |
  |--------------------------+---------------------------------------------------|
  | Write to MCU flash       | upon each write of internal buffer data to flash  |
  |--------------------------+---------------------------------------------------|
  | Erase MCU flash.         |                  upon execution                   |
   ------------------------------------------------------------------------------*/

#include <Types.h>
#include <Config.h>
#include <built_in.h>

const STX = 0x0F;                // Start of TeXt.

int GPCounter = 0;               // Global Purpose Counter, used for counting anything that needs to be counted :)
int BytesToWrite  at GPCounter;  // Number of bytes PC application is sending for current write operation.
int BytesToGet    at GPCounter;  // Number of bytes we need to acquire from PC for current command.
int BlocksToErase at GPCounter;  // Number of blocks to erase in current erase operation.

unsigned long GPAddress = 0;     // Global Purpose Counter, used for addressing anything that needs to be addressed :)
unsigned long StartAddress at GPAddress; // Start address for current PC command.

// I/O Buffer
struct {
  char fBuffer[_FLASH_ERASE];    // Buffer data.
  char *fRWPtr;                  // Buffer read/write pointer.

  // Buffer's methods.
  #define Buffer_WriteByte(data)    *Buffer.fRWPtr++ = data               // Write a byte into buffer.
  #define Buffer_ReadByte()         *Buffer.fRWPtr++                      // Read a byte from buffer.
  #define Buffer_ReadByteAt(offset) *Buffer.fBuffer[offset]               // Read a byte from specific buffer location.
  #define Buffer_Seek(offset)       Buffer.fRWPtr = Buffer.fBuffer+offset // Set buffer poiner at specific buffer location.
  #define Buffer_Count()            Buffer.fRWPtr-Buffer.fBuffer          // Number of bytes in buffer.
  #define Buffer_Reset()            Buffer.fRWPtr = Buffer.fBuffer        // Set buffer poiner at buffer start.
  #define Buffer_Size()             sizeof(Buffer.fBuffer)                // Buffer's size in bytes.

  #define Buffer_SaveToFlash() _Buffer_SaveToFlash()                      // Write buffer content to flash memory at specified address.
} Buffer;

enum TCmd CmdCode = cmdNON;      // Current command code.

// Supported MCU families/types.
enum TMcuType {mtPIC16 = 1, mtPIC18, mtPIC18FJ, mtPIC24, mtDSPIC = 10, mtPIC32 = 20};

// Bootloader info field ID's.
enum TBootInfoField {bifMCUTYPE=1,  // MCU type/family.
                     bifMCUID,      // MCU ID number.
                     bifERASEBLOCK, // MCU flash erase block size.
                     bifWRITEBLOCK, // MCU flash write block size.
                     bifBOOTREV,    // Bootloader revision.
                     bifBOOTSTART,  // Bootloader start address.
                     bifDEVDSC,     // Device descriptor string.
                     bifMCUSIZE     // MCU flash size
                     };

// Byte field (1 byte).
typedef struct {
  char fFieldType;
  char fValue;
} TCharField;

// Int field (2 bytes).
typedef struct {
  char fFieldType;
  union {
    unsigned short intVal;
    struct {
      char bLo;
      char bHi;
    };
  } fValue;
} TUIntField;

// Long field (4 bytes).
typedef struct {
  char fFieldType;
  unsigned long fValue;
} TULongField;

// String field (MAX_STRING_FIELD_LENGTH bytes).
#define MAX_STRING_FIELD_LENGTH 20
typedef struct {
  char fFieldType;
  char fValue[MAX_STRING_FIELD_LENGTH];
} TStringField;

// Bootloader info record (device specific information).
typedef struct {
  char bSize;                   // 1
  TCharField   bMcuType;        // 2
  TULongField  ulMcuSize;       // 5
  TUIntField   uiEraseBlock;    // 3
  TUIntField   uiWriteBlock;    // 3
  TUIntField   uiBootRev;       // 3
  TULongField  ulBootStart;     // 5
  TStringField sDevDsc;         // 21
} TBootInfo;

// Bootloader info record.
// It is used by PC application tool to identify device and get device
// specific information.
const TBootInfo BootInfo = {
    sizeof(TBootInfo),                      // This record's size in bytes.
    {bifMCUTYPE,    MCU_TYPE},              // MCU family.
    {bifMCUSIZE,    __FLASH_SIZE},          // MCU flash size.
    {bifERASEBLOCK, _FLASH_ERASE},          // MCU Flash erase block size in bytes.
    {bifWRITEBLOCK, _FLASH_WRITE_LATCH},    // MCU Flash write block size in bytes.
    {bifBOOTREV,    BOOTLOADER_REVISION},   // Version of bootlaoder firmware.
    {bifBOOTSTART,  BOOTLOADER_START},      // Bootloader code start address.
    {bifDEVDSC,     DEVICE_NAME}            // Name of this device.
};

/******************************************************************************
 *                                                                            *
 *  Function:     static void _Buffer_SaveToFlash()                           *
 *                                                                            *
 *  Description:  Write data buffer to internal MCU flash.                    *
 *                                                                            *
 *  Parameters:   None.                                                       *
 *                                                                            *
 *  Return Value: None.                                                       *
 *                                                                            *
 *  Requirements: StartAddress must contain flash address where this write    *
 *                should occur.                                               *
 *                                                                            *
 *  Notes:        None.                                                       *
 *                                                                            *
 ****************************       CHANGE LOG       **************************
 * Version | ACTION                                           |  DATE  | SIG  *
 * --------|--------------------------------------------------|--------|----- *
 *         |                                                  |        |      *
 *    0.01 | - Initial release                                | 030511 |  ST  *
 *         |                                                  |        |      *
 ******************************************************************************/
static void _Buffer_SaveToFlash() {
  int bCount;                                     // Byte counter.

  bCount = Buffer_Count();                        // Get number of bytes in buffer.
  Buffer.fRWPtr = Buffer.fBuffer;                 // Reset buffer pointer.
  while (bCount > 0) {
    FLASH_Write(StartAddress, Buffer.fRWPtr);     // Write chunk (flash write latch size) of buffer data.
    bCount -= _FLASH_WRITE_LATCH;                 // Decrement bytes count.
    Buffer.fRWPtr += _FLASH_WRITE_LATCH;          // Increment buffer pointer.
    #ifdef __MIKROC_PRO_FOR_DSPIC__
    StartAddress += (_FLASH_WRITE_LATCH / 3) * 2; // Increment flash address.
    #else
    StartAddress += _FLASH_WRITE_LATCH;           // Increment flash address.
    #endif
  }
}

/******************************************************************************
 *                                                                            *
 *  Function:     static void SendBootInfo()                                  *
 *                                                                            *
 *  Description:  Send bootloader info record.                                *
 *                                                                            *
 *  Parameters:   None.                                                       *
 *                                                                            *
 *  Return Value: None.                                                       *
 *                                                                            *
 *  Requirements: Bootloader info record (BootInfo) must be loaded with       *
 *                appropriate values.                                         *
 *                                                                            *
 *  Notes:        None.                                                       *
 *                                                                            *
 ****************************       CHANGE LOG       **************************
 * Version | ACTION                                           |  DATE  | SIG  *
 * --------|--------------------------------------------------|--------|----- *
 *         |                                                  |        |      *
 *    0.01 | - Initial release                                | 030511 |  ST  *
 *         |                                                  |        |      *
 ******************************************************************************/
static void SendBootInfo() {
  typedef struct {
    char fArray[sizeof(TBootInfo)];
  } TBootInfoArray; // structure with an array type to handle copying.

  // Note: Additional handling needs to be taken if boot info record's size
  // exceeds HidWriteBuff size (64 bytes).
  *(TBootInfoArray *)(void *)HidWriteBuff = BootInfo; // Copy boot info record into transmit buffer.
  HID_Write(HidWriteBuff, 64);                        // Send boot info.
}

/******************************************************************************
 *                                                                            *
 *  Function:     static void Check4Cmd()                                     *
 *                                                                            *
 *  Description:  Check received USB HID packet for new command.              *
 *                                                                            *
 *  Parameters:   None.                                                       *
 *                                                                            *
 *  Return Value: None.                                                       *
 *                                                                            *
 *  Requirements: USB HID read buffer (HidReadBuff) must not be empty.        *
 *                Use HID_Read() first, to check if packet was received.      *
 *                                                                            *
 *  Notes:        None.                                                       *
 *                                                                            *
 ****************************       CHANGE LOG       **************************
 * Version | ACTION                                           |  DATE  | SIG  *
 * --------|--------------------------------------------------|--------|----- *
 *         |                                                  |        |      *
 *    0.01 | - Initial release                                | 030511 |  ST  *
 *         |                                                  |        |      *
 ******************************************************************************/
static void Check4Cmd() {
  if (CmdCode == cmdNON) {               // Are we in 'Idle' mode?
                                         //   New commands only in 'Idle' mode!
    if (HidReadBuff[0] != STX)           // Do we have an 'STX' at start?
                                         //   Each command must start with STX char.
      return ;                           // No, then exit.

    // Process received command.
    CmdCode = HidReadBuff[1];            // Get command code.
    Lo(GPAddress) = HidReadBuff[2];      // Get address lo byte.
    Hi(GPAddress) = HidReadBuff[3];      // Get address hi byte.
    Higher(GPAddress) = HidReadBuff[4];  // Get address higher byte.
    Highest(GPAddress) = HidReadBuff[5]; // Get address highest byte.
    Lo(GPCounter) = HidReadBuff[6];      // Get counter lo byte.
    Hi(GPCounter) = HidReadBuff[7];      // Get counter hi byte.
  }
  else {
    // maybe abort cmd...
  }
}

/******************************************************************************
 *                                                                            *
 *  Function:     static char GetData()                                       *
 *                                                                            *
 *  Description:  Get data from received USB HID packet.                      *
 *                Data are copied to data buffer.                             *
 *                Should be used within commands where we expect PC to send   *
 *                us some data (i.e. Flash Write command).                    *
 *                                                                            *
 *  Parameters:   None.                                                       *
 *                                                                            *
 *  Return Value: None.                                                       *
 *                                                                            *
 *  Requirements: - USB HID read buffer (HidReadBuff) must not be empty.      *
 *                  Use HID_Read() first, to check if packet was received.    *
 *                - BytesToGet counter must contain number of bytes we        *
 *                  expect to receive.                                        *
 *                                                                            *
 *  Notes:        None.                                                       *
 *                                                                            *
 ****************************       CHANGE LOG       **************************
 * Version | ACTION                                           |  DATE  | SIG  *
 * --------|--------------------------------------------------|--------|----- *
 *         |                                                  |        |      *
 *    0.01 | - Initial release                                | 030511 |  ST  *
 *         |                                                  |        |      *
 ******************************************************************************/
static char GetData() {
  char i;                                // HID read buffer byte counter.
  char *sPtr;                            // Local byte pointer.

  // Process received USB packet.
  sPtr = HidReadBuff;                    // Set local pointer to HID read buffer.
  i = 0;                                 // Clear HID read buffer byte counter.
  while (1) {
    if (!BytesToGet)                     // Did we get it all?
      return 1;                          //   Yes, return with all done.
    if (Buffer_Count() == Buffer_Size()) // Is data buffer full?
      return 1;                          //   Yes, return with buffer full.
    if (i == sizeof(HidReadBuff))        // End of received packet?
      return 0;                          //   Yes, return with more to get.
    Buffer_WriteByte(*sPtr++);           // Copy to buffer.
    BytesToGet--;                        // Decrement data buffer counter.
    i++;                                 // Increment HID read buffer byte counter
  }
  return 0;                              // Return with more to get.
}

/******************************************************************************
 *                                                                            *
 *  Function:     static void SendACK(enum TCmd cmd)                          *
 *                                                                            *
 *  Description:  Acknowledge command or part of command.                     *
 *                                                                            *
 *  Parameters:   - cmd: command code.                                        *
 *                                                                            *
 *  Return Value: None.                                                       *
 *                                                                            *
 *  Requirements: None.                                                       *
 *                                                                            *
 *  Notes:        None.                                                       *
 *                                                                            *
 ****************************       CHANGE LOG       **************************
 * Version | ACTION                                           |  DATE  | SIG  *
 * --------|--------------------------------------------------|--------|----- *
 *         |                                                  |        |      *
 *    0.01 | - Initial release                                | 030511 |  ST  *
 *         |                                                  |        |      *
 ******************************************************************************/
static void SendACK(enum TCmd cmd) {
  // Make acknowledgement packet.
  HidWriteBuff[0] = STX;       // Start of packet indetifier.
  HidWriteBuff[1] = cmd;       // Command code to acknowledge.
  HID_Write(HidWriteBuff, 64); // Send acknowledgement packet.
}

/******************************************************************************
 *                                                                            *
 *  Function:     void StartBootloader()                                      *
 *                                                                            *
 *  Description:  Enter bootloader mode and start bootloader stack execution. *
 *                                                                            *
 *  Parameters:   None.                                                       *
 *                                                                            *
 *  Return Value: None.                                                       *
 *                                                                            *
 *  Requirements: None.                                                       *
 *                                                                            *
 *  Notes:        None.                                                       *
 *                                                                            *
 ****************************       CHANGE LOG       **************************
 * Version | ACTION                                           |  DATE  | SIG  *
 * --------|--------------------------------------------------|--------|----- *
 *         |                                                  |        |      *
 *    0.01 | - Initial release                                | 030511 |  ST  *
 *         |                                                  |        |      *
 ******************************************************************************/
void StartBootloader() {
  char dataRx;           // Packet reception flag.
  char writeData = 0;    // Write command execution flag.

  Buffer_Reset();        // Reset data buffer.

  // Bootloader loop.
  while(1) {
    USB_Polling_Proc();  // Check USB.
    dataRx = HID_Read(); // Read received USB packet, if any.
    if (dataRx) {        // Do we have an incoming?
      dataRx = 0;        //   Yes, clear reception flag.
      Check4Cmd();       // Check received packet for new command.
      switch(CmdCode) {  // Process command.
        case cmdWRITE: { // Cmd: Write data to flash.
                        if (writeData) {   // Are we already executing an write command?
                          if (GetData()) { // Yes, then do we have some data to write?
#ifdef __MIKROC_PRO_FOR_PIC32__
                            if ((StartAddress < BOOTLOADER_START_PHY) ||
                                ((StartAddress >= MCU_BOOT_FLASH_START_PHY) &&
                                 (StartAddress <  MCU_BOOT_FLASH_CONFIG_BLOCK_START_PHY))) // Are we out of bootloader area?
#else
                            if (StartAddress < BOOTLOADER_START) // Are we out of bootloader area?
#endif
                              Buffer_SaveToFlash();              //   Yes, write data buffer to flash.
                            SendACK(CmdCode);                    // Acknowledge data write and ask for more if any.
                            Buffer_Reset();                      // Reset data buffer.
                            if (BytesToWrite == 0) {             // Are there more data to write?
                              writeData = 0;                     //   No, reset executing write command flag.
                              CmdCode = cmdNON;                  //   Set 'Idle' command code.
                            }
                          }
                        }
                        else {
                          writeData = 1; // Set executing write command flag.
                        }
                        break;
                       }
        case cmdERASE: { // Cmd: Erase flash.
                        while (BlocksToErase--) {                   // More to erase?
#ifdef __MIKROC_PRO_FOR_PIC32__
                          if ((StartAddress < BOOTLOADER_START_PHY) ||
                              ((StartAddress >= MCU_BOOT_FLASH_START_PHY) &&
                               (StartAddress <  MCU_BOOT_FLASH_CONFIG_BLOCK_START_PHY))) // Are we out of bootloader area?
#else
                          if (StartAddress < BOOTLOADER_START)      // Are we out of bootloader area?
#endif
                            FLASH_Erase(StartAddress);              //   Yes, erase flash block.
#ifdef __MIKROC_PRO_FOR_DSPIC__
                            StartAddress -= (_FLASH_ERASE / 3) * 2; //   Increment flash address.
#else
                            StartAddress -= _FLASH_ERASE;           //   Increment flash address.
#endif
                        }
                        SendACK(CmdCode);                           // Acknowledge flash erase command.
                        CmdCode = cmdNON;                           // Set 'Idle' command code.
                        break;
                       }
        case cmdSYNC: { // Cmd: Synchronize bootloader and PC app.
                       SendACK(CmdCode); // Acknowledge SYNC command.
                       CmdCode = cmdNON; // Set 'Idle' command code.
                       break;
                      }
        case cmdREBOOT: { // Cmd: Reboot the MCU.
                         #ifdef __MIKROC_PRO_FOR_PIC__
                         asm RESET; // Reset MCU.
                         #endif
                         #ifdef __MIKROC_PRO_FOR_DSPIC__
                         asm RESET; // Reset MCU.
                         #endif
                         #ifdef __MIKROC_PRO_FOR_PIC32__
                         Reset();   // Reset MCU.
                         #endif
                         CmdCode = cmdNON; // Set 'Idle' command code.
                         break;
                        }
      }
    }
  }
}

/******************************************************************************
 *                                                                            *
 *  Function:     char EnterBootloaderMode()                                  *
 *                                                                            *
 *  Description:  Device mode checker.                                        *
 *                Wait 5sec for PC to send boot request.                      *
 *                                                                            *
 *  Parameters:   None.                                                       *
 *                                                                            *
 *  Return Value: 1 - we have boot request.                                   *
 *                    Continue with bootloader mode.                          *
 *                0 - no boot request.                                        *
 *                    Continue with already loaded application code.          *
 *                                                                            *
 *  Requirements: None.                                                       *
 *                                                                            *
 *  Notes:        None.                                                       *
 *                                                                            *
 ****************************       CHANGE LOG       **************************
 * Version | ACTION                                           |  DATE  | SIG  *
 * --------|--------------------------------------------------|--------|----- *
 *         |                                                  |        |      *
 *    0.01 | - Initial release                                | 030511 |  ST  *
 *         |                                                  |        |      *
 ******************************************************************************/
char EnterBootloaderMode() {
char dataRx,             // Packet reception flag.
     timer = 250;        // 5sec timer = 250 * 20ms.

  // Check for bootloader request loop.
  while (1) {
    USB_Polling_Proc();  // Check USB.
    dataRx = HID_Read(); // Read received USB packet, if any.
    if (dataRx) {        // Do we have an incoming?
      dataRx = 0;        //   Yes, clear reception flag.
      Check4Cmd();       // Check received packet for new command.
      switch (cmdCode) { // Process command.
        case cmdBOOT: {  // Cmd: Enter bootloader mode.
                        SendACK(CmdCode);   // Acknowledge enter bootloader mode command.
                        CmdCode = cmdNON;   // Set 'Idle' command code.
                        Delay_10ms();
                        return 1;           // Return with do bootloader code.
                      }
         case cmdINFO: { // Cmd: Get bootloader info.
                          SendBootInfo();   // Send bootloader info record.
                          CmdCode = cmdNON; // Set 'Idle' command code.
                          break;
                        }
      }
    }
    // make 20 seconds for 5sec delay
    Delay_10ms();
    Delay_10ms();
    if (!(timer--)) // Do we have a timeout?
      return 0;     //   Yes, return with do application code.
  }
}

/******************************************************************************
 *                                                                            *
 *  Function:     void StartProgram()                                         *
 *                                                                            *
 *  Description:  Application reset vector holder.                            *
 *                                                                            *
 *  Parameters:   None.                                                       *
 *                                                                            *
 *  Return Value: None.                                                       *
 *                                                                            *
 *  Requirements: - Size of this routine must be equal to MCU reset           *
 *                  vector size.                                              *
 *                - Must be placed at                                         *
 *                  BOOTLOADER_START-RESET_VECTOR_SIZE address.               *
 *                                                                            *
 *  Notes:        None.                                                       *
 *                                                                            *
 ****************************       CHANGE LOG       **************************
 * Version | ACTION                                           |  DATE  | SIG  *
 * --------|--------------------------------------------------|--------|----- *
 *         |                                                  |        |      *
 *    0.01 | - Initial release                                | 030511 |  ST  *
 *         |                                                  |        |      *
 ******************************************************************************/
void StartProgram() {
  // nops to accomodate MCU reset vector size.
  asm nop;
  #ifdef __MIKROC_PRO_FOR_PIC32__
  asm nop;
  #endif
}
Clone this wiki locally