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: