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:
- Baud: 230400
- Data: 8 Bits
- Parity: None
- 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