8.2.1 8-bit Host Assisted DFU Code

Following example provides the 8-bit Host Assisted DFU pattern generation code:
void DFU_PE_InjectTestPattern(void)
{
    /* Programming Executive (PE) test pattern 'MCHP' PGC->Tx PGC->Rx*/
    const char *PE_TP_MCLR = "100000000000000000000000000000000000000000000000000000000000000001";
    const char *PE_TP_PGC  = "001010101010101010101010101010101010101010101010101010101010101011";
    const char *PE_TP_PGD  = "000110000111100110011000000001111001100001100000000110011000000000";

    DBG_MSG_OTA("* Sending test pattern *\r\n%s\r\n%s\r\n%s\r\n", PE_TP_MCLR, PE_TP_PGC, PE_TP_PGD);
    UART2.Deinitialize();

    MCLR_SetDigitalOutput();
    PGC_SetDigitalOutput();
    PGD_SetDigitalOutput();

    DELAY_milliseconds(MSEC_TO_SEC);

    for (volatile int i=0; i<(int)strlen(PE_TP_MCLR); i++)
    {
        /* MCLR - CHipEnable*/
        if((PE_TP_MCLR[i] - '0'))
            MCLR_SetHigh();
        else
            MCLR_SetLow();
        /* UART TX */
        if((PE_TP_PGC[i] - '0'))
            PGC_SetHigh();
        else
            PGC_SetLow();

        /* UART RX */
        if((PE_TP_PGD[i] - '0'))
            PGD_SetHigh();
        else
            PGD_SetLow();

        DELAY_microseconds(TP_DELAY_USEC);
    }

    PGD_SetDigitalInput();
    MCLR_SetDigitalInput();
    UART2.Initialize();
}
After successfully entering the DFU mode, the PB0/DFU_Rx (Pin 26) and PB1/DFU_Tx (Pin 10) pins will be reconfigured as UART lines with the below configuration:
  1. Baud: 230400
  2. Data: 8 Bits
  3. Parity: None
  4. Stop: 1 Bit
In DFU mode, the RNWF02 module runs a program executive (PE) firmware which can support following operations:
  • PE version read
  • Device ID read
  • Flash erase
  • Flash write
These PE operations are triggered using a 4 bytes command frame and response length depends on the requested operation. The successful entry of the DFU mode is verified by reading the PE version and Device ID. Following snippet of code can read the PE version (0x01) and Device ID (0x29C7x053).
uint8_t DFU_PE_Version(void)
{
    uint32_t data = 0;
    uint8_t  peVersion = 0;
    uint8_t  byteResp[4];

    data = PE_CMD_EXEC_VERSION;
    data = (data << 16) | 0x1;

    DBG_MSG_OTA("Sending PE version request\r\n");
#ifdef DFU_DEBUG
    DBG_MSG_OTA("%08x\r\n", (unsigned int)DFU_PE_htonl(data));
#endif

    RNWF_IF_Write((uint8_t *)&data, 4);
    
    if(RNWF_IF_Read(byteResp, 4) == 4)
    {
        peVersion = byteResp[0];
        DBG_MSG_OTA("PE version: %d\r\n\r\n", (unsigned int)peVersion);
        return peVersion;
    }
    return 0;
}

uint32_t DFU_PE_Chip_ID(void)
{
    uint32_t data = 0;    
    uint32_t  byteResp[2];

    data = PE_CMD_GET_DEVICE_ID;
    data = (data << 16) | 0x01;

    DBG_MSG_OTA("Sending PE chip ID request\r\n");
#ifdef DFU_DEBUG
    DBG_MSG_OTA("%08x\r\n", (unsigned int)DFU_PE_htonl(data));
#endif
    RNWF_IF_Write((uint8_t *)&data, 4);

    /* Response */
    RNWF_IF_Read((uint8_t *)byteResp, 8);

    DBG_MSG_OTA("Chip ID: %lX\r\n", (uint32_t)byteResp[1]);
    return byteResp[1];
}
Once the device is in the DFU mode, the device's secured Flash can be erased and Firmware binary can be written over the UART interface. The erase operation takes the starting address (0x6000-0000 or 0x600F-0000) and the number of pages of 4096 bytes.
bool DFU_PE_Erase(const uint32_t address, const uint32_t length)
{
    uint32_t data = 0;
    uint32_t pages = length / (uint32_t)PE_ERASE_PAGE_SIZE;
    uint8_t  byteResp[4];

    if (length % (uint32_t)PE_ERASE_PAGE_SIZE > (uint32_t)0)
    {
        pages += (uint32_t)1;
    }
    DBG_MSG_OTA("PE erase pages = %d\r\n", pages);

    data = PE_CMD_PAGE_ERASE;
    data = data << 16;
    data |= (pages &= 0x0000ffff);


    DBG_MSG_OTA("Sending PE erase\r\n");
#ifdef DFU_DEBUG
    DBG_MSG_OTA("%08x\r\n", (unsigned int)DFU_PE_htonl(data));
#endif
    RNWF_IF_Write((uint8_t *)&data, 4);
    DELAY_microseconds(WRITE_DELAY_USEC);

#ifdef DFU_DEBUG
    DBG_MSG_OTA("%08x\r\n", (unsigned int)DFU_PE_htonl(address));
#endif
    RNWF_IF_Write((uint8_t *)&address, 4);
    DELAY_microseconds(WRITE_DELAY_USEC);
                
    DELAY_milliseconds(3500);
    /* Response */
    RNWF_IF_Read(byteResp, 4);
        
    if (((char)byteResp[2] != (char)PE_CMD_PAGE_ERASE) || ((char)byteResp[0] != (char)0) || ((char)byteResp[1] != (char)0))
    {
        DBG_MSG_OTA("Error: PE erase failed\r\n");
        return false;
    }

    DBG_MSG_OTA("\r\nErase done!\r\n");
    return true;
}
The DFU write operation takes the address, firmware binary and the length of integer factors of 4096 bytes but not exceeding it. Following is the reference code for implementing the DFU write operation.
bool DFU_PE_Write(uint32_t address, const uint32_t length, uint8_t *PE_writeBuffer)
{
 /* The address must be 32-bit aligned, and the number of bytes (length) must be a
 multiple of a 32-bit word. */
 uint32_t data = 0;
 uint32_t checksumValue = 0;
 uint8_t byteResp[4];
 if (length>(uint16_t)MAX_PE_WRITE_SIZE)
 {
 DBG_MSG_OTA("ERROR: Length exceeds MAX_PE_WRITE_SIZE\r\n");
 return false;
 }
 /* Length should be integer factor of 4096 and divisible by 4 */
 if ((((uint16_t)MAX_PE_WRITE_SIZE % length) != (uint16_t)0) || ((length % (uint16_t)4) != (uint16_t)0))
 {
 DBG_MSG_OTA("ERROR: Length should be integer factor of 4096 and divisible by 4\r\n");
 return false;
 }
 /* Assemble PE write command */
 data |= ((uint32_t)0x0000ffff & (uint32_t)PE_CMD_PGM_CLUSTER_VERIFY);
 data = data << 16;
 data |= (CFGMethod & 0x0000ffff);
#ifdef DFU_DEBUG
 DBG_MSG_OTA("ID:\r\n");
 DBG_MSG_OTA("%08x\r\n", (unsigned int)DFU_PE_htonl(data));
#endif
 RNWF_IF_Write((uint8_t *)&data, sizeof(uint32_t));
 DELAY_microseconds(WRITE_DELAY_USEC);
#ifdef DFU_DEBUG
 /* Address */
 DBG_MSG_OTA("Address:\r\n");
 DBG_MSG_OTA("%08x\r\n", (unsigned int)DFU_PE_htonl(address));
#endif
 RNWF_IF_Write((uint8_t *)&address, sizeof(uint32_t));
 DELAY_microseconds(WRITE_DELAY_USEC);
#ifdef DFU_DEBUG
 /* Length */
 DBG_MSG_OTA("Length: %d\r\n", (unsigned int)length);
 DBG_MSG_OTA("%08x\r\n", (unsigned int)DFU_PE_htonl(length));
#endif
 RNWF_IF_Write((uint8_t *)&length, sizeof(uint32_t));
 DELAY_microseconds(WRITE_DELAY_USEC);
 /* Checksum */
 for (uint16_t i=0; i<length; i++)
 {
 checksumValue += PE_writeBuffer[i];
 }
#ifdef DFU_DEBUG
 DBG_MSG_OTA("Checksum:\r\n");
 DBG_MSG_OTA("%08x\r\n", (unsigned int)DFU_PE_htonl(checksumValue));
#endif
 RNWF_IF_Write((uint8_t *)&checksumValue, sizeof(uint32_t));
 DELAY_microseconds(WRITE_DELAY_USEC);
#ifdef DFU_DEBUG
 /* Data */
 DBG_MSG_OTA("PE_writeBuffer:\r\n");
#endif 
// for (uint16_t i=0; i<length; i++)
 {
#ifdef DFU_DEBUG
 DBG_MSG_OTA("%02x ", PE_writeBuffer[i]);
#endif 
 RNWF_IF_Write((uint8_t *)PE_writeBuffer, length);
// RNWF_IF_Write((uint8_t *)&PE_writeBuffer[i], 1);
 DELAY_microseconds(60);
 }
#ifdef DFU_DEBUG
 DBG_MSG_OTA("\r\n");
#endif 
 /* Response */
 RNWF_IF_Read(byteResp, 4);
 /* Verify response for errors */
 if (((char)byteResp[2] != (char)PE_CMD_PGM_CLUSTER_VERIFY) || ((char)byteResp[0] != (char)0) || ((char)byteResp[1] != (char)0))
 {
 DBG_MSG_OTA("Error: PE write failed[%02X %02X %02X %02X]\r\n", byteResp[0], byteResp[1], byteResp[2], byteResp[3]);
 return false;
 }
 return true;
}
Note:
  • The OTA service layer implements these functionality to ease the development of Host Assisted OTA.
  • For the Host assisted DFU, the Host side pins must be able to drive the pattern and also reconfigure the same pins as UART lines using the pin multiplexing options. Recommendation is to use Microchip’s Microcontrollers as host which can support this feature by default