From 75d70c5200309a64167cee25bc3b64c1d51d1d7f Mon Sep 17 00:00:00 2001 From: Boris Honman Date: Sun, 11 Aug 2019 13:48:04 -0400 Subject: [PATCH] started porting over the actual bootloader code from the MLA demo --- bootloader/src/bootloader.c | 793 ++++++++++++++++++++++++++++++++++++ bootloader/src/bootloader.h | 47 +++ bootloader/src/main.c | 5 +- bootloader/src/typedefs.h | 426 +++++++++++++++++++ bootloader/src/usb_config.h | 3 + bootloader/src/usb_events.c | 5 +- 6 files changed, 1277 insertions(+), 2 deletions(-) create mode 100644 bootloader/src/bootloader.c create mode 100644 bootloader/src/bootloader.h create mode 100644 bootloader/src/typedefs.h diff --git a/bootloader/src/bootloader.c b/bootloader/src/bootloader.c new file mode 100644 index 0000000..58cd024 --- /dev/null +++ b/bootloader/src/bootloader.c @@ -0,0 +1,793 @@ +#include "bootloader.h" +#include "typedefs.h" +#include "usb.h" +#include "usb_device_hid.h" + + +//The bootloader version, which the bootloader PC application can do extended query to get. +//Value provided is expected to be in the format of BOOTLOADER_VERSION_MAJOR.BOOTLOADER_VERSION_MINOR +//Ex: 1.01 would be BOOTLOADER_VERSION_MAJOR == 1, and BOOTLOADER_VERSION_MINOR == 1 +#define BOOTLOADER_VERSION_MAJOR 1 //Legal value 0-255 +#define BOOTLOADER_VERSION_MINOR 2 //Legal value 0-99. (1 = X.01) + +//Section defining the address range to erase for the erase device command, along with the valid programming range to be reported by the QUERY_DEVICE command. +#define PROGRAM_MEM_START_ADDRESS REMAPPED_APPLICATION_RESET_VECTOR //Beginning of application program memory (not occupied by bootloader). **THIS VALUE MUST BE ALIGNED WITH 64 BYTE BLOCK BOUNDRY** Also, in order to work correctly, make sure the StartPageToErase is set to erase this section. + +#define MAX_PAGE_TO_ERASE 511 //Last 64 byte page of flash on the PIC18F4550 +#define PROGRAM_MEM_STOP_ADDRESS 0x008000 //**MUST BE WORD ALIGNED (EVEN) ADDRESS. This address does not get updated, but the one just below it does: IE: If PROGRAM_MEM_STOP_ADDRESS = 0x200, 0x1FF is the last programmed address (0x200 not programmed)** +#define CONFIG_WORDS_START_ADDRESS 0x300000 //0x300000 is start of CONFIG space for these devices +#define CONFIG_WORDS_SECTION_LENGTH 14 //14 bytes worth of Configuration words on these devices +#define USER_ID_ADDRESS 0x200000 //User ID is 8 bytes starting at 0x200000 +#define USER_ID_SIZE 8 +#define DEVICE_WITH_EEPROM //Comment this out, if you never want the bootloader to reprogram the EEPROM space +#define EEPROM_SIZE 0x100 //256 bytes of EEPROM on this device +#define EEPROM_EFFECTIVE_ADDRESS 0xF00000 //Location in the .hex file where the EEPROM contents are stored +#define WRITE_BLOCK_SIZE 0x20 //32 byte programming block size on the PIC18F4550/PIC18F4553 family devices +#define ERASE_PAGE_SIZE 64 + +//Derived microcontroller address/page constants +#define START_PAGE_TO_ERASE (PROGRAM_MEM_START_ADDRESS/ERASE_PAGE_SIZE) //The first flash page number to erase, which is the start of the application program space +#define ERASE_PAGE_ADDRESS_MASK (uint24_t)(0x1000000 - ERASE_PAGE_SIZE) //AND mask to move an address pointer to the start of an erase page + + + +//Bootloader Command From Host - Switch() State Variable Choices +#define QUERY_DEVICE 0x02 //Command that the host uses to learn about the device (what regions can be programmed, and what type of memory is the region) +#define UNLOCK_CONFIG 0x03 //Note, this command is used for both locking and unlocking the config bits (see the "//Unlock Configs Command Definitions" below) +#define ERASE_DEVICE 0x04 //Host sends this command to start an erase operation. Firmware controls which pages should be erased. +#define PROGRAM_DEVICE 0x05 //If host is going to send a full RequestDataBlockSize to be programmed, it uses this command. +#define PROGRAM_COMPLETE 0x06 //If host send less than a RequestDataBlockSize to be programmed, or if it wished to program whatever was left in the buffer, it uses this command. +#define GET_DATA 0x07 //The host sends this command in order to read out memory from the device. Used during verify (and read/export hex operations) +#define RESET_DEVICE 0x08 //Resets the microcontroller, so it can update the config bits (if they were programmed, and so as to leave the bootloader (and potentially go back into the main application) +#define SIGN_FLASH 0x09 //The host PC application should send this command after the verify operation has completed successfully. If checksums are used instead of a true verify (due to ALLOW_GET_DATA_COMMAND being commented), then the host PC application should send SIGN_FLASH command after is has verified the checksums are as exected. The firmware will then program the SIGNATURE_WORD into flash at the SIGNATURE_ADDRESS. +#define QUERY_EXTENDED_INFO 0x0C //Used by host PC app to get additional info about the device, beyond the basic NVM layout provided by the query device command + +//Unlock Configs Command Definitions +#define UNLOCKCONFIG 0x00 //Sub-command for the ERASE_DEVICE command +#define LOCKCONFIG 0x01 //Sub-command for the ERASE_DEVICE command + +//Query Device Response "Types" +#define MEMORY_REGION_PROGRAM_MEM 0x01 //When the host sends a QUERY_DEVICE command, need to respond by populating a list of valid memory regions that exist in the device (and should be programmed) +#define MEMORY_REGION_EEDATA 0x02 +#define MEMORY_REGION_CONFIG 0x03 +#define MEMORY_REGION_USERID 0x04 +#define MEMORY_REGION_END 0xFF //Sort of serves as a "null terminator" like number, which denotes the end of the memory region list has been reached. +#define BOOTLOADER_V1_01_OR_NEWER_FLAG 0xA5 //Tacked on in the VersionFlag byte, to indicate when using newer version of bootloader with extended query info available + + +//BootState Variable States +#define IDLE 0x00 +#define NOT_IDLE 0x01 + +//OtherConstants +#define INVALID_ADDRESS 0xFFFFFFFF +#define CORRECT_UNLOCK_KEY 0xB5 + +//Application and Microcontroller constants +#define BYTES_PER_ADDRESS_PIC18 0x01 //One byte per address. PIC24 uses 2 bytes for each address in the hex file. +#define USB_PACKET_SIZE 0x40 +#define WORDSIZE 0x02 //PIC18 uses 2 byte words, PIC24 uses 3 byte words. +#define REQUEST_DATA_BLOCK_SIZE 0x3A //Number of data bytes in a standard request to the PC. Must be an even number from 2-58 (0x02-0x3A). Larger numbers make better use of USB bandwidth and + //yeild shorter program/verify times, but require more micrcontroller RAM for buffer space. + + + +/** USB Packet Request/Response Formatting Structure **********************************************************/ +typedef union +{ + unsigned char Contents[USB_PACKET_SIZE]; + + //General command (with data in it) packet structure used by PROGRAM_DEVICE and GET_DATA commands + struct{ + unsigned char Command; + unsigned long Address; + unsigned char Size; + //unsigned char PadBytes[58-REQUEST_DATA_BLOCK_SIZE]; //Uncomment this if using a smaller than 0x3A RequestDataBlockSize. Compiler doesn't like 0 byte array when using 58 byte data block size. + unsigned char Data[REQUEST_DATA_BLOCK_SIZE]; + }; + + //This struct used for responding to QUERY_DEVICE command (on a device with four programmable sections) + struct{ + unsigned char Command; + unsigned char PacketDataFieldSize; + unsigned char BytesPerAddress; + unsigned char Type1; + unsigned long Address1; + unsigned long Length1; + unsigned char Type2; + unsigned long Address2; + unsigned long Length2; + unsigned char Type3; + unsigned long Address3; + unsigned long Length3; + unsigned char Type4; + unsigned long Address4; + unsigned long Length4; + unsigned char Type5; + unsigned long Address5; + unsigned long Length5; + unsigned char Type6; + unsigned long Address6; + unsigned long Length6; + unsigned char VersionFlag; //Used by host software to identify if device is new enough to support QUERY_EXTENDED_INFO command + unsigned char ExtraPadBytes[7]; + }; + + struct{ //For UNLOCK_CONFIG command + unsigned char Command; + unsigned char LockValue; + }; + + //Structure for the QUERY_EXTENDED_INFO command (and response) + struct{ + unsigned char Command; + unsigned int BootloaderVersion; + unsigned int ApplicationVersion; + unsigned long SignatureAddress; + unsigned int SignatureValue; + unsigned long ErasePageSize; + unsigned char Config1LMask; + unsigned char Config1HMask; + unsigned char Config2LMask; + unsigned char Config2HMask; + unsigned char Config3LMask; + unsigned char Config3HMask; + unsigned char Config4LMask; + unsigned char Config4HMask; + unsigned char Config5LMask; + unsigned char Config5HMask; + unsigned char Config6LMask; + unsigned char Config6HMask; + unsigned char Config7LMask; + unsigned char Config7HMask; + }; +} PacketToFromPC; + +PacketToFromPC PacketFromPC; +PacketToFromPC PacketToPC; +unsigned char ProgrammingBuffer[ERASE_PAGE_SIZE]; +unsigned char BootState; +unsigned int ErasePageTracker; +unsigned char BufferedDataIndex; +uint24_t ProgrammedPointer; +unsigned char ConfigsLockValue; + +USB_VOLATILE USB_HANDLE txHandle = 0; +USB_VOLATILE USB_HANDLE rxHandle = 0; + +void WriteFlashBlock(void); +void WriteConfigBits(void); +void WriteEEPROM(void); +void UnlockAndActivate(unsigned char UnlockKey); +void ResetDeviceCleanly(void); +void TableReadPostIncrement(void); +void SignFlash(void); +void LowVoltageCheck(void); + +void UserInit(void) +{ + //Initialize bootloader state variables + BootState = IDLE; + ProgrammedPointer = INVALID_ADDRESS; + BufferedDataIndex = 0; + ConfigsLockValue = TRUE; +} + +/****************************************************************************** + * Function: void ProcessIO(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This function receives/sends USB packets to/from the USB + * host. It also processes any received OUT packets and + * is reponsible for generating USB IN packet data. + * + * Note: None + *****************************************************************************/ +void ProcessIO(void) +{ + static unsigned char i; + static ROM uint8_t* pROM; + + //Checks for and processes application related USB packets (assuming the + //USB bus is in the CONFIGURED_STATE, which is the only state where + //the host is allowed to send application related USB packets to the device. + if((USBGetDeviceState() != CONFIGURED_STATE) || (USBIsDeviceSuspended() == 1)) + { + //No point to trying to run the application code until the device has + //been configured (finished with enumeration) and is not currently suspended. + return; + } + + //Check the current bootloader state (if we are currently waiting from a new + //command to process from the host, or if we are still processing a previous + //command. + if(BootState == IDLE) + { + //We are currently in the IDLE state waiting for a command from the + //PC software on the USB host. + if(!HIDRxHandleBusy(rxHandle)) //Did we receive a command? + { + //We received a new command from the host. Copy the OUT packet from + //the host into a local buffer for processing. + rxHandle = HIDRxPacket(HID_IN_EP, (char *)&PacketFromPC, USB_PACKET_SIZE); //Also re-arms the OUT endpoint to be able to receive the next packet + //HIDRxReport((char *)&PacketFromPC, USB_PACKET_SIZE); //Also re-arms the OUT endpoint to be able to receive the next packet + BootState = NOT_IDLE; //Set flag letting state machine know it has a command that needs processing. + + //Pre-initialize a response packet buffer (only used for some commands) + for(i = 0; i < USB_PACKET_SIZE; i++) //Prepare the next packet we will send to the host, by initializing the entire packet to 0x00. + PacketToPC.Contents[i] = 0; //This saves code space, since we don't have to do it independently in the QUERY_DEVICE and GET_DATA cases. + } + }//if(BootState == IDLE) + else //(BootState must be NOT_IDLE) + { + //Check the latest command we received from the PC app, to determine what + //we should be doing. + switch(PacketFromPC.Command) + { + case QUERY_DEVICE: + //Make sure the USB IN endpoint buffer is available, then load + //up a query response packet to send to the host. + if(!HIDTxHandleBusy(txHandle)) + { + //Prepare a response packet, which lets the PC software know about the memory ranges of this device. + PacketToPC.Command = QUERY_DEVICE; + PacketToPC.PacketDataFieldSize = REQUEST_DATA_BLOCK_SIZE; + PacketToPC.BytesPerAddress = BYTES_PER_ADDRESS_PIC18; + PacketToPC.Type1 = MEMORY_REGION_PROGRAM_MEM; + PacketToPC.Address1 = (unsigned long)PROGRAM_MEM_START_ADDRESS; + PacketToPC.Length1 = (unsigned long)(PROGRAM_MEM_STOP_ADDRESS - PROGRAM_MEM_START_ADDRESS); //Size of program memory area + PacketToPC.Type2 = MEMORY_REGION_CONFIG; + PacketToPC.Address2 = (unsigned long)CONFIG_WORDS_START_ADDRESS; + PacketToPC.Length2 = (unsigned long)CONFIG_WORDS_SECTION_LENGTH; + PacketToPC.Type3 = MEMORY_REGION_USERID; //Not really program memory (User ID), but may be treated as it it was as far as the host is concerned + PacketToPC.Address3 = (unsigned long)USER_ID_ADDRESS; + PacketToPC.Length3 = (unsigned long)(USER_ID_SIZE); + PacketToPC.Type4 = MEMORY_REGION_END; + #if defined(DEVICE_WITH_EEPROM) + PacketToPC.Type4 = MEMORY_REGION_EEDATA; + PacketToPC.Address4 = (unsigned long)EEPROM_EFFECTIVE_ADDRESS; + PacketToPC.Length4 = (unsigned long)EEPROM_SIZE; + PacketToPC.Type5 = MEMORY_REGION_END; + #endif + PacketToPC.VersionFlag = BOOTLOADER_V1_01_OR_NEWER_FLAG; + //Init pad bytes to 0x00... Already done after we received the QUERY_DEVICE command (just after calling HIDRxReport()). + + //Now send the packet to the USB host software, assuming the USB endpoint is available/ready to accept new data. + txHandle = HIDTxPacket(HID_OUT_EP, (char *)&PacketToPC, USB_PACKET_SIZE); + BootState = IDLE; + } + break; + case UNLOCK_CONFIG: + ConfigsLockValue = TRUE; + if(PacketFromPC.LockValue == UNLOCKCONFIG) + { + ConfigsLockValue = FALSE; + } + BootState = IDLE; + break; + case ERASE_DEVICE: + //First erase main program flash memory + for(ErasePageTracker = START_PAGE_TO_ERASE; ErasePageTracker < (unsigned int)(MAX_PAGE_TO_ERASE + 1); ErasePageTracker++) + { + ClearWatchdog(); + #ifdef __XC8__ + TBLPTRU = 0x00; + TBLPTRH = (uint8_t)((uint24_t)ErasePageTracker >> 2); + TBLPTRL = (uint8_t)((uint24_t)ErasePageTracker << 6); + #else + TBLPTR = ErasePageTracker << 6; + #endif + EECON1 = 0b10010100; //Prepare for erasing flash memory + UnlockAndActivate(CORRECT_UNLOCK_KEY); + USBDeviceTasks(); //Call USBDeviceTasks() periodically to prevent falling off the bus if any SETUP packets should happen to arrive. + } + + #if defined(DEVICE_WITH_EEPROM) + //Now erase EEPROM (if any is present on the device) + i = EEPROM_EFFECTIVE_ADDRESS & (EEPROM_SIZE-1); + do{ + EEADR = i; + EEDATA = 0xFF; + EECON1 = 0b00000100; //EEPROM Write mode + USBDeviceTasks(); //Call USBDeviceTasks() periodically to prevent falling off the bus if any SETUP packets should happen to arrive. + UnlockAndActivate(CORRECT_UNLOCK_KEY); + }while(i++<((EEPROM_SIZE-1)+(EEPROM_EFFECTIVE_ADDRESS & (EEPROM_SIZE-1)))); + #endif + + //Now erase the User ID space (0x200000 to 0x200007) + //TBLPTR = USER_ID_ADDRESS; + TBLPTRU = 0x20; + TBLPTRH = 0x00; + TBLPTRL = (uint8_t)USER_ID_ADDRESS; + EECON1 = 0b10010100; //Prepare for erasing flash memory + UnlockAndActivate(CORRECT_UNLOCK_KEY); + + BootState = IDLE; + break; + case PROGRAM_DEVICE: + //Check if host is trying to program the config bits + if(PacketFromPC.Contents[3] == 0x30) // //PacketFromPC.Contents[3] is bits 23:16 of the address. + { //0x30 implies config bits + if(ConfigsLockValue == FALSE) + { + WriteConfigBits(); //Doesn't get reprogrammed if the UNLOCK_CONFIG (LockValue = UNLOCKCONFIG) command hasn't previously been sent + } + BootState = IDLE; + break; + } + + #if defined(DEVICE_WITH_EEPROM) + //Check if host is trying to program the EEPROM + if(PacketFromPC.Contents[3] == 0xF0) //PacketFromPC.Contents[3] is bits 23:16 of the address. + { //0xF0 implies EEPROM + WriteEEPROM(); + BootState = IDLE; + break; + } + #endif + + if(ProgrammedPointer == (uint24_t)INVALID_ADDRESS) + ProgrammedPointer = PacketFromPC.Address; + + if(ProgrammedPointer == (uint24_t)PacketFromPC.Address) + { + for(i = 0; i < PacketFromPC.Size; i++) + { + ProgrammingBuffer[BufferedDataIndex] = PacketFromPC.Data[i+(REQUEST_DATA_BLOCK_SIZE-PacketFromPC.Size)]; //Data field is right justified. Need to put it in the buffer left justified. + BufferedDataIndex++; + ProgrammedPointer++; + if(BufferedDataIndex == WRITE_BLOCK_SIZE) + { + WriteFlashBlock(); + } + } + } + //else host sent us a non-contiguous packet address... to make + //this firmware simpler, host should not do this without sending + //a PROGRAM_COMPLETE command in between program sections. + BootState = IDLE; + break; + case PROGRAM_COMPLETE: + WriteFlashBlock(); + ProgrammedPointer = INVALID_ADDRESS; //Reinitialize pointer to an invalid range, so we know the next PROGRAM_DEVICE will be the start address of a contiguous section. + BootState = IDLE; + break; + case GET_DATA: + //Init pad bytes to 0x00... Already done after we received the QUERY_DEVICE command (just after calling HIDRxReport()). + PacketToPC.Command = GET_DATA; + PacketToPC.Address = PacketFromPC.Address; + PacketToPC.Size = PacketFromPC.Size; + + pROM = (ROM uint8_t*)PacketFromPC.Address; + for(i = 0; i < PacketFromPC.Size; i++) + { + if(PacketFromPC.Contents[3] == 0xF0) //PacketFromPC.Contents[3] is bits 23:16 of the address. + { //0xF0 implies EEPROM, which doesn't use the table pointer to read from + #if defined(DEVICE_WITH_EEPROM) + EEADR = (((unsigned char)PacketFromPC.Address) + i); //The bits 7:0 are 1:1 mapped to the EEPROM address space values + EECON1 = 0b00000000; //EEPROM read mode + EECON1bits.RD = 1; + PacketToPC.Data[i+((USB_PACKET_SIZE - 6) - PacketFromPC.Size)] = EEDATA; + #endif + } + else //else must have been a normal program memory region, or one that can be read from with the table pointer + { + PacketToPC.Data[i+((USB_PACKET_SIZE - 6) - PacketFromPC.Size)] = *pROM++; + } + } + + //Assuming the USB IN (to host) buffer is available/ready, copy the + //data over so it can get sent to the USB host software. + if(!HIDTxHandleBusy(txHandle)) + { + HIDTxPacket(HID_OUT_EP, (char *)&PacketToPC, USB_PACKET_SIZE); + BootState = IDLE; + } + break; + case SIGN_FLASH: + SignFlash(); + BootState = IDLE; + break; + case QUERY_EXTENDED_INFO: + //Prepare a response packet with the QUERY_EXTENDED_INFO response info in it. + //This command is only supported in bootloader firmware verison 1.01 or later. + //Make sure the regular QUERY_DEVIER reponse packet value "PacketToPC.Type6" is = BOOTLOADER_V1_01_OR_NEWER_FLAG; + //to let the host PC software know that the QUERY_EXTENDED_INFO command is implemented + //in this firmware and is available for requesting by the host software. + PacketToPC.Command = QUERY_EXTENDED_INFO; //Echo the command byte + PacketToPC.BootloaderVersion = ((unsigned int)BOOTLOADER_VERSION_MAJOR << 8)| BOOTLOADER_VERSION_MINOR; + PacketToPC.ApplicationVersion = *(ROM unsigned int*)APP_VERSION_ADDRESS; + PacketToPC.SignatureAddress = APP_SIGNATURE_ADDRESS; + PacketToPC.SignatureValue = APP_SIGNATURE_VALUE; + PacketToPC.ErasePageSize = ERASE_PAGE_SIZE; + PacketToPC.Config1LMask = 0xFF; + PacketToPC.Config1HMask = 0xFF; + PacketToPC.Config2LMask = 0xFF; + PacketToPC.Config2HMask = 0xFF; + PacketToPC.Config3LMask = 0x00; + PacketToPC.Config3HMask = 0xFF; + PacketToPC.Config4LMask = 0xFF; + PacketToPC.Config4HMask = 0x00; + PacketToPC.Config5LMask = 0xFF; + PacketToPC.Config5HMask = 0xFF; + PacketToPC.Config6LMask = 0xFF; + PacketToPC.Config6HMask = 0xFF; + PacketToPC.Config7LMask = 0xFF; + PacketToPC.Config7HMask = 0xFF; + + //Now actually command USB to send the packet to the host + if(!HIDTxHandleBusy(txHandle)) + { + HIDTxPacket(HID_OUT_EP, (char *)&PacketToPC, USB_PACKET_SIZE); + BootState = IDLE; //Packet will be sent, go back to idle state ready for next command from host + } + break; + case RESET_DEVICE: + ResetDeviceCleanly(); + //break; //no need, commented to save space + default: + //Should never hit the default + BootState = IDLE; + }//End switch + }//End of else of if(BootState == IDLE) + +} + +//Should be called once, only after the regular erase/program/verify sequence +//has completed successfully. This function will program the magic +//APP_SIGNATURE_VALUE into the magic APP_SIGNATURE_ADDRESS in the application +//flash memory space. This is used on the next bootup to know that the the +//flash memory image of the application is intact, and can be executed. +//This is useful for recovery purposes, in the event that an unexpected +//failure occurs during the erase/program sequence (ex: power loss or user +//unplugging the USB cable). +void SignFlash(void) +{ + static unsigned char i; + static ROM uint8_t* pROM; + + //First read in the erase page contents of the page with the signature WORD + //in it, and temporarily store it in a RAM buffer. + pROM = (ROM uint8_t*)(APP_SIGNATURE_ADDRESS & ERASE_PAGE_ADDRESS_MASK); + for(i = 0; i < ERASE_PAGE_SIZE; i++) + { + ProgrammingBuffer[i] = *pROM++; + } + + //Now change the signature WORD value at the correct address in the RAM buffer + ProgrammingBuffer[(APP_SIGNATURE_ADDRESS & ~ERASE_PAGE_ADDRESS_MASK)] = (unsigned char)APP_SIGNATURE_VALUE; + ProgrammingBuffer[(APP_SIGNATURE_ADDRESS & ~ERASE_PAGE_ADDRESS_MASK) + 1] = (unsigned char)(APP_SIGNATURE_VALUE >> 8); + + //Now erase the flash memory block with the signature WORD in it + //TBLPTR = APP_SIGNATURE_ADDRESS & ERASE_PAGE_ADDRESS_MASK; + TBLPTRU = (uint8_t)((APP_SIGNATURE_ADDRESS & ERASE_PAGE_ADDRESS_MASK) >> 16); + TBLPTRH = (uint8_t)((APP_SIGNATURE_ADDRESS & ERASE_PAGE_ADDRESS_MASK) >> 8); + TBLPTRL = (uint8_t)(APP_SIGNATURE_ADDRESS & ERASE_PAGE_ADDRESS_MASK); + + EECON1 = 0x94; //Prepare for flash erase operation + UnlockAndActivate(CORRECT_UNLOCK_KEY); + + //Now re-program the values from the RAM buffer into the flash memory. Use + //reverse order, so we program the larger addresses first. This way, the + //write page with the flash signature word is the last page that gets + //programmed (assuming the flash signature resides on the lowest address + //write page, which is recommended, so that it becomes the first page + //erased, and the last page programmed). + pROM = (ROM uint8_t*)((APP_SIGNATURE_ADDRESS & ERASE_PAGE_ADDRESS_MASK) + ERASE_PAGE_SIZE - 1); //Point to last byte on the erase page + //TBLPTR = (APP_SIGNATURE_ADDRESS & ERASE_PAGE_ADDRESS_MASK) + ERASE_PAGE_SIZE - 1; //Point to last byte on the erase page + i = ERASE_PAGE_SIZE - 1; + while(1) + { + #ifdef __XC8__ + TBLPTRU = (uint32_t)pROM >> 16; + TBLPTRH = (uint16_t)pROM >> 8; + TBLPTRL = (uint8_t)pROM; + #else + TBLPTR = (uint24_t)pROM; + #endif + TABLAT = ProgrammingBuffer[i]; + + #ifdef __XC8__ + #asm + tblwt + #endasm + #else //must be C18 instead + _asm tblwt _endasm + #endif + + //Check if we are at a program write block size boundary + if((i % WRITE_BLOCK_SIZE) == 0) + { + //The write latches are full, time to program the block. + ClearWatchdog(); + EECON1 = 0xA4; //Write to flash on next WR = 1 operation + UnlockAndActivate(CORRECT_UNLOCK_KEY); + } + //Move ROM pointer back to next location + pROM--; + #ifdef __XC8__ + #asm + tblrdpostdec + #endasm + #else //must be C18 instead + _asm tblrdpostdec _endasm + #endif + + //Check if we are done writing all blocks + if(i == 0) + { + break; //Exit while loop + } + i--; + } +} + + +//Before resetting the microcontroller, we should shut down the USB module +//gracefully, to make sure the host correctly recognizes that we detached +//from the bus. Some USB hosts malfunction/fail to re-enumerate the device +//correctly if the USB device does not stay detached for a minimum amount of +//time before re-attaching to the USB bus. For reliable operation, the USB +//device should stay detached for as long as a human would require to unplug and +//reattach a USB device (ex: 100ms+), to ensure the USB host software has a +//chance to process the detach event and configure itself for a state ready for +//a new attachment event. +void ResetDeviceCleanly(void) +{ + //USBDisableWithLongDelay(); + Reset(); + Nop(); + Nop(); +} + + + + + +//Routine used to write data to the flash memory from the ProgrammingBuffer[]. +void WriteFlashBlock(void) //Use to write blocks of data to flash. +{ + static unsigned char i; + static unsigned char BytesTakenFromBuffer; + static unsigned char CorrectionFactor; + + #ifdef __XC8__ + static ROM uint8_t* pROM; + + pROM = (ROM uint8_t*)(ProgrammedPointer - BufferedDataIndex); + TBLPTRU = 0x00; + TBLPTRH = (uint8_t)((uint16_t)pROM >> 8); + TBLPTRL = (uint8_t)pROM; + #else + TBLPTR = (ProgrammedPointer - BufferedDataIndex); + #endif + + BytesTakenFromBuffer = 0; + + //Check the lower 5 bits of the TBLPTR to verify it is pointing to a 32 byte aligned block (5 LSb = 00000). + //If it isn't, need to somehow make it so before doing the actual loading of the programming latches. + //In order to maximize programming speed, the PC application meant to be used with this firmware will not send + //large blocks of 0xFF bytes. If the PC application + //detects a large block of unprogrammed space in the hex file (effectively = 0xFF), it will skip over that + //section and will not send it to the firmware. This works, because the firmware will have already done an + //erase on that section of memory when it received the ERASE_DEVICE command from the PC. Therefore, the section + //can be left unprogrammed (after an erase the flash ends up = 0xFF). + //This can result in a problem however, in that the next genuine non-0xFF section in the hex file may not start + //on a 32 byte aligned block boundary. This needs to be handled with care since the microcontroller can only + //program 32 byte blocks that are aligned with 32 byte boundaries. + //So, use the below code to avoid this potential issue. + + #if(WRITE_BLOCK_SIZE == 0x20) + CorrectionFactor = (TBLPTRL & 0b00011111); //Correctionfactor = number of bytes tblptr must go back to find the immediate preceeding 32 byte boundary + TBLPTRL &= 0b11100000; //Move the table pointer back to the immediately preceeding 32 byte boundary + #elif(WRITE_BLOCK_SIZE == 0x10) + CorrectionFactor = (TBLPTRL & 0b00001111); //Correctionfactor = number of bytes tblptr must go back to find the immediate preceeding 16 byte boundary + TBLPTRL &= 0b11110000; //Move the table pointer back to the immediately preceeding 16 byte boundary + #elif(WRITE_BLOCK_SIZE == 0x8) + CorrectionFactor = (TBLPTRL & 0b00000111); //Correctionfactor = number of bytes tblptr must go back to find the immediate preceeding 16 byte boundary + TBLPTRL &= 0b11111000; //Move the table pointer back to the immediately preceeding 16 byte boundary + #else + #warning "Double click this error message and fix this section for your microcontroller type." + #endif + + for(i = 0; i < WRITE_BLOCK_SIZE; i++) //Load the programming latches + { + if(CorrectionFactor == 0) + { + if(BufferedDataIndex != 0) //If the buffer isn't empty + { + TABLAT = ProgrammingBuffer[BytesTakenFromBuffer]; + #ifdef __XC8__ + #asm + tblwtpostinc + #endasm + #else //must be C18 instead + _asm tblwtpostinc _endasm + #endif + + BytesTakenFromBuffer++; + BufferedDataIndex--; //Used up a byte from the buffer. + } + else //No more data in buffer, need to write 0xFF to fill the rest of the programming latch locations + { + TABLAT = 0xFF; + #ifdef __XC8__ + #asm + tblwtpostinc + #endasm + #else //must be C18 instead + _asm tblwtpostinc _endasm + #endif + + + } + } + else + { + TABLAT = 0xFF; + #ifdef __XC8__ + #asm + tblwtpostinc + #endasm + #else //must be C18 instead + _asm tblwtpostinc _endasm + #endif + CorrectionFactor--; + } + } + + // TBLPTR--; //Need to make table pointer point to the region which will be programmed before initiating the programming operation + #ifdef __XC8__ + #asm + tblrdpostdec + #endasm + #else //must be C18 instead + _asm tblrdpostdec _endasm //Do this instead of TBLPTR--; since it takes less code space. + #endif + + EECON1 = 0b10100100; //flash programming mode + UnlockAndActivate(CORRECT_UNLOCK_KEY); + + //Now need to fix the ProgrammingBuffer[]. We may not have taken a full 32 bytes out of the buffer. In this case, + //the data is no longer justified correctly. + for(i = 0; i < BufferedDataIndex; i++) //Need to rejustify the remaining data to the "left" of the buffer (if there is any left) + { + ProgrammingBuffer[i] = ProgrammingBuffer[BytesTakenFromBuffer+i]; + } +} + + +void WriteConfigBits(void) //Also used to write the Device ID +{ + static unsigned char i; + + #ifdef __XC8__ + TBLPTRU = 0x30; + TBLPTRH = (uint8_t)((uint16_t)PacketFromPC.Address >> 8); + TBLPTRH = (uint8_t)(PacketFromPC.Address); + #else + TBLPTR = (uint24_t)PacketFromPC.Address; + #endif + + for(i = 0; i < PacketFromPC.Size; i++) + { + TABLAT = PacketFromPC.Data[i+(REQUEST_DATA_BLOCK_SIZE-PacketFromPC.Size)]; + #ifdef __XC8__ + #asm + tblwt + #endasm + #else //must be C18 instead + _asm tblwt _endasm + #endif + + EECON1 = 0b11000100; //Config bits programming mode + UnlockAndActivate(CORRECT_UNLOCK_KEY); + + #ifdef __XC8__ + #asm + tblrdpostinc + #endasm + #else //must be C18 instead + _asm tblrdpostinc _endasm + #endif + } +} + +#if defined(DEVICE_WITH_EEPROM) +void WriteEEPROM(void) +{ + static unsigned char i; + + for(i = 0; i < PacketFromPC.Size; i++) + { + EEADR = (((unsigned char)PacketFromPC.Address) + i); + EEDATA = PacketFromPC.Data[i+(REQUEST_DATA_BLOCK_SIZE-PacketFromPC.Size)]; + + EECON1 = 0b00000100; //EEPROM Write mode + UnlockAndActivate(CORRECT_UNLOCK_KEY); + } + +} +#endif + +//It is preferrable to only place this sequence in only one place in the flash memory. +//This reduces the probabilty of the code getting executed inadvertently by +//errant code. It is also recommended to enable BOR (in hardware) and/or add +//software checks to avoid microcontroller "overclocking". Always make sure +//to obey the voltage versus frequency graph in the datasheet, even during +//momentary events (such as the power up and power down ramp of the microcontroller). +void UnlockAndActivate(unsigned char UnlockKey) +{ + INTCONbits.GIE = 0; //Make certain interrupts disabled for unlock process. + + //Check to make sure the caller really was trying to call this function. + //If they were, they should always pass us the CORRECT_UNLOCK_KEY. + if(UnlockKey != CORRECT_UNLOCK_KEY) + { + //Warning! Errant code execution detected. Somehow this + //UnlockAndActivate() function got called by someone that wasn't trying + //to actually perform an NVM erase or write. This could happen due to + //microcontroller overclocking (or undervolting for an otherwise allowed + //CPU frequency), or due to buggy code (ex: incorrect use of function + //pointers, etc.). In either case, we should execute some fail safe + //code here to prevent corruption of the NVM contents. + OSCCON = 0x03; //Switch to INTOSC at low frequency + while(1) + { + Sleep(); + } + Reset(); + } + + #ifdef __XC8__ + EECON2 = 0x55; + EECON2 = 0xAA; + EECON1bits.WR = 1; + #else + _asm + //Now unlock sequence to set WR (make sure interrupts are disabled before executing this) + MOVLW 0x55 + MOVWF EECON2, 0 + MOVLW 0xAA + MOVWF EECON2, 0 + BSF EECON1, 1, 0 //Performs write by setting WR bit + _endasm + #endif + + while(EECON1bits.WR); //Wait until complete (relevant when programming EEPROM, not important when programming flash since processor stalls during flash program) + EECON1bits.WREN = 0; //Good practice now to clear the WREN bit, as further protection against any accidental activation of self write/erase operations. +} + + +//Note: The ClrWdt() and "_asm tblrdpostinc _endasm" are inline assembly language +//instructions. The ClearWatchdog() and TableReadPostIncrement() functions are +//theoretically extraneous, since the operations being accomplished could be +//done without calling them as separate functions. However, when using inline +//assembly language, the C18 compiler normally doesn't know what the code will +//actually do (ex: will it modify STATUS reg, WREG, BSR contents??). As a +//result, it is potentially dangerous for the C compiler to make assumptions, +//that might turn out not to be correct. Therefore, the C18 compiler disables +//the compiler optimizations for a function, when one or more inline asm +//instructions are located within the C function. Therefore, to promote best +//code size optimizations from the C18 compiler, it is best to locate inline +//assembly sequences in their own separate C functions, that do not contain much +//other code (which could otherwise be optimized by the C compiler). This often +//results in the smallest code size, and is the reason it is being done here. +void TableReadPostIncrement(void) +{ + #ifdef __XC8__ + #asm + tblrdpostinc + #endasm + #else //must be C18 instead + _asm tblrdpostinc _endasm + #endif +} + diff --git a/bootloader/src/bootloader.h b/bootloader/src/bootloader.h new file mode 100644 index 0000000..2ffab5b --- /dev/null +++ b/bootloader/src/bootloader.h @@ -0,0 +1,47 @@ +/************************************************************************* + * Copyright (C) 2019 by Justin Byers + * + * This file is part of clubdance_v2. + * + * clubdance_v2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * clubdance_v2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with clubdance_v2. If not, see . + *************************************************************************/ + +/** + * @file bootloader.h + * @author Justin Byers + * @date August 11, 2019 + * @brief + * + */ +#ifndef BOOTLOADER_ +#define BOOTLOADER_ + +void UserInit(void); +void ProcessIO(void); +void ClearWatchdog(void); +void DisableUSBandExecuteLongDelay(void); + + +//Vector remapping/absolute address constants +#define REMAPPED_APPLICATION_RESET_VECTOR 0x1400 +//#define REMAPPED_APPLICATION_HIGH_ISR_VECTOR 0x1408 //See VectorRemap.asm +//#define REMAPPED_APPLICATION_LOW_ISR_VECTOR 0x1418 //See VectorRemap.asm +#define BOOTLOADER_ABSOLUTE_ENTRY_ADDRESS 0x001C //Execute a "goto 0x001C" inline assembly instruction, if you want to enter the bootloader mode from the application via software + +#define APP_SIGNATURE_ADDRESS 0x1406 //0x1006 and 0x1007 contains the "signature" WORD, indicating successful erase/program/verify operation +#define APP_SIGNATURE_VALUE 0x600D //leet "GOOD", implying that the erase/program was a success and the bootloader intentionally programmed the APP_SIGNATURE_ADDRESS with this value +#define APP_VERSION_ADDRESS 0x1416 //0x1016 and 0x1017 should contain the application image firmware version number + +#endif /* BOOTLOADER_ */ + diff --git a/bootloader/src/main.c b/bootloader/src/main.c index 917235e..ce431db 100644 --- a/bootloader/src/main.c +++ b/bootloader/src/main.c @@ -7,8 +7,11 @@ #include #include "usb.h" +#include "bootloader.h" void main(void) { + UserInit(); + // initialize the USB framework USBDeviceInit(); USBDeviceAttach(); @@ -22,6 +25,6 @@ void main(void) { continue; // run application specific tasks - //DANCEPAD_Tasks(); + ProcessIO(); } } diff --git a/bootloader/src/typedefs.h b/bootloader/src/typedefs.h new file mode 100644 index 0000000..655682c --- /dev/null +++ b/bootloader/src/typedefs.h @@ -0,0 +1,426 @@ +/******************************************************************************* +Copyright 2016 Microchip Technology Inc. (www.microchip.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +To request to license the code under the MLA license (www.microchip.com/mla_license), +please contact mla_licensing@microchip.com +*******************************************************************************/ + +#ifndef __CUSTOMIZED_TYPE_DEFS_H_ +#define __CUSTOMIZED_TYPE_DEFS_H_ + +#include +#define ROM const +#define rom +#include +#include +#ifndef Nop() +#define Nop() {asm("NOP");} +#endif +#ifndef ClrWdt() +#define ClrWdt() {asm("CLRWDT");} +#endif +#ifndef Reset() +#define Reset() {asm("RESET");} +#endif +#ifndef Sleep() +#define Sleep() {asm("SLEEP");} +#endif + +#define __EXTENSION + +#if !defined(__PACKED) + #define __PACKED +#endif + +/* get compiler defined type definitions (NULL, size_t, etc) */ +#include +#include +#include +#include + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define PUBLIC /* Function attributes */ +#define PROTECTED +#define PRIVATE static + +/* INT is processor specific in length may vary in size */ +typedef signed int INT; +typedef signed char INT8; +typedef signed short int INT16; +typedef signed long int INT32; + +/* UINT is processor specific in length may vary in size */ +typedef unsigned int UINT; +typedef unsigned char UINT8; +typedef unsigned short int UINT16; +typedef unsigned long int UINT32; /* other name for 32-bit integer */ + + +typedef union +{ + UINT8 Val; + struct + { + __EXTENSION UINT8 b0:1; + __EXTENSION UINT8 b1:1; + __EXTENSION UINT8 b2:1; + __EXTENSION UINT8 b3:1; + __EXTENSION UINT8 b4:1; + __EXTENSION UINT8 b5:1; + __EXTENSION UINT8 b6:1; + __EXTENSION UINT8 b7:1; + } bits; +} UINT8_VAL, UINT8_BITS; + +typedef union +{ + UINT16 Val; + UINT8 v[2] __PACKED; + struct __PACKED + { + UINT8 LB; + UINT8 HB; + } byte; + struct __PACKED + { + __EXTENSION UINT8 b0:1; + __EXTENSION UINT8 b1:1; + __EXTENSION UINT8 b2:1; + __EXTENSION UINT8 b3:1; + __EXTENSION UINT8 b4:1; + __EXTENSION UINT8 b5:1; + __EXTENSION UINT8 b6:1; + __EXTENSION UINT8 b7:1; + __EXTENSION UINT8 b8:1; + __EXTENSION UINT8 b9:1; + __EXTENSION UINT8 b10:1; + __EXTENSION UINT8 b11:1; + __EXTENSION UINT8 b12:1; + __EXTENSION UINT8 b13:1; + __EXTENSION UINT8 b14:1; + __EXTENSION UINT8 b15:1; + } bits; +} UINT16_VAL, UINT16_BITS; + +typedef union +{ + UINT32 Val; + UINT16 w[2] __PACKED; + UINT8 v[4] __PACKED; + struct __PACKED + { + UINT16 LW; + UINT16 HW; + } word; + struct __PACKED + { + UINT8 LB; + UINT8 HB; + UINT8 UB; + UINT8 MB; + } byte; + struct __PACKED + { + UINT16_VAL low; + UINT16_VAL high; + }wordUnion; + struct __PACKED + { + __EXTENSION UINT8 b0:1; + __EXTENSION UINT8 b1:1; + __EXTENSION UINT8 b2:1; + __EXTENSION UINT8 b3:1; + __EXTENSION UINT8 b4:1; + __EXTENSION UINT8 b5:1; + __EXTENSION UINT8 b6:1; + __EXTENSION UINT8 b7:1; + __EXTENSION UINT8 b8:1; + __EXTENSION UINT8 b9:1; + __EXTENSION UINT8 b10:1; + __EXTENSION UINT8 b11:1; + __EXTENSION UINT8 b12:1; + __EXTENSION UINT8 b13:1; + __EXTENSION UINT8 b14:1; + __EXTENSION UINT8 b15:1; + __EXTENSION UINT8 b16:1; + __EXTENSION UINT8 b17:1; + __EXTENSION UINT8 b18:1; + __EXTENSION UINT8 b19:1; + __EXTENSION UINT8 b20:1; + __EXTENSION UINT8 b21:1; + __EXTENSION UINT8 b22:1; + __EXTENSION UINT8 b23:1; + __EXTENSION UINT8 b24:1; + __EXTENSION UINT8 b25:1; + __EXTENSION UINT8 b26:1; + __EXTENSION UINT8 b27:1; + __EXTENSION UINT8 b28:1; + __EXTENSION UINT8 b29:1; + __EXTENSION UINT8 b30:1; + __EXTENSION UINT8 b31:1; + } bits; +} UINT32_VAL; + +/***********************************************************************************/ + +/* Alternate definitions */ +typedef void VOID; + +typedef char CHAR8; +typedef unsigned char UCHAR8; + +typedef unsigned char BYTE; /* 8-bit unsigned */ +typedef unsigned short int WORD; /* 16-bit unsigned */ +typedef unsigned long DWORD; /* 32-bit unsigned */ +//typedef unsigned long long QWORD; /* 64-bit unsigned */ +typedef signed char CHAR; /* 8-bit signed */ +typedef signed short int SHORT; /* 16-bit signed */ +typedef signed long LONG; /* 32-bit signed */ +//typedef signed long long LONGLONG; /* 64-bit signed */ +typedef union +{ + BYTE Val; + struct __PACKED + { + __EXTENSION BYTE b0:1; + __EXTENSION BYTE b1:1; + __EXTENSION BYTE b2:1; + __EXTENSION BYTE b3:1; + __EXTENSION BYTE b4:1; + __EXTENSION BYTE b5:1; + __EXTENSION BYTE b6:1; + __EXTENSION BYTE b7:1; + } bits; +} BYTE_VAL, BYTE_BITS; + +typedef union +{ + WORD Val; + BYTE v[2] __PACKED; + struct __PACKED + { + BYTE LB; + BYTE HB; + } byte; + struct __PACKED + { + __EXTENSION BYTE b0:1; + __EXTENSION BYTE b1:1; + __EXTENSION BYTE b2:1; + __EXTENSION BYTE b3:1; + __EXTENSION BYTE b4:1; + __EXTENSION BYTE b5:1; + __EXTENSION BYTE b6:1; + __EXTENSION BYTE b7:1; + __EXTENSION BYTE b8:1; + __EXTENSION BYTE b9:1; + __EXTENSION BYTE b10:1; + __EXTENSION BYTE b11:1; + __EXTENSION BYTE b12:1; + __EXTENSION BYTE b13:1; + __EXTENSION BYTE b14:1; + __EXTENSION BYTE b15:1; + } bits; +} WORD_VAL, WORD_BITS; + +typedef union +{ + DWORD Val; + WORD w[2] __PACKED; + BYTE v[4] __PACKED; + struct __PACKED + { + WORD LW; + WORD HW; + } word; + struct __PACKED + { + BYTE LB; + BYTE HB; + BYTE UB; + BYTE MB; + } byte; + struct __PACKED + { + WORD_VAL low; + WORD_VAL high; + }wordUnion; + struct __PACKED + { + __EXTENSION BYTE b0:1; + __EXTENSION BYTE b1:1; + __EXTENSION BYTE b2:1; + __EXTENSION BYTE b3:1; + __EXTENSION BYTE b4:1; + __EXTENSION BYTE b5:1; + __EXTENSION BYTE b6:1; + __EXTENSION BYTE b7:1; + __EXTENSION BYTE b8:1; + __EXTENSION BYTE b9:1; + __EXTENSION BYTE b10:1; + __EXTENSION BYTE b11:1; + __EXTENSION BYTE b12:1; + __EXTENSION BYTE b13:1; + __EXTENSION BYTE b14:1; + __EXTENSION BYTE b15:1; + __EXTENSION BYTE b16:1; + __EXTENSION BYTE b17:1; + __EXTENSION BYTE b18:1; + __EXTENSION BYTE b19:1; + __EXTENSION BYTE b20:1; + __EXTENSION BYTE b21:1; + __EXTENSION BYTE b22:1; + __EXTENSION BYTE b23:1; + __EXTENSION BYTE b24:1; + __EXTENSION BYTE b25:1; + __EXTENSION BYTE b26:1; + __EXTENSION BYTE b27:1; + __EXTENSION BYTE b28:1; + __EXTENSION BYTE b29:1; + __EXTENSION BYTE b30:1; + __EXTENSION BYTE b31:1; + } bits; +} DWORD_VAL; + +/* MPLAB C Compiler for PIC18 does not support 64-bit integers */ +/*typedef union +{ + QWORD Val; + DWORD d[2] __PACKED; + WORD w[4] __PACKED; + BYTE v[8] __PACKED; + struct __PACKED + { + DWORD LD; + DWORD HD; + } dword; + struct __PACKED + { + WORD LW; + WORD HW; + WORD UW; + WORD MW; + } word; + struct __PACKED + { + __EXTENSION BYTE b0:1; + __EXTENSION BYTE b1:1; + __EXTENSION BYTE b2:1; + __EXTENSION BYTE b3:1; + __EXTENSION BYTE b4:1; + __EXTENSION BYTE b5:1; + __EXTENSION BYTE b6:1; + __EXTENSION BYTE b7:1; + __EXTENSION BYTE b8:1; + __EXTENSION BYTE b9:1; + __EXTENSION BYTE b10:1; + __EXTENSION BYTE b11:1; + __EXTENSION BYTE b12:1; + __EXTENSION BYTE b13:1; + __EXTENSION BYTE b14:1; + __EXTENSION BYTE b15:1; + __EXTENSION BYTE b16:1; + __EXTENSION BYTE b17:1; + __EXTENSION BYTE b18:1; + __EXTENSION BYTE b19:1; + __EXTENSION BYTE b20:1; + __EXTENSION BYTE b21:1; + __EXTENSION BYTE b22:1; + __EXTENSION BYTE b23:1; + __EXTENSION BYTE b24:1; + __EXTENSION BYTE b25:1; + __EXTENSION BYTE b26:1; + __EXTENSION BYTE b27:1; + __EXTENSION BYTE b28:1; + __EXTENSION BYTE b29:1; + __EXTENSION BYTE b30:1; + __EXTENSION BYTE b31:1; + __EXTENSION BYTE b32:1; + __EXTENSION BYTE b33:1; + __EXTENSION BYTE b34:1; + __EXTENSION BYTE b35:1; + __EXTENSION BYTE b36:1; + __EXTENSION BYTE b37:1; + __EXTENSION BYTE b38:1; + __EXTENSION BYTE b39:1; + __EXTENSION BYTE b40:1; + __EXTENSION BYTE b41:1; + __EXTENSION BYTE b42:1; + __EXTENSION BYTE b43:1; + __EXTENSION BYTE b44:1; + __EXTENSION BYTE b45:1; + __EXTENSION BYTE b46:1; + __EXTENSION BYTE b47:1; + __EXTENSION BYTE b48:1; + __EXTENSION BYTE b49:1; + __EXTENSION BYTE b50:1; + __EXTENSION BYTE b51:1; + __EXTENSION BYTE b52:1; + __EXTENSION BYTE b53:1; + __EXTENSION BYTE b54:1; + __EXTENSION BYTE b55:1; + __EXTENSION BYTE b56:1; + __EXTENSION BYTE b57:1; + __EXTENSION BYTE b58:1; + __EXTENSION BYTE b59:1; + __EXTENSION BYTE b60:1; + __EXTENSION BYTE b61:1; + __EXTENSION BYTE b62:1; + __EXTENSION BYTE b63:1; + } bits; +} QWORD_VAL; +*/ +#undef __EXTENSION + +#ifndef uint24_t + #define uint24_t uint32_t +#endif + +typedef void(*pFunc)(void); +/* +typedef union _POINTER +{ + struct + { + BYTE bLow; + BYTE bHigh; + //BYTE bUpper; + }; + uint16_t _word; // bLow & bHigh + + //pFunc _pFunc; // Usage: ptr.pFunc(); Init: ptr.pFunc = &; + + BYTE* bRam; // Ram byte pointer: 2 bytes pointer pointing + // to 1 byte of data + uint16_t* wRam; // Ram word poitner: 2 bytes poitner pointing + // to 2 bytes of data + + ROM BYTE* bRom; // Size depends on compiler setting + ROM WORD* wRom; + //ROM near BYTE* nbRom; // Near = 2 bytes pointer + //ROM near uint16_t* nwRom; + //ROM far BYTE* fbRom; // Far = 3 bytes pointer + //ROM far uint16_t* fwRom; +} POINTER; +*/ + + +#endif /* __CUSTOMIZED_TYPE_DEFS_H_ */ diff --git a/bootloader/src/usb_config.h b/bootloader/src/usb_config.h index 9561b58..a9dde19 100644 --- a/bootloader/src/usb_config.h +++ b/bootloader/src/usb_config.h @@ -171,6 +171,9 @@ #define HID_NUM_OF_DSC 1 #define HID_RPT01_SIZE 29 +#define HID_IN_EP 0 +#define HID_OUT_EP 1 + /** DEFINITIONS ****************************************************/ #endif //USBCFG_H diff --git a/bootloader/src/usb_events.c b/bootloader/src/usb_events.c index 5554840..aa08319 100644 --- a/bootloader/src/usb_events.c +++ b/bootloader/src/usb_events.c @@ -1,5 +1,6 @@ #include "usb.h" #include "usb_device_hid.h" +#include "bootloader.h" /******************************************************************* * Function: bool USER_USB_CALLBACK_EVENT_HANDLER( @@ -53,7 +54,9 @@ bool USER_USB_CALLBACK_EVENT_HANDLER(USB_EVENT event, void *pdata, uint16_t size case EVENT_CONFIGURED: // When the device is configured, we should (re)initialize our state variables - USBEnableEndpoint(1, USB_IN_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP); + UserInit(); + USBEnableEndpoint(HID_IN_EP, USB_IN_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP); + USBEnableEndpoint(HID_OUT_EP, USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP); break; case EVENT_SET_DESCRIPTOR: