PIC18F14K50 and FSEN (UCFG.2) with CWRITE strange behaviour

Started by trastikata, Aug 14, 2021, 10:29 PM

Previous topic - Next topic

trastikata

Hello all,

I was writing an USB bootloader for PIC18F14K50 and found an odd behaviour when the USB FSEN (UCFG.2) is enabled and data is written in the FLASH memory with insufficient delay between both.

Took me while to track the issue but this is how it behaves ... if UCFG.2 is set with insufficient delay after it, CWrite is not working. What makes it strange is that the CEraze, preceding the CWrite is actually working regardless the delay.

See the screenshots further down:


ERR.jpg

John Lawton

My understanding here is slim, but could that issue be due to the USB enumeration that takes place at program start which is operating under fast interrupt causing problems with your flash memory operations?

I'm interested to know how to get flash storage working, as I have a USB application with an 18F24J50 that has no EEPROM and I want a few bytes of non-volatile storage. So far I haven't managed to work out exactly how to do it.

For instance how do I work out the right address for the flash storage memory, (4096 in your example) and how (if) I should use the ORG command (4120 in your example)?

Does the technique in https://protoncompiler.com/index.php/topic,341.msg2126.html#msg2126 help here?

John

trastikata

Quote from: John Lawton on Sep 10, 2021, 04:06 PMcould that issue be due to the USB enumeration that takes place at program start which is operating under fast interrupt causing problems with your flash memory operations?

Hello John,

it is not enumerating at this point, thus it is not related. This was a test code to track the issue in a larger program and all the test code does is what you see there - enable the bit, erase the segment and write the EEPROM.

Anyway since I figured it out, it is no longer a problem, maybe I can report it to Microchip to test for Errata.

Quote from: John Lawton on Sep 10, 2021, 04:06 PMI'm interested to know how to get flash storage working, as I have a USB application with an 18F24J50 that has no EEPROM and I want a few bytes of non-volatile storage.

I've an example for you on exactly the same family devices, it does what you need - first I put some coefficients in the FLASH memory at compiling time and later if and when re-calibration is needed the firmware rewrites them.

Here is how it works:

I. First you need to locate in the datasheet:
- Program memory size (18Fx4J50 = 16k, 18Fx5J50 = 32k, 18Fx6J50 = 64k)
- Block write size (for this family it is either 2 or 64 bytes)
- Block erase size ((for this family it is 1024 bytes)
- Block erase and write times if you don't want to complicate your program with checking the registers if the erase and write cycles have finished. I don't remember where I've seen it but erase and write times are given as 5ms, because the program timing allows it, I give it 20ms delay.

II. Determine address locations
- Which data will be stored where - should be multiple of 2 (Ex. 2 bytes coefficient stored in 30786 and 30788 - I prefer using the upper sectors to avoid being overwritten if the program is too long - the assembler will complain if this is the case. However make sure you are not erasing fuse settings address)
- The 64 byte page write boundary for the corresponding data (Ex. for the above 30784)
- The 1024 byte block erase boundary for the corresponding data (Ex. for the above 30720)

III. At assembly time (if you wish so) put some coefficients at the required addresses
Device = 18F25J50
Declare Xtal = 12

Main:
' do something here

Org 30786   
    CData $11   'Coef.Byte0
Org 30788   
    CData $AA   'Coef.Byte1

IV. During execution time first we have to erase the entire 1024 byte block because if the byte where you are writing is not erased, you can't overwrite it. Before that, make sure that you are not erasing program code, thus the program does not reach the start erase address. I usually open the HEX file in MPLAB X IPE and see if the hex erase start address is after the last program code. P.s. erased addresses will be filled with FF hex values.
Device = 18F25J50
Declare Xtal = 12

Main:
' do something here

CErase 30720
    DelayMS 20

V. Last step is writing the new values at the corresponding addresses. Writes in the FLASH memory is done in blocks as mentioned earlier and has to be performed all at once. Thus in this case to initiate the write cycle you will have to send 64 bytes. You should be careful to place the bytes at the correct place in the array relative to the write start address. Ex. my 64 byte page write boundary is 30784 but the 2 bytes coefficient is stored in 30786 and 30788, so I have to place the new values in the 2nd and the 4th place of the array.
P.s. instead of arrays you can simply send 64 bytes from a loop(s) as I do to save RAM.
Device = 18F25J50
Declare Xtal = 12

Dim bArray[64] As Byte

Main:
' do something here

Clear bArray                    'clear the array i.e. fill with 0's
bArray[2] = $DD                 'Load some values in the 2nd place
bArray[4] = $CC                 'Load some values in the 4th place
CWrite 30784, [bArray]          'Send all 64 bytes to be written
DelayMS 20   

Hope this helps,
Trastikata

John Lawton

#3
Thanks very much for taking the time to write such a fullsome reply. I'm sure it will help me in the way I was looking for.

It all seems a lot harder than reading and writing to the EEPROM blocks of old. Does Microchip really think this is progress or maybe they don't care, I'd rather pay a few pence for the convenience of a decent bit of EEPROM memory - if I had the choice that is. :)

UPDATE: reading and writing to flash data storage is now working for me, many thanks, and I've learned a bit more about how things work in the process.  The tip of setting the EEPROM address to locate the data is great. As the CErase command still isn't documented in the manual, perhaps Les, it should be now that it is being used.  I'm not a fan of secret sauce language!
Oh by the way, the commands Declare EEPROM_Address = xxxx
Declare EEPROM_Size = yy

seem to be undocumented too.

tumbleweed

QuoteIt all seems a lot harder than reading and writing to the EEPROM blocks of old. Does Microchip really think this is progress or maybe they don't care

None of the devices in the old 18FxxJ series have internal EEPROM. I think it's related to the process they used for those (that part is over 10 years old now).

If you want something with USB that has internal EEPROM, try the 18F24K50. It has a lot more "traditional" arrangement (plus it's bound to be cheaper).

TimB


trastikata

Would you like the source to my 14k50 bootloader? I will need a small change to work with current compiler but it handles all the bootloading etc. It even has hacking safeguards built in.

If nothing else it may be interesting to take ideas from

trastikata

Quote from: TimB on Sep 11, 2021, 10:00 AMtrastikata

Would you like the source to my 14k50 bootloader? I will need a small change to work with current compiler but it handles all the bootloading etc. It even has hacking safeguards built in.

If nothing else it may be interesting to take ideas from

Thank you Tim,

I finished the bootloader for that particular device - PIC18F14K50 - a while ago, also with full bootloader code and fuse settings block protection - basically indestructible unless ICSP programmer is used, but I wouldn't mind seeing your code - always can pick-up new ideas, verify some concepts and improve what needs improvement.

All the lost time was due to the errata I think, mentioned in the OP - while everything should have worked, it didn't and took some extensive testing to track the problem in FSEN.

P.s. I think it is based on your original article posted on the old wiki, with some changes to fit my application and PC host FLASH application. Here's my code:

Device = 18F14K50

Declare Xtal = 48

Include "USB_Bootloader.inc"
'VID = 6017
'PID = 2099


Declare Auto_Variable_Bank_Cross = On
Declare Reminders = OFF
Declare FSR_CONTEXT_SAVE = On
Declare LABEL_BANK_RESETS = On
Declare Optimiser_Level = 3
Declare Dead_Code_Remove = On


On_Interrupt     GoTo USER_HIGH_INTERRUPT_VECTOR   ; RE-MAP INT VECTORS
On_Low_Interrupt GoTo USER_LOW_INTERRUPT_VECTOR    ; RE-MAP INT VECTORS

Config_Start
        CPUDIV = NOCLKDIV ;No CPU System Clock divide
        USBDIV = OFF ;USB clock comes directly from the OSC1/OSC2 oscillator block; no divide
        FOSC = HS ;HS oscillator
        PLLEN = On ;Oscillator multiplied by 4
        PCLKEN = On ;Primary clock enabled
        FCMEN = OFF ;Fail-Safe Clock Monitor disabled
        IESO = OFF ;Oscillator Switchover mode disabled
        PWRTEN = OFF ;PWRT disabled
        BOREN = On ;Brown-out Reset enabled and controlled by software (SBOREN is enabled)
        BORV = 19 ;VBOR set to 1.9 V nominal
        WDTEN = OFF ;WDT is controlled by SWDTEN bit of the WDTCON register
        WDTPS = 32768 ;1:32768
        HFOFST = OFF ;The system clock is held off until the HFINTOSC is stable.
        MCLRE = OFF ;RA3 input pin enabled; MCLR disabled
        STVREN = On ;Stack full/underflow will cause Reset
        LVP = OFF ;Single-Supply ICSP disabled
        BBSIZ = On ;2kW boot block size
        XINST = OFF ;Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
        Debug = OFF ;Background debugger disabled, RA0 and RA1 configured as general purpose I/O pins
        Cp0 = OFF ;Block 0 not code-protected
        CP1 = OFF ;Block 1 not code-protected
        CPB = OFF ;Boot block not code-protected
        CPD = OFF ;Data EEPROM not code-protected
        WRT0 = OFF ;Block 0 not write-protected
        WRT1 = OFF ;Block 1 not write-protected
        WRTC = On ;Configuration registers write-protected
        WRTB = On ;Boot block write-protected
        WRTD = OFF ;Data EEPROM not write-protected
        EBTR0 = OFF ;Block 0 not protected from table reads executed in other blocks
        EBTR1 = OFF ;Block 1 not protected from table reads executed in other blocks
        EBTRB = OFF ;Boot block not protected from table reads executed in other blocks
Config_End


Symbol MainCodeStart            = 4096                       ; Main code start address / Keep on a 64 byte boundry
Symbol EndOfCodeSpace           = 16383                         ; End of code space
Symbol FlashEraseSeg            = 64                          ; The size of memory bank erased in one go
Symbol FlashWriteBlock          = 16                            ; The memory size Flash write needs to do in one block
Symbol FlashClearedStatus       = 255                           ; Address in Flash of the current status of the eeprom if cleared etc
Symbol EraseProgram             = $01                           ; Erase memory ; Returns $55 via USB when done
Symbol WriteFlash               = $02
Symbol FinishWrite              = $03                           ; Finish the write ; Returns $55 via USB then and writes to eeprom to finish the write sequence
Symbol RunMainCode              = $04                           ; Run the main code above the bootloader                         


Dim bBuffer[16] As Byte
Dim x As Dword
Dim PromAddress As Dword
Dim tempPromAddress As Dword
Dim BootOk As Byte
Dim temp As Byte
Dim y As Word
Dim USBBuffer As String * 64


MainProgramLoop:
    Clear : DelayMS 100 : UCFG.2 = 1 : DelayMS 100
   
    BootOk = ERead FlashClearedStatus
   
    If BootOk = "W" Then                          ; Read in from eeprom the current write status
        GoTo USB_look_UP
    Else
        GoTo USB_Enumerate
    EndIf                                                 ; Check if were ok to run now

USB_look_UP:
    For x = 0 To 150000
        mUSBService()
        If tUSB_Attached = True Then
            DelayMS 1
            mUSBService()
            DelayMS 1
            mUSBService()
            GoTo USB_connected
        EndIf
        DelayUS 25
    Next

GoTo RunMain

USB_connected:
    For x = 0 To 150000
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                GoTo USB_connected_out
            EndIf
        EndIf
        DelayUS 25
    Next

GoTo RunMain

USB_connected_out:
' The FLASH program should reeive this string in order to enter into flash mode
    USBBuffer = RXReport
    If USBBuffer[0] = "F" Then
        If USBBuffer[1] = "L" Then
            If USBBuffer[2] = "A" Then
                If USBBuffer[3] = "S" Then
                    If USBBuffer[4] = "H" Then
                        GoSub USBOK_Responce
                        GoTo USBMode
                    EndIf
                EndIf
            EndIf
        EndIf
    EndIf

    GoTo RunMain


USB_Enumerate:
    While 1 = 1
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                GoTo USB_ForceConnected
            EndIf
        EndIf
    Wend

USB_ForceConnected:
' The FLASH program should reeive this string in order to enter into flash mode
    USBBuffer = RXReport
    If USBBuffer[0] = "F" Then
        If USBBuffer[1] = "L" Then
            If USBBuffer[2] = "A" Then
                If USBBuffer[3] = "S" Then
                    If USBBuffer[4] = "H" Then
                        GoSub USBOK_Responce
                        GoTo USBMode
                    EndIf
                EndIf
            EndIf
        EndIf
    EndIf
   
    GoTo USB_Enumerate

RunMain:
    UCON.3 = 0
    DelayMS 1000
    @ Call MainCodeStart                                            ; All is ok so run main code


USBMode:
    While 1 = 1
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                Select RXReport[0]
                Case EraseProgram
                    GoSub EraseProgramData
                    GoSub USBOK_Responce
                Case WriteFlash
                    GoSub WriteFlashData
                Case FinishWrite
                    GoSub FlashClearStatus
                    GoSub USBOK_Responce
                Case RunMainCode
                    GoSub USBOK_Responce
                    GoTo RunMain                               ; Run the main code above the Bootloader
                EndSelect
            EndIf
        EndIf
    Wend

GoTo USBMode

FlashClearStatus:
    EWrite FlashClearedStatus, ["W"]

    For x = 0 To 7
        mUSBService()
        DelayMS 1
    Next
Return

EraseProgramData:
    EWrite FlashClearedStatus, ["C"]

    For x = 0 To 7
        DelayMS 1
        mUSBService()
    Next

    PromAddress = MainCodeStart

    For PromAddress = MainCodeStart To (EndOfCodeSpace - FlashEraseSeg) Step FlashEraseSeg
        CErase PromAddress
        For x = 0 To 7
            DelayMS 1
            mUSBService()
        Next
    Next
Return


WriteFlashData:
    GoSub USBRequest
   
    While 1 = 1
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                Break
            EndIf
        EndIf
    Wend

    USBBuffer = RXReport
    PromAddress.Byte3 = 0
    PromAddress.Byte2 = 0
    PromAddress.Byte1 = USBBuffer[0]
    PromAddress.Byte0 = USBBuffer[1]
    tempPromAddress = PromAddress

    GoSub USBRequest
   
    While 1 = 1
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                Break
            EndIf
        EndIf
    Wend

    For x = 0 To 15
        USBBuffer[x] = RXReport[x]
    Next

    y = PromAddress
    CWrite y,[USBBuffer[0], USBBuffer[1], USBBuffer[2], USBBuffer[3], USBBuffer[4], USBBuffer[5], USBBuffer[6], USBBuffer[7], USBBuffer[8],_
              USBBuffer[9],  USBBuffer[10], USBBuffer[11], USBBuffer[12], USBBuffer[13], USBBuffer[14], USBBuffer[15]]

    For x = 0 To 7
        DelayMS 1
        mUSBService()
    Next
   
   
    For x = 0 To 15
        y = tempPromAddress
        temp = CRead y
        If temp <> USBBuffer[x] Then
            DelayMS 1
            mUSBService()
            GoTo SendBadResultandReturn
        EndIf
        tempPromAddress = tempPromAddress + 1
    Next

ResultOkExit:
    DelayMS 1
    mUSBService()
    GoSub USBOK_Responce
Return

SendBadResultandReturn:
    GoSub USBNotOK_Responce
Return


USBRequest:
    Clear TXReport
    TXReport = "187"
    HID_WriteReport()
Return

USBOK_Responce:
    Clear TXReport
    TXReport = "85"
    HID_WriteReport()
Return

USBNotOK_Responce:
    Clear TXReport
    TXReport = "170"
    HID_WriteReport()
Return

    'Bootloader revision
    Org 4090
    CData $01

    Org 4092
    CData $01

    Org 4094
    CData $00


    Org  MainCodeStart + $8
USER_HIGH_INTERRUPT_VECTOR:
    Org MainCodeStart + $18
USER_LOW_INTERRUPT_VECTOR:
   
   

TimB


Here is one version of the code

As it was for a logger there is a lot of code dealing with the data reading from a serial Sram and stuff like CRC's

The security prevents bootloading new code in unless you know the secret numbers to be randomly added to the main code.

   
   
    ;$define EnableSim
   
    ; Notes
   
    ; Only one clear before intitilasing and enumeration allowed!!!!!!
   
     
;=== Compiler related defines ==================================
   
    Device 18F14K50
   
    Create_Coff = On
                                 
    Xtal = 24

    Dead_Code_Remove = On 
    Optimiser_Level = 3
   

    ' descriptor file,
    USB_Descriptor "TriplogikDescMC.inc"
   
;=== interrupt based defines ===============================

    On_Interrupt     GoTo USER_HIGH_INTERRUPT_VECTOR   ; RE-MAP INT VECTORS
    On_Low_Interrupt GoTo USER_LOW_INTERRUPT_VECTOR    ; RE-MAP INT VECTORS
     
;=== Chip Fuse settings ============================================

Config_Start
   CPUDIV = CLKDIV2 ; CPU System Clock divided by 2
   USBDIV = OFF ; USB Clock comes directly from the OSC1/OSC2 oscillator block; no divide
   FOSC = HS ; HS oscillator
   PLLEN = On ; Oscillator multiplied by 4
   PCLKEN = OFF ; Primary clock disabled <<<<<<<<<<<<
   FCMEN = OFF ; Fail-Safe Clock Monitor disabled
   IESO = On ; Oscillator Switchover mode enabled     <<<<<<<<<<<<<<<<<<<<<
   PWRTEN = On ; PWRTEN enabled
   BOREN = On ; Brown-out Reset enabled and controlled by software (SBOREN is enabled)
   BORV = 27 ; VBOR set to 2.7 V nominal
   WDTEN = OFF ; WDT is controlled by SWDTEN bit of the WDTCON register
   MCLRE = OFF ; RE3 input pin enabled; MCLR disabled
   HFOFST = On ; HF-INTOSC starts clocking the CPU without waiting for the oscillator to stablize.
   STVREN = On ; Stack full/underflow will cause Reset
   LVP = OFF ; Low Voltage Programming disabled
   XINST = OFF ; Enhanced CPU disabled
   CP0 = OFF ; Block 0 not code-protected
   CP1 = OFF ; Block 1 not code-protected
   CPB = OFF ; Boot block not code-protected
   CPD = OFF ; Data EEPROM not code-protected
   WRT0 = OFF ; Block 0 not write-protected
   WRT1 = OFF ; Block 1 not write-protected
   WRTB = OFF ; Boot block not write-protected
   WRTC = OFF ; Configuration registers (300000-3000FFh) not write-protected
   WRTD = OFF ; Data EEPROM not write-protected
   EBTR0 = OFF ; Block 0 not protected from table reads executed in other blocks
   EBTR1 = OFF ; Block 1 not protected from table reads executed in other blocks
   EBTRB = OFF ; Boot block not protected from table reads executed in other blocks
Config_End



;=== SFR Alias's ===================================================

    Symbol C1IE = PIE2.6     ' Comparator C1 Interrupt Enable bit
   
    Symbol ADON = ADCON0.0     ' A/D ON bit
    Symbol NOT_DONE = ADCON0.1 ' A/D Conversion Status flag
    Symbol GO_DONE = ADCON0.1       ' A/D Conversion Enable
    Symbol CHS0 = ADCON0.2     ' Analog Channel Select bits
    Symbol CHS1 = ADCON0.3     ' Analog Channel Select bits
    Symbol CHS2 = ADCON0.4     ' Analog Channel Select bits
    Symbol CHS3 = ADCON0.5     ' Analog Channel Select bits
   
    Symbol NVCFG0 = ADCON1.0 ' Negative Voltage Reference select bit
    Symbol NVCFG1 = ADCON1.1 ' Negative Voltage Reference Select Bit
    Symbol PVCFG0 = ADCON1.2 ' Positive Voltage Reference select bit
    Symbol PVCFG1 = ADCON1.3 ' Positive Voltage Reference Select Bit
   
    Symbol ADCS0 = ADCON2.0 ' A/D Conversion Clock Select bits
    Symbol ADCS1 = ADCON2.1 ' A/D Conversion Clock Select bits
    Symbol ADCS2 = ADCON2.2 ' A/D Conversion Clock Select bits
    Symbol ACQT0 = ADCON2.3 ' A/D Acquisition Time Select bits
    Symbol ACQT1 = ADCON2.4 ' A/D Acquisition Time Select bits
    Symbol ACQT2 = ADCON2.5 ' A/D Acquisition Time Select bits
    Symbol ADFM = ADCON2.7  ' A/D Result Format Select bit

    Symbol C1ON = CM1CON0.7     ' Comparator C1 Enable bit
    Symbol C1OUT = CM1CON0.6    ' Comparator C1 Output bit
    Symbol C1OE = CM1CON0.5     ' Comparator C1 Output Enable bit
    Symbol C1POL = CM1CON0.4    ' Comparator C1 Output Polarity Select bit
    Symbol C1SP = CM1CON0.3     ' Comparator C1 Speed/Power Select bit
    Symbol C1R = CM1CON0.3      ' Comparator C1 Reference Select bit (non-inverting input)
    Symbol C1CH1 = CM1CON0.3    ' Comparator C1 Channel Select Bit
    Symbol C1CH0 = CM1CON0.3    ' Comparator C1 Channel Select Bit


    Symbol RABIP = INTCON2.0     ' RB Port Change Interrupt Priority bit
    Symbol TMR0IP = INTCON2.2   ' TMR0 Overflow Interrupt Priority bit
    Symbol INTEDG2 = INTCON2.4  ' External Interrupt2 Edge Select bit
    Symbol INTEDG1 = INTCON2.5  ' External Interrupt1 Edge Select bit
    Symbol INTEDG0 = INTCON2.6  ' External Interrupt0 Edge Select bit
    Symbol NOT_RABPU = INTCON2.7    ' PORTA And PORTB Pull-up Enable Bit
 
    Symbol WPUA0 = WPUA.0       ' PortA.0 Weak Pull-up Enable bit
    Symbol WPUA1 = WPUA.1       ' PortA.1 Weak Pull-up Enable bit
    Symbol WPUA3 = WPUA.3       ' PortA.2 Weak Pull-up Enable bit
    Symbol WPUA4 = WPUA.4       ' PortA.3 Weak Pull-up Enable bit
    Symbol WPUA5 = WPUA.5       ' PortA.4 Weak Pull-up Enable bit

    Symbol SBOREN = RCON.6      ' Software Brownout enable


    Symbol C1RSEL = CM2CON1.5   ' Comparator C1 Reference Select bit
                                '1 = FVR routed to C1VREF input
                                '0 = CVREF routed to C1VREF input

    Symbol FVR1EN = REFCON0.7   ' Fixed Voltage Reference 1 Enable Bit
                                ' 0 = FVR1 is disabled
                                ' 1 = FVR is enabled
    Symbol FVR1ST = REFCON0.6   ' Fixed Voltage Reference 1 Stable bit
                                ' 0 = FVR1 is not stable
                                ' 1 = FVR1 is stable
    Symbol FVR1ST1 = REFCON0.5  ' Fixed Voltage Reference 1 Voltage Select bits
    Symbol FVR1ST0 = REFCON0.4  ' Fixed Voltage Reference 1 Voltage Select bits
                                ' 00 = Reserved, do not use
                                ' 01 = 1.024V (x1)
                                ' 10 = 2.048V (x2)
                                ' 11 = 4.096V (x4)

    Symbol D1EN = REFCON1.7     ' DAC 1 Enable Bit
    Symbol D1LPS = REFCON1.6    ' DAC 1 Low-Power Voltage State Select Bit
    Symbol DAC1OE = REFCON1.5   ' DAC 1 Voltage Output Enable Bit
    Symbol D1PSS1 = REFCON1.3   ' DAC 1 Positive Source Select bits
    Symbol D1PSS0 = REFCON1.2   ' DAC 1 Positive Source Select bits
                                ' 00 = VDD
                                ' 01 = VREF+
                                ' 10 = FVR1 output
                                ' 11 = Reserved, do not use
    Symbol D1NSS = REFCON1.0    'DAC1 Negative Source Select bits
   
    Symbol ANS11 = ANSELH.3     ' RB5 Analog Select Control Bit
    Symbol ANS10 = ANSELH.2     ' RB4 Analog Select Control Bit
    Symbol ANS9 = ANSELH.1      ' RC7 Analog Select Control Bit
    Symbol ANS8 = ANSELH.0      ' RC6 Analog Select Control Bit

       
    Symbol ANS7 = ANSEL.7       ' RC3 Analog Select Control Bit
    Symbol ANS6 = ANSEL.6       ' RC2 Analog Select Control Bit
    Symbol ANS5 = ANSEL.5       ' RC1 Analog Select Control Bit
    Symbol ANS4 = ANSEL.4       ' RC0 Analog Select Control Bit
    Symbol ANS3 = ANSEL.3       ' RA4 Analog Select Control Bit
   
   
    Symbol GIE = INTCON.7       ' Global Interrupt Enable Bit
    Symbol PEIE = INTCON.6      ' Peripheral Interrupt Enable Bit
    Symbol TMR0IE = INTCON.5    ' TMR0 Overflow Interrupt Enable Bit
    Symbol INT0IE = INTCON.4    ' INT0 External Interrupt Enable Bit
    Symbol RABIE = INTCON.3     ' RA And RB Port Change Interrupt Enable Bit
    Symbol TMR0IF = INTCON.2    ' TMR0 Overflow Interrupt Flag Bit
    Symbol INT0IF = INTCON.1    ' INT0 External Interrupt Flag Bit
    Symbol RABIF =  INTCON.0    ' RA And RB Port Change Interrupt Flag Bit(1)
   
    Symbol RX9D = RCSTA.0  ' 9th bit of received data (Can be parity bit) (Usart 1)
    Symbol OERR = RCSTA.1  ' Overrun Error (Usart 1)
    Symbol FERR = RCSTA.2  ' Framing Error (Usart 1)
    Symbol ADDEN = RCSTA.3 ' Address Detect Enable (Usart 1)
    Symbol CREN = RCSTA.4  ' Continuous Receive Enable (Usart 1)
    Symbol SREN = RCSTA.5  ' Single Receive Enable (Usart 1)
    Symbol RX9 = RCSTA.6   ' 9-bit Receive Enable (Usart 1)
    Symbol SPEN = RCSTA.7  ' Serial Port Enable (Usart 1)

    Symbol TMR1IF = PIR1.0 ' TMR1 Overflow Interrupt Flag bit
    Symbol TMR2IF = PIR1.1 ' TMR2 to PR2 Match Interrupt Flag bit
    Symbol CCP1IF = PIR1.2 ' CCP1 Interrupt Flag bit
    Symbol SSPIF = PIR1.3  ' Master Synchronous Serial Port Interrupt Flag bit
    Symbol TXIF = PIR1.4   ' EUSART Transmit Interrupt Flag bit
    Symbol RCIF = PIR1.5   ' EUSART Receive Interrupt Flag bit
    Symbol ADIF = PIR1.6   ' A/D Converter Interrupt Flag bit
   
    Symbol TMR1ON = T1CON.0     ' Timer1 ON
    Symbol TMR1CS = T1CON.1     ' Timer1 Clock Source Select
    Symbol NOT_T1SYNC = T1CON.2 ' Timer1 External Clock Input Synchronization Control
    Symbol T1OSCEN = T1CON.3    ' Timer1 Oscillator Enable Control
    Symbol T1CKPS0 = T1CON.4    ' Timer1 Input Clock Prescale Select bits
    Symbol T1CKPS1 = T1CON.5    ' Timer1 Input Clock Prescale Select bits
    Symbol T1RUN = T1CON.6      ' Timer1 System Clock Status bit
    Symbol RD16 = T1CON.7       ' 16-bit Read/Write Mode Enable bit
   
    Symbol IPEN = RCON.7
   
    Symbol TMR1IP = IPR1.0 ' TMR1 Overflow Interrupt Priority bit
    Symbol TMR2IP = IPR1.1 ' TMR2 to PR2 Match Interrupt Priority bit
    Symbol CCP1IP = IPR1.2 ' CCP1 Interrupt Priority bit
    Symbol SSPIP = IPR1.3  ' Master Synchronous Serial Port Interrupt Priority bit
    Symbol TXIP = IPR1.4   ' EUSART Transmit Interrupt Priority bit
    Symbol RCIP = IPR1.5   ' EUSART Receive Interrupt Priority bit
    Symbol ADIP = IPR1.6   ' A/D Converter Interrupt Priority bit
   
    Symbol TMR1IE = PIE1.0   ' TMR1 Overflow Interrupt Enable bit
    Symbol TMR2IE = PIE1.1   ' TMR2 to PR2 Match Interrupt Enable bit
    Symbol CCP1IE = PIE1.2   ' CCP1 Interrupt Enable bit
    Symbol SSPIE = PIE1.3    ' Master Synchronous Serial Port Interrupt Enable bit
    Symbol TXIE = PIE1.4     ' EUSART Transmit Interrupt Enable bit
    Symbol RCIE = PIE1.5     ' EUSART Receive Interrupt Enable bit
    Symbol ADIE = PIE1.6     ' A/D Converter Interrupt Enable bit
    Symbol reserved = PIE1.7 ' Maintain this bit clear     

    Symbol IDLE_IF = UIR.4    ' Idle Detect Interrupt bit
    Symbol SUSPND_USB = UCON.1   ' Suspend USB bit
   
    Symbol TXEN = TXSTA.5  ' Tx enable USART 1

;*=============================================================*
;* Description  Edata defines                                  *
;*=============================================================*
                                                                    ;********** these edata statments may need to be removed from this code
    EData 0,0,0,0                                                   ' Next address
    EData 0,0                                                       ' Segment nos and erase required
    EData 0,0                                                       ' Flash Prog bootloader info (not used in this code)
    EData 0                                                         ' The flag that says its ok to write the serial no anything other than 0 prevents it
    EData 0
   
    Symbol EeNextLogAddress = 0                                     ' Address in eeprom of the next log address
   
    Symbol EeSflashEraseSegs = 4                                    ' Segments that need erasing
    Symbol EeSflashErasereq = 5                                     ' Sflash Needs erasing
    Symbol FlashClearedStatus   = 6                                 ; Address in Eeeprom of the current status of the eeprom if cleared etc
    Symbol FlashLoadedStatus = 7                                    ; Address in Eeeprom of the current status of the eeprom if a program was written yet       
    Symbol OKToWriteSerial = 8                                      ;
    Symbol EeLowPowerRequired = 9
    Symbol FreeEepromSpace = $1A                                    ' Pointers used in the password subs
    Symbol EndEepromSpace = $7E
    Symbol EepromPasswordStart = $30
    Symbol UserNameStart = $80                                      ; Pointer to were in eeprom the User name space is stored
   
   
;*=============================================================*
;* Description  General Hardware I/0 defines                   *
;*=============================================================*

    Dim VibInput As PORTC.0                                         ; The vibration sensor input pin
    Dim VibInputTris As TRISC.0
   
    Dim LowVint As PORTC.1                                          ; Low voltage comparitor input and a/d
    Dim LowVintTris As TRISC.1
   
    Dim LightSense As PORTC.2                                       ; Light level sensor input
    Dim LightSenseTris As TRISC.2
   
    Dim SounderSink As LATC.3                                       ; Low on this and the HPWM going to make the sounder sound
    Dim SounderSinkTris As TRISC.3
   
    Dim LedAndSounderDrive As LATC.5                                ; The Hpm channel for the led mosfet and the sounder driver
    Dim LedAndSounderDriveTris As TRISC.5
   
    Dim GpsEnable As LATC.6                                         ; Gps enable line to mosfet active low
    Dim GpsEnableTris As TRISC.6
       
    Dim GPSPowerOn As 0
    Dim GPSPowerOff As 1
   
    Dim WayPointLogButton As PORTA.3                                ; The way point button  (input only)
    Dim WayPointLogButtonTris As TRISA.3                               
   
    Dim ScapEnable As LATB.7                                        ; Super cap enable to mosfet active low ; Usart has to be disabled to control
   
    Dim LightSensePower As LATB.6                                   ; Power to the light sense chip
    Dim LightSensePowerTris As TRISB.6

    Dim RXPIN As LATB.5                                             ; Rx Pin
 
    Symbol SCapCntrl = LATB.7
   
    Symbol SCapTris = TRISB.7
    Symbol USBDPlus = PORTA.0
   
;*=============================================================*
;* Description  Sflash Pin Definitions                         *
;*=============================================================*


    Symbol M25PChipSelect = LATC.4                                  ; M25P128 Chip Select pin 
    Symbol M25PChipSelectTris = TRISC.4                             ; M25P128 Tris Chip Select pin
    Symbol SPIClockPin = LATB.6                                     ; SPI Clock pin
    Symbol SPIClockPinTris = TRISB.6                                ; SPI Clock pin

    Symbol SPIDoPin = LATC.7                                        ; SPI Data out pin
    Symbol SPIDoPinTris = TRISC.7                                   ; SPI Tris Data out
   
    Symbol SPIDiPin = PORTB.4                                       ; SPI Data in pin
    Symbol SPIDiPinTris = TRISB.4                                   ; SPI Tris Data in 
   
   


;=== Timer based defines ===============================

    Dim TIMER1REG As TMR1L.Word
   
    Symbol FUDGE_FACTOR = 5
    Symbol TMR1_VAL =((65536)-(XTAL*2500))+FUDGE_FACTOR ' CALCULATE OSC OFFSET VALUES FOR 100HZ INTERRUPT
   
;===============================================================

    Dim FSR_0 As FSR0L.Word
    Dim FSR_1 As FSR1L.Word
    Dim FSR_2 As FSR2L.Word
    Dim MemoryLatPointer As TBLPTRL.Word
   
;======= M25P128 Instructions ==================================

    Symbol M25P_WREN = $06               ; Write Enable
    Symbol M25P_WRDI = $04               ; Write Disable
    Symbol M25P_RDID = $9F               ; Read Identification
    Symbol M25P_RDSR = $05               ; Read STATUS Register
    Symbol M25P_WRSR = $01               ; Write STATUS Register
    Symbol M25P_Read = $03               ; Read Data Bytes
    Symbol M25P_FAST_READ = $0B          ; Read Data Bytes At Higher Speed
    Symbol M25P_PP = $02                 ; PAGE Program
    Symbol M25P_SE = $D8                 ; Sector Erase
    Symbol M25P_BE = $C7                 ; Bulk Erase
   
; Variables used with commands to the M25P128

    Dim M25PDataAddress As Dword                                ' The data address Variable
    Dim M25PIndex As Byte                                       ' Index Counter
    Dim M25PTempB As Byte                                       ' Temp Byte var
    Dim MP25TempD As Dword                                      ' Temp Dword var
    Dim SPITemp As Byte                                         ' Temp SPI var
    Dim M25PStatus As Byte
    Dim SRWD As M25PStatus.7                                    ; Status Register Write Disable
    Dim BP2 As M25PStatus.4                                     ; The Block Protect (BP2, BP1, BP0) bits
    Dim BP1 As M25PStatus.3
    Dim BP0 As M25PStatus.2
    Dim WEL As M25PStatus.1                                     ; The Write Enable Latch
    Dim WIP As M25PStatus.0                                     ; The Write In Progress
   
    ' General Variables
   
    Dim Index1 As Byte
    Dim Index2 As Byte
    Dim PromAddress As Word
    Dim TempB1 As Byte
    Dim TempB2 As Byte
    Dim TempB3 As Byte
    Dim TempW1 As Word
    Dim Eepromaddress As Byte
    Dim XorVal As Byte
    Dim OKToReadFlash As Byte
    Dim FSR0_Temp As Word
    Dim FSR2_Temp As Word
    Dim FSR0_Temp_2 As Word
    Dim FSR2_Temp_2 As Word

; Eeprom related variables

    Dim I2cAddressVar As Byte
    Dim I2cDataVar As Byte   
    Dim I2cReadVar As Byte

; Firmware related vars
   
    Dim BootOk As Bit
    Dim CodeSpaceWrittenThisSession As Bit

; Led and Sounder related info
   
    Dim Led_Status As Bit
   
    Dim DelayUSBValue As Byte
    Dim DelayCount As Byte
   
; Password related stuff
   
    Dim AccessGranted As Bit
   
    Dim PasswordString2[16] As Byte
    Dim PasswordString[16] As Byte At __USBIn_Buffer                ; Fixed address for the password string so it matches both this and the main code
     
   
    Symbol PassFailureDelay = 250                                   ; The delay value between accepting another attempt following a failed password
   
    Dim FlashArray[64] As Byte
   
    Dim FlashCleared As Bit                                         ; It is password related as you should not be able to do a read


; Main code Offset defines
   
    Symbol MainCodeStart            = $1600                        ; Main code start address
    Symbol EndOfCodeSpace           = $4000                         ; End of code space
    Symbol FlashEraseSeg            = 64
    Symbol FlashWriteBlock          = 16

    Symbol ReturnPasswordSub2 = MainCodeStart - $12
;=== USB Defines ===============================================

    Symbol SerialNoSize             = 16                         ; the no of bytes in the usb serialno

   
    Dim Led_Timer As Byte
    Dim Led_OnTimer As Byte
    Symbol Led_FlashReloadVal = 70
   
; Important vars to keep in the same bank

    Dim TempD1 As Dword
    Dim SflashIndex As Word
    Dim LocalIndex As Byte
    Dim SflashFrom As Dword
    Dim SflashDataCnt As Word
    Dim USBCrc As Byte
    Dim SpiIndex As Byte


; Validation control variables
    Dim ValidationWord As Bit
    Dim ValidationCount As Byte
    Dim ValidationValue As Word
    Dim ValidationValueReqd As Word
    Dim ValidationLookupAddress As Word
    Symbol ValidationCountNumber = 8
       
;***************************************************************************
;   Command List
;   Below is the list of commands available and info on them
;
;
   
    Symbol CommandBytes             = 64                        ; The number of command bytes always expected
   
    Symbol ReturnFirmWareVerNo      = 1                         ; Return the firmware number
    ; Bytes expected 1 (command)   
    ; returns 2 bytes Low byte first  x.y Were the lower byte is y
   
    Symbol ReturnBootloaderVerNo    = 2                         ; Return the firmware number
    ; Bytes expected 1 (command)   
    ; returns 2 bytes Low byte first  x.y Were the lower byte is y   
       
    Symbol ReturnAddressOfNextLog   = 3                         ; Return the addrerss of the next location log in Flash Ram
    ; Bytes expected 1 (command)   
    ; Returns the address of the next location in FlashRam (read the eeprom for this)
    ; Data is 4 bytes in size althogh only the bottom 3 bytes are valid LSB first
   
    Symbol SendDataFromSFlash  = 4                     ; Return the data from address1 to Address address1 + Bytes wanted saved in Serial Flash Ram
    ; Bytes expected 7 (command) + Address1 + Bytes Wanted  (Address are LSB first) max no bytes is $ffff  LSB first
    ; Returns the data stored at address Address1 X no bytes

    Symbol SaveDataInEeprom             = 5                     ; Save the following data in Eeprom at Address x
    ; Bytes expected 3  (command) + Address (256bytes) + Data   
    ; Returns 1 byte $55 to indicate done
       
    Symbol SendDataFromEeprom           = 6                     ; Send the data stored in Eeprom at Address x
    ; Bytes expected 2 (command) Address   
    ; Returns 1 byte the data Read from Epprom the data at the address
   
    Symbol EraseSerialFlash          = 7                        ; Erase Serial Flash Segments
    ; Bytes expected 5 (command) + Address as a Dword   
    ; Returns 1 byte $55 to indicate Erase be performed on next power up
       
    Symbol IsSFlashEraseDone            = 8                     ; Report if Serial flash Erase is finished   
    ; Returns 1 byte:  $55 to indicate yes $AA to indicate not yet

   
'    Symbol WriteDataToSflash            = 10
'    ; Write the Following bytes to Sflash
'    ; First 4 bytes are the address to write to next is the number of bytes
'    ; max no of bytes is 59
   
'    Symbol IsFlashWriteDone             = 11
'    ; Report if Serial flash write is finished
'    ; Returns 1 byte:  $55 to indicate yes $AA to indicate not yet
   
    Symbol EnableGPS                    = 12
    ; Turn on the power to the GPS module
     
    Symbol DisableGps                   = 13
    ; Turn Off the power to the GPS module
   
'    Symbol ReturnSerNo                  = 14
'    ; Send the unique serno of the device
'    ; Data is a Dword and returns
'    ; Lsbyte first

    Symbol BulkEraseSerialFlash          = 15                    ; Erase All Serial Flash
    ; Bytes expected 1 (command)   
    ; Returns 1 byte $55 to indicate Erase Started 
   
    Symbol ReturnSflashStatusReg         = 16
    ; Bytes expected 1 (command)
    ; Reads and Returns 1 byte which is the status register
     
    Symbol WriteFlash               = $20                       ; Write Y bytes from Prom at X,X,X address Note next packet via USB has the data
    ; Bytes expected 5 (command) + bytes to write + 3 address bytes   
    ; returns nothing
   
    Symbol RunMainCode             = $21                        ; Run the main code above the bootloader
       
    Symbol FinishWrite             = $22                        ; Finish the write
    ; Bytes expected 1 (command)
    ; Returns $55 via USB then and writes to eeprom to finish the write sequence
   
    Symbol EraseProgram             = $23                       ; Erase memory
    ; Bytes expected 1 (command)
    ; Returns $55 via USB when done
   
    Symbol AccessID                  = $30                      ; The current password is being applied
    ; Bytes expected 17 (command)
    ; Command $30, Password (128 bits, 16 bytes)
    ; example $30,$00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$10,$11,$12,$13,$14,$15
   
    Symbol ChangePassword            = $31                      ; The current password is being applied
    ; Bytes expected 17 (command)
    ; Command $31, Password (128 bits, 16 bytes)
    ; example $31,$00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$10,$11,$12,$13,$14,$15
   
    Symbol ReturnUserName            = $32                      ; Return the current user name string
    ; Bytes expected 1 (command)
    ; Returns 16 chars that are the user name   
     
    Symbol WriteUserName             = $33                      ; write to eeprom a user name
    ; Bytes expected 17 (command + 16 chars)
    ; Command $33 User name (128 bits, 16 bytes) should be null terminated
    ; example $33,"J","o","e"," ","B","l","o","g","s",$00,$00,$00,$00,$00,$00,$00

    Symbol WriteNewUSBSerial         = $34                      ; Write to Prom a new USB Serial no
    ; Bytes expected 17 (command + 16 chars)
    ; Command $34 User name (16 bytes)
    ; example $34,"1","2","3","4","5","6","7","8","9","0","1","2","3","4","5","6" 
   
    Symbol RunBuildtests             = $40                      ; Peform the first built tests
    ; Bytes expected 1 (command)
    ; Returns $55 to indicate the test has started
    ; $F0 to say GPS talking
    ; $F1 to say Vibration sensor ok

    ' USB buffer...
    Symbol USBBufferSizeTX  = 64
    Symbol USBBufferSizeRX  = 64
   
    Dim USBInBuffer[USBBufferSizeRX] As Byte                ; this is the receiving buffer (pic side) aliased to the USB RAM space   
    Dim USBOutBuffer[USBBufferSizeTX] As Byte
   

               
    Dim USBCommandIn As Byte At USBInBuffer#0
    Dim SflashFromAddressAlias As Dword At USBInBuffer#1
    Dim SflashWriteByteCountAlias As Byte At USBInBuffer#5 
    Dim SflashDataCntAlias As Word At USBInBuffer#5
    Dim SflashWriteArrayStartAlias[1] As Byte At USBInBuffer#6     ' Make an array alias to the right section of the buffer

    Dim FirmWareVerAlias As Word At USBOutBuffer#0
    Dim ErrorsAlias As Byte At USBOutBuffer#0
    Dim LastLogAlias As Dword At USBOutBuffer#0
    Dim USBGeneralStatus As Byte At USBOutBuffer#0
    Dim USBOutDword As Dword At USBOutBuffer#0
    Dim EepromAddressAlias As USBInBuffer#1
    Dim EepromDataAlias As USBInBuffer#2
    Dim USbDataOut1 As Byte At USBOutBuffer#0
    Dim USBPasswordAlias As Byte At USBInBuffer#1

    Dim TempPasswordSpace[80] As Byte
   
    ' some useful flags...
    Dim PP0 As Byte System        ' USBPOLL status return
    Symbol CARRY_FLAG = STATUS.0  ' high if microcontroller does not have control over the buffer
    Symbol ATTACHED_STATE = 6     ' is USB attached
    Symbol TRNIF = UIR.3   ' low if USB Busy
   
       
;***************************************************************************
;   Command List
;   Below is the list of commands available and info on them
;
;
                       
       
    Dim StartAddressAlias As Dword At USBInBuffer#1 
    Dim ByteCountAlias As Byte At USBInBuffer#5 
   
     GoTo ProtonCodeStart
'; Macros =======================================================

    HWPMDisable Macro
        LedAndSounderDriveTris = 1                                  ; Disable the hpwm by makeing the drive pin an input
    Endm   

    HWPMEnable Macro
        LedAndSounderDriveTris = 0                                  ; Enable the HPWM by makeing the drive pin an outout
    Endm   


    LedOn Macro
        HWPMEnable                                                  ; Turn on the led by making the hpsm driver on
    Endm
   
    Ledoff Macro
        HWPMDisable                                                 ; Turn off the led by making the hpwm driver pin active
    Endm       
   
    SounderOn Macro
        HWPMEnable
        Low  SounderSink                                            ; Sounder on by enabling the HPWM and the Sounder sink Low Note the led will also come on
    Endm
   
    SounderOff Macro
        HWPMDisable                                                 ; Sounder off by HPWM being disabled and the sink set as an input
        SounderSinkTris = 1                                         ; !!!!!!!!!!!!NOTE!!!!!!!!!!!!! Led will go off you will need to set it on again if required
    Endm
   
    GpsOn Macro
        Low GpsEnable                                               ; Enable the GPS by sending the Mfet line low
    Endm
   
    GpsOff Macro
        High GpsEnable                                              ; Disable the GPS by sending the Mfet line high
    Endm
   
    ScapOn Macro
        Low ScapEnable                                              ; Turns on the Supercap
    Endm
   
    ScapOff Macro
        High ScapEnable                                              ; Turns on the Supercap
    Endm
   
    IntsOff Macro
        GIE = 0
    Endm
   
    IntsOn Macro
        GIE = 1
    Endm
   
    LVDetectOn Macro       
        C1ON = 1
        WREG = CM1CON0                                              ; Read the reg to set it up for a mismatch
    Endm

    LVDetectOff Macro
        C1ON = 0
    Endm

    DelayUS_4 Macro
        GoSub DelayUS4
    Endm
;====================================================================
'#################### M25P128 MACROS ###########################

;===============================================================
; Enables write mode by sending the M25P_WREN message
;
;   Use M25PWriteEnable
;
;---------------------------------------------------------------

    M25PWriteEnable Macro
    GoSub M25PWriteEnable_Sub
    Endm

;===============================================================
; Disables write mode by sending the M25P_WRDI message
;
;   Use M25PWriteEnable
;
;---------------------------------------------------------------   
    M25PWriteDisable Macro
    GoSub M25PWriteDisable_Sub
    Endm   



    M25PBulkErase Macro
        GoSub M25PBulkEraseSub
    Endm


   
;===============================================================
;  One of the core commands to send data out via the SPI Port
;
;   Use SPI_Out data (as byte or 8bit num)
;
;---------------------------------------------------------------

    SPI_Out Macro  P1
    #if (Prm_Count > 1)
    #error "SPI_Out - Too many parameters"
    #else
    #if (Prm_Count < 1)
    #error "SPI_Out - Too few parameters"
    #else
    #if(Prm_1 != Byte) && (Prm_1 != Num8)
    #error "SPI_Out - SPIData(Param1) Should be a Byte variable or number"
    #endif
    #if (Prm_1 == Byte)
    BYTE_WREG P1
    #endif
    #if (Prm_1 == Num8)
    NUM_WREG P1
    #endif
    GoSub SPI_OutSub
    #endif
    #endif
    Endm

;===============================================================
;  One of the core commands to read data in via the SPI Port
;
;   Use byte_var = SPI_In
;
;---------------------------------------------------------------

    SPI_In Macro
    GoSub SPI_InSub
    #if (SPI_In_RETURN != 1)
    #error "SPI_In - Mandatory return parameter misssing"
    #else
    #if(Return_Type != Byte)
    #error "SPI_In - Return variable should be a Byte variable"
    #else
    #if (Return_Type == Byte)
    BYTE_BYTE SPITemp, Return_Var
    #endif
    #endif
    #endif
    Endm

;===============================================================
;  Read ID value of the chip
;
;   Use Dword_var = M25PReadId
;
;---------------------------------------------------------------
    M25PReadId Macro
    GoSub M25pReadIdSub
    #if (M25PReadId_RETURN != 1)
    #error "M25PReadId - Mandatory return parameter misssing"
    #else
    #if(Return_Type != Dword)
    #error "M25PReadId - Return variable should be a DWord variable"
    #else
    #if (Return_Type == Dword)
    DWORD_DWORD MP25TempD, Return_Var
    #endif
    #endif
    #endif
    Endm


;===============================================================
;  Read the status reg in the chip
;
;   Use M25PReadStatus
;   result is placed in M25PStatus
;   M25PStatus has a series of alias
;   SRWD = Status Register Write Disable
;   BP2 = The Block Protect (BP2, BP1, BP0) bits
;   BP1
;   BP0
;   WEL = The Write Enable Latch
;   WIP = The Write In Progress
;   Notes
;   The Chip is left protected so you can use
;   M25PReReadStatus to enable you to poll the chip to look for
;   the status of the write etc operations
;   recommended use
;
;   M25PReadStatus
;   While WIP = 1
;   M25PReReadStatus
;   Wend
;---------------------------------------------------------------

    M25PReadStatus Macro
    GoSub M25PReadStatusSub
    Endm

;===============================================================
;  Read again the status reg in the chip
;  Does not touch the CS pin and really just
;  Issues the command
;  M25PStatus = SPI_In
;  See above for example
;---------------------------------------------------------------
 
    M25PReReadStatus Macro
    GoSub M25PReReadStatusSub
    Endm   

;===============================================================
;  Write to the status reg in the chip
;
;   Use M25PWriteStatus value (byte or 8 bit num)
;
;---------------------------------------------------------------
   
    M25PWriteStatus Macro  P1
    #if (Prm_Count > 1)
    #error "M25PWriteStatus - Too many parameters"
    #else
    #if (Prm_Count < 1)
    #error "M25PWriteStatus - Too few parameters"
    #else
    #if(Prm_1 != Byte) && (Prm_1 != Num8)
    #error "M25PWriteStatus - StatusValue(Param1) Should be a Byte variable or number"
    #endif
    #if (Prm_1 == Byte)
    BYTE_BYTE P1, M25PTemp
    #endif
    #if (Prm_1 == Num8)
    NUM_BYTE P1, M25PTempB
    #endif
    GoSub M25PWriteStatusSub
    #endif
    #endif
    Endm   

;===============================================================
;  Enable the chip by pulling the CS line low
;
;---------------------------------------------------------------   
    M25PEnable Macro
    Clear M25PChipSelect                                        ; Enable the chip
    Endm
   
;===============================================================
;  Disable the chip by pulling the CS line low
;
;---------------------------------------------------------------   
    M25PDisable Macro
    Set M25PChipSelect                                          ; Disable the chip again
    Endm
   
   
    M25PReadByte Macro  P1
    #if (Prm_Count > 1)
    #error "M25PReadByte - Too many parameters"
    #else
    #if (Prm_Count < 1)
    #error "M25PReadByte - Too few parameters"
    #else
    #if(Prm_1 != Dword) && (Prm_1 != Num32) && (Prm_1 != Num16) && (Prm_1 != Num8)
    #error "M25PReadByte - DataAddress(Param1) Should be a DWord variable or number"
    #endif
    #if (Prm_1 == Dword)
    DWORD_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num32)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num16)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num8)
    NUM_DWORD P1, M25PDataAddress
    #endif
    GoSub M25PReadByteSub
    #endif
    #endif
    #if (M25PReadByte_RETURN != 1)
    #error "M25PReadByte - Mandatory return parameter misssing"
    #else
    #if(Return_Type != Byte)
    #error "M25PReadByte - Return variable should be a Byte variable"
    #else
    #if (Return_Type == Byte)
    BYTE_BYTE SPITemp, Return_Var
    #endif
    #endif
    #endif
    Endm


    M25PEraseSector Macro  P1
    #if (Prm_Count > 1)
    #error "M25PEraseSector - Too many parameters"
    #else
    #if (Prm_Count < 1)
    #error "M25PEraseSector - Too few parameters"
    #else
    #if(Prm_1 != Dword) && (Prm_1 != Num32) && (Prm_1 != Num16) && (Prm_1 != Num8)
    #error "M25PEraseSector - DataAddress(Param1) Should be a DWord variable or number"
    #endif
    #if (Prm_1 == Dword)
    DWORD_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num32)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num16)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num8)
    NUM_DWORD P1, M25PDataAddress
    #endif
    GoSub M25PEraseSectorSub
    #endif
    #endif
    Endm

    M25PReadBytes Macro  P1, P2, P3
    #if (Prm_Count > 3)
    #error "M25PReadBytes - Too many parameters"
    #else
    #if (Prm_Count < 3)
    #error "M25PReadBytes - Too few parameters"
    #else
    #if(Prm_1 != Dword) && (Prm_1 != Num32) && (Prm_1 != Num16) && (Prm_1 != Num8)
    #error "M25PReadBytes - DataAddress(Param1) Should be a DWord variable or number"
    #endif
    #if(Prm_2 != BYTE_ARRAY_PTR)
    #error "M25PReadBytes - DataDestination(Param2) Should be a BYTE array pointer"
    #endif
    #if(Prm_3 != Byte) && (Prm_3 != Num8)
    #error "M25PReadBytes - DataCount(Param3) Should be a Byte variable or number"
    #endif
    #if (Prm_1 == Dword)
    DWORD_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num32)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num16)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num8)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_2 == BYTE_ARRAY_PTR)
        NUM_FSR2 P2
    #endif
    #if (Prm_3 == Byte)
    BYTE_BYTE P3, M25PIndex
    #endif
    #if (Prm_3 == Num8)
    NUM_BYTE P3, M25PIndex
    #endif
    GoSub M25PReadBytesSub
    #endif
    #endif
    Endm   
   
M25PBulkEraseSub:

   

   
    M25PReadStatus                                              ; Check its not in the middle of a current write
    While WIP = 1
        M25PReReadStatus
    Wend
    M25PDisable                                                 ; Disable to ensure the read status is stopped
    DelayUS_4
    M25PEnable                                                  ; Enable the chip again
    M25PWriteEnable
    M25PEnable                                                  ; Enable the chip
    SPI_Out M25P_BE                                             ; Sent the Bulk erase instruction
    M25PDisable                                                 ; Disable the chip to start the erase 
    Return   


M25PEraseSectorSub:
    M25PReadStatus                                              ; Check its not in the middle of a current write
    While WIP = 1
    M25PReReadStatus
    Wend
    M25PDisable                                                 ; Disable to ensure the read status is stopped
    DelayUS_4
    M25PEnable                                                  ; Enable the chip again
    M25PWriteEnable
    M25PEnable                                                  ; Enable the chip
    SPI_Out M25P_SE                                             ; Sent the Sector erase instruction
    SPI_Out M25PDataAddress.Byte2                               ; Send the address
    SPI_Out M25PDataAddress.Byte1                               ; The whole sector that the address is in will be
    SPI_Out M25PDataAddress.Byte0                               ; Erased
    M25PDisable                                                 ; Disable the chip to start the erase
    Return

    M25StartRead Macro  P1
    #if (Prm_Count > 1)
    #error "M25StartRead - Too many parameters"
    #else
    #if (Prm_Count < 1)
    #error "M25StartRead - Too few parameters"
    #else
    #if(Prm_1 != Dword) && (Prm_1 != Num32) && (Prm_1 != Num16) && (Prm_1 != Num8)
    #error "M25StartRead - DataAddress(Param1) Should be a DWord variable or number"
    #endif
    #if (Prm_1 == Dword)
    DWORD_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num32)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num16)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num8)
    NUM_DWORD P1, M25PDataAddress
    #endif
    GoSub M25POpenReadSub
    #endif
    #endif
    Endm

    M25PCloseRead Macro
    Set M25PChipSelect
    Endm



SPI_OutSub:
    SPITemp = WREG       
    SpiIndex = 8
    Repeat
        If SPITemp.7 = 1 Then
            SPIDoPin = 1
        Else
            SPIDoPin = 0
        EndIf
        DelayUS 1
        SPIClockPin = 1
        DelayUS 1
        SPIClockPin = 0
        SPITemp = SPITemp << 1
        Dec SpiIndex
    Until SpiIndex = 0
    Return

SPI_InSub:   
    SpiIndex = 8
    Repeat
        SPITemp = SPITemp << 1
        SPITemp.0 = SPIDiPin   
        SPIClockPin = 1
        DelayUS 1       
        SPIClockPin = 0
        DelayUS 1       
        Dec SpiIndex
    Until SpiIndex = 0   
    Return
               
   
M25PWriteEnable_Sub:
    M25PEnable                                                  ; Enable the chip
    SPI_Out M25P_WREN                                           ; Send the WREN command
    M25PDisable                                                 ; Disable the chip again
    Return                                                      ; All done return
   
M25PWriteDisable_Sub:   
    M25PEnable                                                  ; Enable the chip
    SPI_Out M25P_WRDI                                           ; Send the WRDI command
    M25PDisable                                                 ; Disable the chip again
    Return 

   
M25pReadIdSub:   
    M25PEnable                                                  ; Enable the chip
    SPI_Out M25P_RDID                                           ; Send the RDID command
    MP25TempD.Byte0 = SPI_In                                    ; readin the 24 bit ID data
    MP25TempD.Byte1 = SPI_In
    MP25TempD.Byte2 = SPI_In           
    M25PDisable                                                 ; Disable the chip again
    Return
   
M25PReadStatusSub:
    M25PEnable                                                  ; Enable the chip
    SPI_Out M25P_RDSR                                           ; Send the RDSR command
    M25PStatus = SPI_In
    Return

M25PReReadStatusSub:
    M25PStatus = SPI_In
    Return

M25PWriteStatusSub:
    M25PWriteEnable
    M25PEnable                                                  ; Enable the chip
    SPI_Out M25PTempB
    M25PDisable                                                 ; Disable the chip again
    Return
   

M25PWriteByteSub:
    M25PReadStatus                                              ; Check its not in the middle of a current write
    While WIP = 0
    M25PReReadStatus
    Wend
    M25PDisable                                                 ; Disable to ensure the read status is stopped
    DelayUS_4
    M25PEnable                                                  ; Enable the chip again
    M25PWriteEnable                                             ; Write enable the device
    M25PEnable                                                  ; Enable the device
    SPI_Out M25P_PP                                             ; Sent the Page Write instruction
    SPI_Out M25PDataAddress.Byte2                               ; Send the address
    SPI_Out M25PDataAddress.Byte1
    SPI_Out M25PDataAddress.Byte0
    SPI_Out M25PTempB                                           ; Finaly the data
    M25PDisable                                                 ; Finish the write by bringing the CS line high
    Return
   
   
M25PWriteDataSub:
    M25PReadStatus                                              ; Check its not in the middle of a current write
    While WIP = 1
    M25PReReadStatus
    Wend
    M25PDisable                                                 ; Disable to ensure the read status is stopped
    DelayUS_4
    M25PEnable                                                  ; Enable the chip again
    M25PWriteEnable                                             ; Write enable the device
    M25PEnable                                                  ; Enable the device
    SPI_Out M25P_PP                                             ; Sent the Page Write instruction
    SPI_Out M25PDataAddress.Byte2                               ; Send the address
    SPI_Out M25PDataAddress.Byte1
    SPI_Out M25PDataAddress.Byte0
   
    If M25PIndex > 0 Then                                       ; Watch that there is more than 1 byte to write
        Repeat                                                  ; Via FRS0 send all the data requested
            SPI_Out POSTINC2
            Dec M25PIndex
        Until M25PIndex = 0
        M25PDisable
    EndIf                                                       ; Finish the write by bringing the CS line high
    Return       

M25PReadByteSub:
    M25PReadStatus                                              ; Check its not in the middle of a current write
    While WIP = 1
    M25PReReadStatus
    Wend
    M25PDisable                                                 ; Disable to ensure the read status is stopped
    DelayUS_4
    M25PEnable                                                  ; Enable the chip again   
    SPI_Out M25P_Read                                           ; Sent the data read instruction
    SPI_Out M25PDataAddress.Byte2                               ; Send the address
    SPI_Out M25PDataAddress.Byte1
    SPI_Out M25PDataAddress.Byte0
    WREG = SPI_In                                               ' Read the byte in
    M25PDisable                                                 
    Return
   
   
M25PReadBytesSub:
    M25PReadStatus                                              ; Check its not in the middle of a current write
    While WIP = 1
    M25PReReadStatus
    Wend
    M25PDisable                                                 ; Disable to ensure the read status is stopped
    DelayUS_4
    M25PEnable                                                  ; Enable the chip again   
    SPI_Out M25P_Read                                           ; Sent the data read instruction
    SPI_Out M25PDataAddress.Byte2                               ; Send the address
    SPI_Out M25PDataAddress.Byte1
    SPI_Out M25PDataAddress.Byte0
    Repeat
        POSTINC2 = SPI_In                                       ' Read the data in
        Dec M25PIndex
    Until M25PIndex = 0
    M25PDisable                                                 
    Return

M25POpenReadSub:
    M25PReadStatus                                              ; Check its not in the middle of a current write
    While WIP = 1
    M25PReReadStatus
    Wend
    M25PDisable                                                 ; Disable to ensure the read status is stopped
    DelayUS_4
    M25PEnable                                                  ; Enable the chip again 
    SPI_Out M25P_Read                                           ; Sent the data read instruction
    SPI_Out M25PDataAddress.Byte2                               ; Send the address
    SPI_Out M25PDataAddress.Byte1
    SPI_Out M25PDataAddress.Byte0
    Return


DelayUS4:
    DelayUS 4
    Return
;===============================================================
;  Write x bytes to memory

;  Use M25PWriteBytes P1, P2, P3
;  P1 = the address
;  P2 = the pointer to the array of data
;  P3 = the no of bytes to write
;---------------------------------------------------------------
   
    M25PWriteBytes Macro  P1, P2, P3
    #if (Prm_Count > 3)
    #error "M25PWriteBytes - Too many parameters"
    #else
    #if (Prm_Count < 3)
    #error "M25PWriteBytes - Too few parameters"
    #else
    #if(Prm_1 != Dword) && (Prm_1 != Num32) && (Prm_1 != Num16) && (Prm_1 != Num8)
    #error "M25PWriteBytes - M25PAddress(Param1) Should be a DWord variable or number"
    #endif
    #if(Prm_2 != BYTE_ARRAY_PTR)
    #error "M25PWriteBytes - Data(Param2) Should be a BYTE array pointer"
    #endif
    #if(Prm_3 != Byte) && (Prm_3 != Num8)
    #error "M25PWriteBytes - DataCount(Param3) Should be a Byte variable or number"
    #endif
    #if (Prm_1 == Dword)
    DWORD_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num32)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num16)
    NUM_DWORD P1, M25PDataAddress
    #endif
    #if (Prm_1 == Num8)
    NUM_DWORD P1, M25PDataAddress
    #endif                           
    #if (Prm_2 == BYTE_ARRAY_PTR)
        NUM_FSR2 P2
    #endif
    #if (Prm_3 == Byte)
    BYTE_BYTE P3, M25PIndex
    #endif
    #if (Prm_3 == Num8)
    NUM_BYTE P3, M25PIndex
    #endif
    GoSub M25PWriteDataSub
    #endif
    #endif
    Endm

    HRSOut "1"

;===============================================================

   
' ************************************************************
' * program starts here...                                   *
' ************************************************************

ProtonCodeStart:
; Here we decide if were running USB Download mode or GPS Logging
   
    OSCCON2.2 = 1                                                   ; Enable the Osc unit
    OSCCON = %01010000                                              ; ensure its on external osc
   
    Reset_Bank
   
    SBOREN = 1                                                      ; Enable Brownouts
    All_Digital = 1
    ANSELH = 0
    ANSEL =  %00001000
   
    TXEN = 0                                                        ; Disable the Usart that Proton enabled
       
    SCapTris = 1                                                    ; make sure the cap is off
    Clear

    IOCA = %00000011                                                ; Set enable interrupts on port A to get the read on porta.0/1 to work
   
    NOT_RABPU = 0                                                   ; Enable pullups for port a and b
    WPUA = %00001000                                                ; Enable it for RA3 the button input
    WPUB = 0                                                        ; No pullups on port b
           
    DelayMS 100                                                     ; A delay for all to settle

    SPIDiPinTris = 1                                                ; Make sure the data is an input
    SPIDoPinTris = 0                                                ; Make sure the data out is output
    M25PChipSelect = 1                                              ; Ensure the M25P chip select is high
    M25PChipSelectTris = 0                                          ; Makes sure the M25P Chip Select Pin is an output
    SPIClockPinTris = 0 
   
    Input WayPointLogButton   
   
    ; Add in the pullups for the vib sensor?/??
       
' ************************************************************
' * wait for USB interface to attach                         *
' ************************************************************

   GoSub IsBootOk                                                   ; Check if were ok to run now
   
   DelayMS 100                                         
   
   If USBDPlus = 0 Then GoTo USB_Enumerate
   If BootOk = 0 Then GoTo USB_Enumerate
   If WayPointLogButton = 0 Then GoTo USB_Enumerate
   
   GoTo RunMain
                         

USB_Enumerate:
    IOCA = %00000000                                                ; Turn of the interrupts on PortA.0/1 thus disabling the read on the pins

    While 1 = 1
        Repeat ' \
        USBPoll '   Wait for the USB interface to become attached
        Until PP0 = %00000110 ' /
        GoTo USBMode
    Wend

    GoTo USBMode

RunMain:
    @ GoTo MainCodeStart                                            ; All is ok so run main code
   
' ************************************************************
' * receive data from the USB bus                            *
' ************************************************************
DoUSBIn:
    Repeat
        GoSub SystemStatus
        USBIn 1, USBInBuffer, Auto
    Until STATUS.0 = 0
    Return
   
' ************************************************************
' * transmit Data                                            *
' ************************************************************
DoUSBOut: 

USBLoop:
    USBPoll
    If TRNIF = 1 Then GoTo USBLoop
DoUSBNow: 
    Repeat 
        GoSub SystemStatus
        USBOut 1, USBOutBuffer, USBBufferSizeTX                               
    Until STATUS.0 = 0     
 
   
; now do some flashing control

    If Led_Timer > 0 Then
        Dec Led_Timer
    Else
        Led_OnTimer = Led_FlashReloadVal                            ; Load this timer so the led comes on properly at the end of any sends
        Led_Timer = 100                                             ; ReLoad our timer so the Led flashs at the right period   
        If Led_Status = 1 Then
            LedOn
            Led_Status = 0
        Else
            Ledoff
            Led_Status = 1
        EndIf
    EndIf           
    Return

   
' ************************************************************
' * Do various checks on system here like USB still connected*
' ************************************************************
SystemStatus: 
   
    GoSub TMR_CONTROL
   
    If IDLE_IF = 1 Then                                              ; Turn off all current drawing hardware
        GpsEnable = GPSPowerOff         
        Ledoff
        ScapOff
        SUSPND_USB = 1                                              ; Shut down the USB
        @ Sleep                                                     ; and put the pic to sleep
    EndIf
     
    Return

' ************************************************************
'  Write the Data to Eeprom to indicate we have done at least
'  one write of code space

' ************************************************************
WriteEppromCodeWrite:

    EWrite FlashLoadedStatus, ["W"]

    Return
   
' ************************************************************
'  Write the Data From the usbbuffer inro flash ram

'  $20, address 4 bytes, payload

' ************************************************************   
   
WriteFlashData:
   
    If CodeSpaceWrittenThisSession = 0 Then
        GoSub WriteEppromCodeWrite 
        CodeSpaceWrittenThisSession = 1
    EndIf 
    PromAddress = StartAddressAlias
    Index2 = ByteCountAlias - 1
   
; If the address were being requested to write to is not in the correct place then
; we just return
   
    If PromAddress < MainCodeStart Then ResultOkExit
    If PromAddress > EndOfCodeSpace Then ResultOkExit

    ValidationWord = 0                                              ; We put this here so we can keep the word block going out of sequence

    GoSub ReadMemoryBlock                                           ; read the prom block into memory

    TempW1  = PromAddress & $3F                                     ; mask to leave the bottom 64 byte address range
   
    FSR_0 = VarPtr USBInBuffer#6                                    ; point to the incoming data
    FSR_2 = VarPtr FlashArray                                       ; Point to were it has to go
    FSR_2 = FSR_2 + TempW1                                          ; in the right place in the array
   
    GoSub ReadAndWriteToProm                                        ; Write that into the memory block and into Prom


   
' ************************************************************
'  Now Read the Data Back to confirm it was saved Ok

' ************************************************************
    PromAddress = StartAddressAlias
    Index2 = ByteCountAlias - 1
    FSR_0 = VarPtr USBInBuffer#6                                    ; point to the incoming data
   
    GoTo VerifyPromWrite                                            ; We can jump straight to this routine to do the final check and report the status etc
   
   

    ; end of WriteFlashData sub
   
;==============================================================



' ************************************************************
'  Read into Memory the requested Block of prom

' ************************************************************

; make a copy of the current 64 byte memory block
ReadMemoryBlock:
    TempW1  = PromAddress & $FFC0                                   ; Mask to the lower 64 byte boundery
    Index1 = 0
   
    Repeat
        FlashArray[Index1] = CRead TempW1
        Inc TempW1
        Inc Index1
    Until Index1 = 64 
    Return 

' ************************************************************
'  Write over that Memory block the required data
'  Then write that to Prom
' ************************************************************
ReadAndWriteToProm:
    Index1 = 0                                                      ; loop round until we have written all the data into our array
    Repeat
        TempB1 = POSTINC0                                           ; An extra routine designed to only let a valid prog be instaled
        POSTINC2 = TempB1                                           ; Sort out the incoming bytes into words
        If ValidationWord = 0 Then
            ValidationValue.Byte0 = TempB1
            ValidationWord = 1
        Else
            ValidationValue.Byte1 = TempB1   
            ValidationWord = 0
            GoSub VerifyValidDownLoad                               ; Every word check for the next value in the sequence
        EndIf       
        Inc Index1
    Until Index1 > Index2

WriteToPromOnly:
    FSR_2 = VarPtr FlashArray                                       ; Point to were it has to read from

     
    TBLPTRU = 0                                                     ; We never use this reg/address here but make sure its clear
    MemoryLatPointer = PromAddress & $FFC0                          ; Point to 64 byte boundary in memory were we want to load our data
   
    Dec MemoryLatPointer
    Index1 = 0
    Repeat
        Index2 = 0
        Repeat                                                      ; loop round until we have written all the data in from this block of memory
            TABLAT = POSTINC2
            TBLWT+*
            Inc Index1
            Inc Index2
        Until Index2 >= FlashWriteBlock 
        GoSub WriteToMem                                            ; Now write this block to memory     
    Until Index1 >= FlashEraseSeg                                   ; keep writting until the full 64 byte block of ram is written to Prom
   
    Return
       
' ************************************************************
'  Finish the write routine

' ************************************************************
FinishWriteSub:

; First we write to eeprom we have started the write/clear phase 
    If ValidationCount = ValidationCountNumber Then                     ' Check we have seen enough right values in the validation sequence
        EWrite FlashClearedStatus, ["W"]
    Else
        GoSub EraseProgramData
    EndIf
       
    Clear USBOutBuffer
    USBOutBuffer#0 = $55
    GoSub DoUSBOut
    DelayUSBValue = 20                                              ' Wait for the data to be sent before jumping back
   
    GoSub DelayUSB
    Return

' ************************************************************
'  Verify Write and exit routine

' ************************************************************
   
VerifyPromWrite:
    Index1 = 0
    Repeat                                                          ; Loop and test whats in memory with what is supposed to be there                               
     
        TempB1 = CRead PromAddress
        If TempB1 <> POSTINC0 Then                                  ; If the data is not what was expected
            USBOutBuffer#0 = $AA                                    ; Then send a fail result           
            GoTo SendResultandReturn
        EndIf
        Inc PromAddress
        Inc Index1
    Until Index1 >= Index2

; If we get here data was written Ok so say so and return

ResultOkExit:
    USBOutBuffer#0 = $55
   
SendResultandReturn:
    GoSub DoUSBOut
    Return 

; ************************************************************
;  Perform a memory write operation

; ************************************************************

WriteToMem:
    Asm
    bsf EECON1, EEPGD ; point to Flash program memory
    bcf EECON1, CFGS ; access Flash program memory
    bsf EECON1, WREN ; enable write to memory
    bcf INTCON, GIE ; disable interrupts
    movlw 55h
    movwf EECON2 ; write 55h
    movlw 0AAh
    movwf EECON2 ; write 0AAh
    bsf EECON1, WR ; start program (CPU stall)
    EndAsm
    Return

; ************************************************************
;  Write a new USB Serial No to Prom

; ************************************************************
WriteNewUSBSerialSub:
    TempB1 = ERead OKToWriteSerial
   
    If TempB1 <> 0 Then Return                                      ; Do nothing unless the eeprom place is clear
   
    Asm
    movlw USBSerialNo & 0xFF                                         ; We have to use asm to extract the serialno address as its not seen by Proton
    movff WREG, PromAddress
    movlw (USBSerialNo >> 8) & 0xFF
    movff WREG, PromAddressh
    EndAsm
   
    GoSub ReadMemoryBlock                                           ; read the prom block into memory
   
    CErase PromAddress                                              ; Now clear that section of memory

    Index1 = 1                                                      ; were reading from #1 in the usbinbuffer for the data
    Index2 = 0
    Repeat                                                         
        FlashArray[Index2] = USBInBuffer[Index1]
        Inc Index1
        Index2 = Index2 + 4                                         ; inc so we get the right place in memory for all those retlw's
    Until Index1 >= SerialNoSize + 1
     
;    Inc PromAddress                                                 ; we need to do this its deced in the sub due to other the other routine calling it         
    GoSub WriteToPromOnly                                           ; Write that into the memory block and into Prom
   
' ************************************************************
'  Now Read the Data Back to confirm it was saved Ok

' ************************************************************

    Asm
    movlw USBSerialNo & 0xFF                                         ; We have to use asm to extract the serialno address as its not seen by Proton
    movff WREG, PromAddress
    movlw (USBSerialNo >> 8) & 0xFF
    movff WREG, PromAddressh
    EndAsm

    Index2 = FlashEraseSeg                                          ; We wrote the entire block so its 64 bytes                                                 
    FSR_0 = VarPtr FlashArray                                       ; point to the array holding the data as a direct comparision with the data from the PC
                                                                    ; in usbinbuffer will be wrong
                                                                   
    GoTo VerifyPromWrite                                            ; We can jump straight to this routine to do the final check and report the status etc
   
   

    ; end of WriteNewUSBSub   
   
' ************************************************************
'  A check routine that has to see a list of values in a squence
'  be sent or the prog will be deleted
' ************************************************************
VerifyValidDownLoad:
    If ValidationCount < ValidationCountNumber Then
        If ValidationValue = ValidationValueReqd Then
            Inc ValidationCount
            ValidationLookupAddress = ValidationLookupAddress + 2
            ValidationValueReqd = CRead ValidationLookupAddress
        EndIf
    EndIf
    Return
   

'' ************************************************************
''  System check to see if the flash write went well
'' 
'' ************************************************************
IsBootOk:           
   
    TempW1 = ERead FlashClearedStatus                               ; Read in from eeprom the current write status
   
    If TempW1 = $5757 Then                                          ; if both clear and write are "W" then all is ok
        BootOk = 1
        Return
    EndIf
           
    BootOk = 0
    Return   
   
' ************************************************************
'  Erase all of program memory above the boot loader line

' ************************************************************   
EraseProgramData:

    TempB1 = FlashClearedStatus

; First we write to eeprom we have started the write/clear phase
   
    EWrite FlashClearedStatus, ["C"]
    DelayUSBValue = 5
    GoSub DelayUSB
    EWrite FlashClearedStatus + 1, ["C"]
   
    ; Now clear all of Flash above the bootloader
   
    PromAddress = MainCodeStart
   
    Repeat
        CErase PromAddress
        PromAddress = PromAddress + FlashEraseSeg
        USBPoll
    Until PromAddress >= EndOfCodeSpace
   
    FlashCleared = 1                                                ; Set this flag so you can read the prom

    ValidationCount = 0
    ValidationLookupAddress = ValidationDataTable                   ; Set up the lookup tables etc to validate the data being sent
    ValidationValueReqd = CRead ValidationLookupAddress
   
EraseExit:   
    Clear USBOutBuffer
    USBOutBuffer#0 = $55
    GoSub DoUSBOut
    Return
   

' ************************************************************
'  A simple safe method of delaying and keeping the usb connection live
'  ' It will be longer than the required delay
' ************************************************************

DelayUSB:
    DelayCount = 0
    Repeat
        USBPoll
        DelayMS 1
        Inc DelayCount
    Until DelayCount >= DelayUSBValue
    Return
       
' ************************************************************
' * Read the last log address from Epprom*
' ************************************************************   
ReadNextLogAddress:
    If OKToReadFlash = 0 Then
        TempD1 = ERead EeNextLogAddress
    Else
        TempD1 = 0
    EndIf                               
    Return
   
' ************************************************************
'  Read from Sflash and send via USB the data from SflashFrom
'  to SflashToo
' ************************************************************

SendSFlashData:
   
    USBCrc = 0
    SflashIndex = 0
    LocalIndex = 0 
    Dec SflashDataCnt
    M25StartRead SflashFrom                                         ; Start a read operation telling the Sflash were to start reading           
    GoSub SPI_InSub                                                 ; Read in data from the SFlash chip
    If OKToReadFlash <> 0 Then                                      ; Once Flash has been requested to be cleared then we will give nothing out
        SPITemp = $FF
    EndIf                                   
    USBCrc = USBCrc ^ SPITemp                                       ; Do the CRC on it
    USBOutBuffer[LocalIndex] = SPITemp                              ; Load in the first byte to the out buffer
    Inc SflashIndex
    Inc LocalIndex   
    While SflashIndex <= SflashDataCnt                              ; loop until we have collected the lot
        GoSub SPI_InSub                                             ; Read in data from the SFlash chip
        If OKToReadFlash <> 0 Then                                  ; Once Flash has been requested to be cleared then we will give nothing out
            SPITemp = $FF
        EndIf                               
        USBCrc = USBCrc ^ SPITemp                                   ; Do the CRC on it
        USBOutBuffer[LocalIndex] = SPITemp
        Inc SflashIndex
        Inc LocalIndex
        If LocalIndex >= USBBufferSizeTX  Then                      ; Make sure we only send 64 Bytes at a time
            GoSub DoUSBOut
            FSR0_Temp = FSR_0                                       ; make a copy of this FSR again
            LocalIndex = 0                           
        EndIf
    Wend

    M25PDisable                                                     ; Close Sflash 
    USBOutBuffer[LocalIndex] = USBCrc
    GoSub DoUSBOut
    Return

' ************************************************************
'  Save data sent send via USB into Eeprom

' ************************************************************
WriteDataToEeeprom:
    If EepromAddressAlias = FlashClearedStatus Then                ; Special code to prevent you manualy saying its ok to run the main code                                ; By preventing you erasing the flag and clearing the main code if you do
        GoSub EraseProgramData                                     ; If you try it will erase the main code
    EndIf
   
    EWrite EepromAddressAlias, [EepromDataAlias]
    USBGeneralStatus = $55
    GoSub DoUSBOut   
    Return
     
;///////////////////////////////////////////////////////////////


' ************************************************************
'  Erase a segment of serial flash

' ************************************************************
EraseFlashSub:

; First work out the number of segments to erase from the next log point

    GoSub ReadNextLogAddress
    TempB1 = TempD1 >> 18                                           ' Work out what segments need erasing
    EWrite EeSflashEraseSegs, [TempB1]                              ' Write that to eeprom
        DelayUSBValue = 5
    GoSub DelayUSB
    EWrite EeSflashErasereq, [$55]                                 ' Write to eeprom that erasing is required
    OKToReadFlash = $55                                             ' No reading of memory allowed now so we flag its empty when asked
    Return

'' ************************************************************
''  Check if the serial flash is busy erasing serial flash
'' 
'' ************************************************************
'IsaFlashEraseDOne:
'    M25PReadStatus
'    M25PDisable
'    If WIP = 1 Then
'        USBGeneralStatus = $AA
'    Else
'        USBGeneralStatus = $55
'    EndIf
'    GoSub DoUSBOut     
'    Return
                   
' ************************************************************
'  Read the firmware version from rom and send out via USB

' ************************************************************
ReadFirmWareNo:
    FirmWareVerAlias = CRead TempW1
    GoSub DoUSBOut
    Return

' ************************************************************
'  CopyOverPassword
'  Copies over the password from the temp string ontop of the USB in buffer
'  for the main routine to another one in this code
' ************************************************************

CopyOverPassword:
    Index1 = 0
    Repeat
        PasswordString2[Index1] = PasswordString[Index1]               ; Copy the contents over for the use in this code
        Inc Index1
    Until Index1 = 16
    Return
   
' ************************************************************
'  AccessIDSub
'  Compares the submitted pass word with the one
'  currently stored On the Device
'
' ************************************************************

AccessIDSub:
     
    GoSub ReadPasswordSub                                             ; read in the current password
       
    GoSub CopyOverPassword
       
    USBPoll
   
    FSR_0 = VarPtr PasswordString2                                   ; Set up the pointers
    FSR_2 = VarPtr USBPasswordAlias
   
    TempB1 = 0
    AccessGranted = 1
    Repeat                                                          ; Loop through the passwords to compare them
        If POSTINC0 <> POSTINC2 Then
            AccessGranted = 0
            Break   
        EndIf
        Inc TempB1
    Until TempB1 = 16

    If AccessGranted = 1 Then                                       
        USbDataOut1 = $55                                           ; The password matched
        GoSub DoUSBOut
    Else
        USbDataOut1 = $AA
        GoSub DoUSBOut                                              ; The password failed
        DelayCount = PassFailureDelay                               ; Delaybefore accepting any more commands
        GoSub DelayUSB
    EndIf       
         
    Return


' ************************************************************
'  ChangePasswordSub
'  Changes the password to access the Device
'
' ************************************************************   
   
ChangePasswordSub:
   
; first we need to scramble all free epprom to make it look harder to decifer
   
   ;
    Eepromaddress = 0
   
    Repeat
        TempW1 = TIMER1REG
        TempB2 = 0
        Repeat
            TempB3 = 0
            If TempW1.0 = 1 Then TempB3.2 = 1
            If TempW1.4 = 1 Then TempB3.0 = 1
            TempPasswordSpace[Eepromaddress] = TempB3         
            TempW1 = TempW1 >> 1
            Inc Eepromaddress
            Inc TempB2
            If Eepromaddress = (EndEepromSpace -FreeEepromSpace) Then Break
        Until TempB2 = 16
    Until Eepromaddress = (EndEepromSpace - FreeEepromSpace)

    FSR_0 = VarPtr PasswordString2                                   ; Set up the pointers
    FSR_2 = VarPtr USBPasswordAlias
   
    TempB1 = 0
    Repeat                                                          ; Copy the password from the usb buffer into the PasswordString
        POSTINC0 = POSTINC2
        Inc TempB1
    Until TempB1 = 16   
   
    GoSub GetXorVal
   

    Eepromaddress = 0
    XorVal = 123
    Repeat
        TempB1 = TempPasswordSpace[Eepromaddress]
        XorVal = XorVal + TempB1
        Inc Eepromaddress
    Until Eepromaddress = (EepromPasswordStart - FreeEepromSpace)
         
     
    TempW1 = Scramblelookup
    Eepromaddress = (EepromPasswordStart - FreeEepromSpace)
    Index1 = 0
    Repeat
        TempB1 = CRead TempW1
        TempB2 = PasswordString2[TempB1]
        TempB1 = 0
        TempB2 = TempB2 ^ XorVal
        Repeat
            TempB3 = 0
            If TempB2.0 = 1 Then TempB3.2 = 1
            If TempB2.4 = 1 Then TempB3.0 = 1
            TempPasswordSpace[Eepromaddress] = TempB3
            TempB2 = TempB2 >> 1
            Inc TempB1
            Inc Eepromaddress
        Until TempB1 = 4
        Inc TempW1
        Inc Index1
    Until Index1 = 16

    Eepromaddress = FreeEepromSpace
    TempB1 = 0
    Repeat
        TempB2 = TempPasswordSpace[TempB1]
        EWrite Eepromaddress, [TempB2]
        Inc TempB1
        Inc Eepromaddress
    Until Eepromaddress = EndEepromSpace 

    GoSub EraseFlashSub
   
    USBGeneralStatus = $55                                          ' Tell the PC that the command has been done
   
    GoSub DoUSBOut
   
    GoSub ReadPasswordSub
       
    GoSub CopyOverPassword
                   
    Return


' ************************************************************
'  ReadPasswordSub
'  Reads in the current password to access the Device from Eeeprom
'
' ************************************************************
ReadPasswordSub:

    GoSub GetXorVal
   
    TempW1 = Scramblelookup
    Eepromaddress = EepromPasswordStart
    Index1 = 0
    Repeat
        TempB1 = 0
        TempB3 = 0
        Repeat           
            TempB2 = ERead Eepromaddress                        ; read the eeprom
            TempB3 = TempB3 >> 1
            If TempB2.2 = 1 Then TempB3.3 = 1
            If TempB2.0 = 1 Then TempB3.7 = 1           
            Inc TempB1
            Inc Eepromaddress
        Until TempB1 = 4
        TempB3 = TempB3 ^ XorVal
        TempB1 = CRead TempW1
        PasswordString[TempB1] = TempB3
        Inc TempW1
        Inc Index1
    Until Index1 = 16
       
    Return
   
   

GetXorVal:
    Eepromaddress = FreeEepromSpace
    XorVal = 123
    Repeat
        TempB1 = ERead Eepromaddress
        XorVal = XorVal + TempB1
        Inc Eepromaddress
    Until Eepromaddress = EepromPasswordStart
   
    Return
   
' ************************************************************
'  ReturnUserNameSub
'  Returns the current user name stored in eeprom
'
' ************************************************************     
   
ReturnUserNameSub:
    Eepromaddress = UserNameStart
   
    Index1 = 0
    Repeat
        TempB1 = ERead Eepromaddress                                 ; Read from Eeprom the username
        USBOutBuffer[Index1] = TempB1                                ; put it in the USBout buffer
        If TempB1 = 0 Then Break                                     ; Exit on a null
        Inc Index1
        Inc Eepromaddress
    Until Index1 >= 16
   
    GoSub DoUSBOut                                                   ; Send the info
   
    Return 

' ************************************************************
'  ReturnPasswordSub
'  Returns the current password stored in eeprom  not accesable via this program
'
' ************************************************************
   
ReturnPasswordSub:

    Index1 = 0
    Repeat
        USBOutBuffer[Index1] = PasswordString2[Index1]
        Inc Index1
    Until Index1 = 16
    GoSub DoUSBOut
    Return
       

' ************************************************************
'  WriteUserNameSub
'  Writes the data in the usbbuffer to the user name space in eeprom
'
' ************************************************************     
   
WriteUserNameSub:
   
    Eepromaddress = UserNameStart
   
    Index1 = 1
    Repeat
        TempB1 = USBInBuffer[Index1]                                 ; Take the data from the usbin buffer
        EWrite Eepromaddress, [TempB1]                               ; And write it to Eeprom
        If TempB1 = 0 Then Break                                     ; exit on a nul
        Inc Index1
        Inc Eepromaddress
    Until Index1 = 17

    USBGeneralStatus = $55                                          ' Tell the PC that the command has been done
   
    GoSub DoUSBOut     
    Return

' ************************************************************
'  ValidationControl
'  Simple sub part of a system to control unortherised programs being instaled
'
' ************************************************************ 
ValidationControl:
    Select USBCommandIn
    Case RunMainCode, WriteFlash, FinishWrite
        Return
    Case Else
        ValidationCount = 0
    EndSelect
    Return
   
' ************************************************************
'  RunBuildtestsSub
'  Test the system at manufacturing time
'
' ************************************************************
RunBuildtestsSub:
    USBGeneralStatus = $55                                         ' Tell the PC that the tests will be done
    GoSub DoUSBOut     
   
    Ledoff
    ScapOn
    Index1 = 0
    Repeat
        DelayUSBValue = 250                                        ' Delay for ~ 1 second   
        GoSub DelayUSB
        Inc Index1
    Until Index1 = 4
   
       
    SBOREN = 0                                                                ' Turn on the GPS module
    GpsOn
   
    Index1 = 0
    Repeat
        DelayUSBValue = 250                                        ' Delay for ~ 1 second   
        GoSub DelayUSB
        Inc Index1
    Until Index1 = 4
     
    SBOREN = 1

   
    TXSTA = $20   ' Enable transmit, BRGH = 0                       ' Set up the usart to recieve data
    SPBRG = 77    ' 19200 Baud @ 24MHz, 0.16%
    SPBRGH = 0
    BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
   

    While 1 = 1                                                    ' Test the GPS module
        While RCIF = 0
            USBPoll
        Wend
        If RCREG = "$" Then
            GpsOff 
            ScapOff                                               ' Disable the GPS module
            TXEN = 0                                               ' turn off the usart
            USBOutBuffer#0 = $F0                                   ' Tell the PC that the usrat is ok
            GoSub DoUSBOut
            GoTo TestStage2   
        EndIf
    Wend

   
     

    LedOn
TestStage2:
   
    While WayPointLogButton = 1
        USBPoll
    Wend       
    USBOutBuffer#0 = $F1
    GoSub DoUSBOut
       
    While VibInput = 1
        USBPoll
    Wend
    While VibInput = 0: Wend
    USBOutBuffer#0 = $F2
    GoSub DoUSBOut
   
   
    Low SounderSink   
    Index1 = 0
    Repeat
        Toggle LedAndSounderDrive
        DelayUSBValue = 250
        GoSub DelayUSB
        Inc Index1
    Until Index1 = 8
   
    SounderSinkTris = 1                                            ' turn off the sounder
       
    Return
                                       
;===========================
Initials:
     

    SounderOff
   
    DelayMS 100                                                     ; Delay

   
; Set up and check the Sflash is working
           
    GoSub InitialsM25P
   
    TIMER1REG = TMR1_Val
    T1CON = %00000000                                     ' Set up Tmr1 to have 1:1 prescaler and act as a timer
    TMR1IF = 0                                         ' Clear Tmr1 interrupt flag

    TMR1IE = 0                                         ' Do not Enable Tmr1 as peripheral interrupt source
    TMR1ON = 1
   
    Led_Timer = 2
   
    OKToReadFlash = ERead EeSflashErasereq

    PR2 = 0b01011101 ;
    T2CON = 0b00000111 ;
    CCPR1L = 0b00101110 ;                                           ;~ 50% duty
    CCP1CON = 0b00111100 ;
   
    High LedAndSounderDrive
       
    Return

;_____________________________________________________________________________


TMR_CONTROL:

    If TMR1IF = 1 Then   
        Clear TMR1ON                                            ' STOP THE TIMER
        TIMER1REG = TIMER1REG + TMR1_VAL                        ' LOAD TMR1
        Set TMR1ON                                              ' START THE TIMER AGAIN       
       
        If Led_OnTimer > 0 Then
            Dec Led_OnTimer
        Else
            LedOn
            Led_Status = 0
        EndIf       
        Clear TMR1IF                             ; Clear TMR1 interrupt flag
    EndIf
   
    Return   


InitialsM25P:
   
    SPIDiPinTris = 1                                            ; Make sure the data is an input
    SPIDoPinTris = 0                                            ; Make sure the data out is output
    M25PChipSelect = 1                                          ; Ensure the M25P chip select is high
    M25PChipSelectTris = 0                                      ; Makes sure the M25P Chip Select Pin is an output
    SPIClockPinTris = 0   

;check the Sflash is working
   
'    While 1 = 1
'        TempB1 = M25PReadByte $FFFFF7                           ; Read a byte right from the top of the memory
'        If TempB1 = $FF Then Break                              ; Its ok break out of the loop and we return
';  Else we have an issue
'        SounderOn
'        DelayMS 10                                              ; ?<<<<<
'        SounderOff
'        DelayMS 100 
'    Wend
                                                       
    Return
           
;=== This is the main USB mode loop =========================

USBMode:

    GoSub Initials
    GoSub ReadPasswordSub 
       
    GoSub CopyOverPassword
   
    LedOn

     
loop1:             
    While 1 = 1                                                 ; Main Loop Runs here
   
                   
        GoSub DoUSBIn
 
        Clear USBOutBuffer
       
        GoSub ValidationControl
       
; Handle all the common USB commands that work all the time       
       
        Select USBCommandIn
           
            Case AccessID                                           ; Try the password to gain access
                GoSub AccessIDSub
           
            Case ChangePassword                                     ; Change the password
                GoSub ChangePasswordSub               
               
            Case EraseProgram                                       ; Erase Program memory
                GoSub EraseProgramData         

            Case WriteFlash                                         ;  Write Y bytes from Prom at X,X,                       
                GoSub WriteFlashData
               
            Case FinishWrite                                        ; Set the appropriate flags to say it was a successful firmware update           
                FlashCleared = 0
                GoSub FinishWriteSub       

            Case ReturnFirmWareVerNo                                ; Return the main code Version Number
                TempW1 = ($4000 - 10)         
                GoSub ReadFirmWareNo 
           
            Case ReturnBootloaderVerNo
                TempW1 = BootVersionNo                              ; Return the Bootloader version Number
                GoSub ReadFirmWareNo
                               
            Case RunMainCode
                If ValidationCount = ValidationCountNumber Then
                    @ Call MainCodeStart                            ; Run the main code above the Bootloader
                EndIf
                 
            Case WriteNewUSBSerial
                 GoSub WriteNewUSBSerialSub
                 
            Case ReturnSflashStatusReg
                M25PReadStatus
                USBGeneralStatus = M25PStatus
                M25PDisable                 
                GoSub DoUSBOut 
               
            Case RunBuildtests
                GoSub RunBuildtestsSub
                                                                             
               
               
        EndSelect
       
        If AccessGranted = 1 Then               
       
            Select USBCommandIn
               
            Case ReturnAddressOfNextLog                             ; Return the address of the Next log
                GoSub ReadNextLogAddress
                LastLogAlias = TempD1
                GoSub DoUSBOut
               
            Case SendDataFromSFlash                                 ; Send the data from Sflash as requested           
                SflashFrom = SflashFromAddressAlias                 ; read in the address from memory
                SflashDataCnt =  SflashDataCntAlias
                If SflashDataCnt > 0 Then                           ; only process this command if the request is for > 0 bytes or we crash
                    GoSub SendSFlashData       
                EndIf
                Clear USBInBuffer                                   ; Always make sure this is clear following this command
               
            Case SaveDataInEeprom
                 GoSub WriteDataToEeeprom
                 
            Case SendDataFromEeprom
                USbDataOut1 = ERead EepromAddressAlias
                GoSub DoUSBOut
           
            Case EraseSerialFlash
                GoSub EraseFlashSub
                USBGeneralStatus = $55                              ' Tell the PC that the command will be done
                GoSub DoUSBOut
                                             
'            Case IsSFlashEraseDone
'                GoSub IsaFlashEraseDOne         
               
'            Case EnableGPS
'                SBOREN = 0
'                DelayUS 100
'                GpsEnable = GPSPowerOn
'                GpsEnableTris = 0
'                DelayUSBValue = 20
'                GoSub DelayUSB
'                SBOREN = 1
           
'            Case DisableGps
'                GpsEnable = GPSPowerOff
                   
            Case BulkEraseSerialFlash
                M25PBulkErase 
               
               
            Case ReturnUserName                                     ; Return the user name of this logger
                 GoSub ReturnUserNameSub
           
            Case WriteUserName
                 GoSub WriteUserNameSub
                                                                             
            EndSelect

        EndIf     
               
    Wend



Scramblelookup:
    CData 8,1,15,12,2,0,15,3,5,9,2,9,4,10,13,8
ValidationDataTable:
    CData As Word $2B75,$5175,$5375,$0775,$6F75,$2775,$5F75,$2B75
Org (MainCodeStart - $16)                                            ; redirection for
GoTo ReadPasswordSub
Org (MainCodeStart - $12)                                            ; redirection for
GoTo ReturnPasswordSub

;***** The firm ware version no *************

BootVersionNo:
   CData As Word $0030

$ifndef EnableSim
    Org MainCodeStart
    @ GoTo ReturnPasswordSub2
$endif
   
Org  MainCodeStart + $8
USER_HIGH_INTERRUPT_VECTOR:
Org MainCodeStart + $18
USER_LOW_INTERRUPT_VECTOR:   







trastikata

Quote from: TimB on Sep 11, 2021, 12:10 PMHere is one version of the code

Thank you Tim, much appreciate you sharing the code.

I noticed you are not protecting the Configuration registers block and the lower memory blocks where the bootloader resides - I am always protecting these blocks in the Fuse settings being afraid that a glitch during flashing/erasing could write or erase something there and render the device inoperable for the user.

Also another question - did you write all those register aliases at the beginning by hand because I see they are not available for PIC18F14K50 in the IDE?     

TimB

From Memory I could not set Block 0 to be non writable as it would not align with my main code block. When writing the loader I would get right up to a 64byte block and if I needed more space would move it just an extra 64 bytes. I wanted to maximise the code space for my main code.
In all the 1000's out in the field the bootloader was never damaged as it was protected with masking in the code to prevent you writing over itself. However in the field we had issues with the main data being corrupted and the Eeprom. So we stopped issuing updates. The last one was in 2016 to fix a leap year bug.

Ref the register aliases
Many many years back I paid someone to produce the data for most of the pics around at the time. I made them available to all on the forum. Then Les said not to include them in the compiler defs so they were added to a separate file and the IDE pulled them from that folder.
In the years since and many IDE and compiler updates later, they were omitted. Some one did write a generator to produce them automatically as a plug-in. It however is no longer working. I miss having them :(



John Lawton

Quote from: tumbleweed on Sep 11, 2021, 12:28 AMNone of the devices in the old 18FxxJ series have internal EEPROM. I think it's related to the process they used for those (that part is over 10 years old now).

If you want something with USB that has internal EEPROM, try the 18F24K50. It has a lot more "traditional" arrangement (plus it's bound to be cheaper).
Unfortunately too late, I have 50 boards made with the 18F24J50 device fitted. :(

John Lawton

Quote from: TimB on Sep 11, 2021, 10:00 AMtrastikata

Would you like the source to my 14k50 bootloader? I will need a small change to work with current compiler but it handles all the bootloading etc. It even has hacking safeguards built in.

If nothing else it may be interesting to take ideas from

I think I have a working version of that bootloader (at least it was working a year ago) that uses John B's PC application. Trouble is it reads/writes to the EEPROM as part of its operations, so will have to be modified to work with the 18F24J50 device I'm using. Sigh!

trastikata

Quote from: John Lawton on Sep 11, 2021, 06:30 PMI think I have a working version of that bootloader (at least it was working a year ago) that uses John B's PC application. Trouble is it reads/writes to the EEPROM as part of its operations, so will have to be modified to work with the 18F24J50 device I'm using. Sigh!

John, here is a modified bootloader for PIC18F25J50, based on Tim's article in the old boards, it is however modified to work with my own firmware update program. Nevertheless it might give you some clues.

Device = 18F25J50

Declare Xtal = 48

Include "USB_Bootloader.inc"

Declare Auto_Variable_Bank_Cross = On
Declare Auto_Heap_Strings = On
Declare Auto_Heap_Arrays = On
Declare Reminders = On
Declare FSR_CONTEXT_SAVE = On
Declare LABEL_BANK_RESETS = On
Declare Optimiser_Level = 3
Declare Dead_Code_Remove = On
Declare All_Digital = 1

On_Interrupt     GoTo USER_HIGH_INTERRUPT_VECTOR   ; RE-MAP INT VECTORS
On_Low_Interrupt GoTo USER_LOW_INTERRUPT_VECTOR    ; RE-MAP INT VECTORS

Config_Start
  WDTEN = OFF ;Disabled - Controlled by SWDTEN bit
  PLLDIV = 5 ;Divide by 5 (20 MHz oscillator input)
  STVREN = On ;Enabled
  XINST = OFF ;Disabled
  Debug = OFF ;Disabled
  CPUDIV = OSC1
  Cp0 = OFF ;Program memory is not code-protected
  OSC = HSPLL ;HS+PLL, USB-HS+PLL
  T1DIG = OFF ;Secondary Oscillator clock source may not be selected
  LPT1OSC = OFF ;High-power operation
  FCMEN = On ;Disabled
  IESO = On ;Disabled
  WDTPS = 32768 ;1:32768
  DSWDTOSC = INTOSCREF ;DSWDT uses INTRC
  RTCOSC = T1OSCREF ;RTCC uses T1OSC/T1CKI
  DSBOREN = On ;Enabled
  DSWDTEN = OFF ;Disabled
  DSWDTPS = G2 ;1:2,147,483,648 (25.7 days)
  IOL1WAY = OFF ;The IOLOCK bit (PPSCON<0>) can be set and cleared as needed
  MSSP7B_EN = MSK7 ;7 Bit address masking mode
  WPFP = PAGE_3 ;Write Protect Program Flash Page 63
  WPEND = PAGE_0 ;Page 0 to WPFP<5:0> erase/write protected
  WPCFG = On ;Configuration Words page is erase/write-protected
  WPDIS = On ;WPFP<5:0>/WPEND protected
Config_End


Symbol MainCodeStart            = 5120                          ; Main code start address / Keep on a 64 byte boundry
Symbol EndOfCodeSpace           = 32767                         ; End of code space
Symbol FlashEraseSeg            = 1024                          ; The size of memory bank erased in one go
Symbol FlashWriteBlock          = 64                            ; The memory size Flash write needs to do in one block
Symbol FlashClearedStatus       = 4992                          ; Address in Flash of the current status of the eeprom if cleared etc
Symbol EraseProgram             = $01                           ; Erase memory ; Returns $55 via USB when done
Symbol WriteFlash               = $02                       
Symbol FinishWrite              = $03                           ; Finish the write ; Returns $55 via USB then and writes to eeprom to finish the write sequence
Symbol RunMainCode              = $04                           ; Run the main code above the bootloader


Dim PromAddress As Dword
Dim tempPromAddress As Dword
Dim BootOk As Byte
Dim temp As Byte
Dim y As Word
Dim x As Dword
Dim USBBuffer As String * 64

MainProgramLoop:
    Clear : OSCTUNE.6 = 1 : DelayMS 100 : UCFG.2 = 1 : DelayMS 5
    ANCON0 = %11111110 : ANCON1 = %00011111 : DelayCS 4 : ADCON1.7 = 1
    T0CON.7 = 0 : T0CON = %00000100 : TMR0H = $6D : TMR0L = $84
    INTCON.7 = 1 : INTCON.5 = 1 : INTCON.2 = 0 : INTCON2.2 = 0
   
    BootOk = CRead FlashClearedStatus
    If BootOk = "W" Then                          ; Read in from eeprom the current write status
        GoTo USB_look_UP       
    Else
        GoTo USB_Enumerate                                 
    EndIf                                                 ; Check if were ok to run now
   
USB_look_UP:
    For x = 0 To 150000
        mUSBService()
        If tUSB_Attached = True Then
            DelayMS 1
            mUSBService()
            DelayMS 1
            mUSBService()
            GoTo USB_connected
        EndIf   
        DelayUS 25   
    Next

    GoTo RunMain
   
USB_connected:
    For x = 0 To 150000
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                Break
            EndIf
        EndIf
        DelayUS 25
    Next

    USBBuffer = RXReport
    If USBBuffer[0] = "F" Then
       If USBBuffer[1] = "L" Then
            If USBBuffer[2] = "A" Then
                If USBBuffer[3] = "S" Then
                    If USBBuffer[4] = "H" Then
                        DelayMS 1
                        mUSBService()
                        DelayMS 1
                        mUSBService()
                        GoSub USBOK_Responce
                        GoTo USBMode
                    EndIf
                EndIf
            EndIf
        EndIf           
    EndIf

    GoTo RunMain


USB_Enumerate:
    While 1 = 1
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                GoTo USB_ForceConnected
            EndIf
        EndIf 
    Wend
   
USB_ForceConnected:
    USBBuffer = RXReport
    If USBBuffer[0] = "F" Then
       If USBBuffer[1] = "L" Then
            If USBBuffer[2] = "A" Then
                If USBBuffer[3] = "S" Then
                    If USBBuffer[4] = "H" Then
                        GoSub USBOK_Responce
                        GoTo USBMode
                    EndIf
                EndIf
            EndIf
        EndIf           
    EndIf
    GoTo USB_Enumerate
   
RunMain:
    UCON.3 = 0
    @ Call MainCodeStart                                            ; All is ok so run main code
   
   
USBMode:   
    While 1 = 1
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                Select RXReport[0]
                    Case EraseProgram
                        GoSub EraseProgramData                                             
                        GoSub USBOK_Responce 
                    Case WriteFlash                                                               
                        GoSub WriteFlashData
                    Case FinishWrite
                        GoSub FlashClearStatus                                             
                        GoSub USBOK_Responce
                    Case RunMainCode               
                        GoSub USBOK_Responce
                        GoTo RunMain                               ; Run the main code above the Bootloader       
                EndSelect
            EndIf 
        EndIf
    Wend   
   
    GoTo USBMode 

FlashClearStatus:
    PromAddress = 4096
    CErase PromAddress
    For x = 0 To 20
        mUSBService()
        DelayMS 1
    Next
   
    For x = FlashClearedStatus To FlashClearedStatus + 63
        CWrite x, ["W"]
    Next
   
    For x = 0 To 7
        mUSBService()
        DelayMS 1
    Next   
Return     

EraseProgramData:
    PromAddress = 4096
    CErase PromAddress
    For x = 0 To 7
        mUSBService()
        DelayMS 1
    Next
   
    For x = FlashClearedStatus To FlashClearedStatus + 63
        CWrite x, ["C"]
    Next
   
    For x = 0 To 7
        mUSBService()
        DelayMS 1
    Next
    PromAddress = MainCodeStart
   
    For PromAddress = MainCodeStart To (EndOfCodeSpace - FlashEraseSeg) Step FlashEraseSeg
        CErase PromAddress
        For x = 0 To 7
            mUSBService()
            DelayMS 1
        Next
    Next
Return

WriteFlashData:
    GoSub USBRequest
     While 1 = 1
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                Break
            EndIf
        EndIf           
    Wend
   
    USBBuffer = RXReport
    PromAddress.Byte3 = 0 
    PromAddress.Byte2 = 0
    PromAddress.Byte1 = USBBuffer[0]
    PromAddress.Byte0 = USBBuffer[1] 
    tempPromAddress = PromAddress

    GoSub USBRequest
    While 1 = 1
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                Break
            EndIf
        EndIf           
    Wend
   
    For x = 0 To 31
        USBBuffer[x] = RXReport[x]
    Next
   
    GoSub USBRequest
    While 1 = 1
        mUSBService()
        If tUSB_Attached = True Then
            If HID_DataAvailable() = True Then
                HID_ReadReport()
                Break
            EndIf
        EndIf           
    Wend
   
    For x = 0 To 31
        USBBuffer[x + 32] = RXReport[x]
    Next
   
    For x = 0 To 63
        y = PromAddress
        temp = USBBuffer[x]
        CWrite y,[temp]
        Inc PromAddress
    Next   
   
    For x = 0 To 7
        mUSBService()
        DelayMS 1
    Next
   
    For x = 0 To 63
        y = tempPromAddress
        temp = CRead y
        If temp <> USBBuffer[x] Then
            GoTo SendBadResultandReturn
        EndIf
        tempPromAddress = tempPromAddress + 1
    Next
 
ResultOkExit:
    GoSub USBOK_Responce
    Return
SendBadResultandReturn:
    GoSub USBNotOK_Responce
Return


USBRequest:
    Clear TXReport
    TXReport = "187"
    HID_WriteReport()
Return

USBOK_Responce:
    Clear TXReport
    TXReport = "85"
    HID_WriteReport()
Return

USBNotOK_Responce:
    Clear TXReport
    TXReport = "170"
    HID_WriteReport()
Return

Org 4090
    CData $01
   
Org 4092
    CData $01
   
Org 4094
    CData $00
 

Org  MainCodeStart + $8
USER_HIGH_INTERRUPT_VECTOR:
Org MainCodeStart + $18
USER_LOW_INTERRUPT_VECTOR:   
   
   

John Lawton

Hi Trastikata, once again many thanks, I can see the similarities to the Tim Box / John B bootloader code I have.

My version of the bootloader code was actually based on a modified version published by See_Moss in 2019 so credit to him as well.

I was hoping to be able to work with John B on an updated/customised version of his PC program but IIRC he said the USB programming framework he had used originally was getting rather out of date and this might hinder ongoing development.

JohnB

I have now a new HID library installed but haven't had time to do much with it.

I'll try and find a day or so to play with it then I will be able to see if we can bring the bootloader back to life
JohnB

John Lawton

Hi John, I think you've still got the test board with bootloader that I sent you, so fingers crossed that the new library will work.

John

JohnB

I'm sorry John. I haven't forgotten, in fact the board is sitting within eyesight from my PC.  However, I have been adding a Fineline type addition to Positron Studio.  It's nearly complete, I am just trying to ensure it will work for both standard and HiRes screens.

I'll give it a try after I have published this new update, probably 2 weeks away as I an awaiting an update to the 3rd party editor which forms the basis of the highlighter.
JohnB