News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

Tiny Multi Bootloader

Started by AlbertoFS, May 19, 2025, 05:35 PM

Previous topic - Next topic

AlbertoFS

I downloaded the files of this bootloader and I do not know what is the correct asm file for the PIC18F25K22?
Thank you
73's de EA3AGV

JonW

If you are looking for the bootloader firmware, you must contact Evan on the tiny bootloader site and pay for it.

JonW

The website is very fragmented, but this link should download all the latest files HERE.  If you look in the firmware folder, a guy named Dan has provided the ASM for that device.

Alternatively you can pay Evan to generate a custom one for you

AlbertoFS

I want to try the version 0.14xx and I will try it.
I took an asm file for the PIC18F25K22 and I rewrite it with Positron.
It takes a little more bits but if more easy to read & to modify for other PICs.
Now it is the time to try it in hardware.
Thank you.
73's de EA3AGV

top204

#4
As Jon correctly stated, the Tiny Bootloader web site is very fragmented, and very difficult to navigate, and a lot of the source codes are not there or not tested etc... It is in desperate need of maintenance, because it has some important and useful sources on it (somewhere). LOL.

I created a bootloader for the Tiny bootloader in Positron8, and it is actually smaller than the original bloated, and confusing, assembler code. I uploaded it to the Tiny Bootloader site forum, but it just got ignored and forgotten. I did the same for the original Tiny+ bootloaders many years ago for projects I was working on, using their mechanism, but my code. :-)

The Tiny bootloader source is for a PIC18F26K40 device, using the internal oscillator at 64MHz, but it may help you in creating your own. It is listed below, and, as can be seen, it is a whole lot easier to navigate and understand the code than the original assembler codes:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Multi-Tiny+ Serial Bootloader for a PIC18F26K40 device.
' Operates with a 64MHz internal oscillator and a Baud of 19200.
'
' Written by Les Johnson for the Positron8 BASIC compiler.
'
Declare Warnings = Off
    Device = 18F26K40                               ' Tell the compiler what device to compile for
    Declare Xtal = 64                               ' Tell the compiler what frequency the device is operating at (in MHz)
    Declare Library_Core = Off                      ' Disable the compiler's Library core and any extra items the compiler adds to a user's program

$define cIdTypePIC      0x6E                        ' The microcntroller type for the PC application
$define cMax_Flash      _code                       ' The amount of flash memory on the device (in bytes)
$define cWriteBlockSize _block                      ' The size of the block write to flash memory
$define cEepromSize     _eeprom                     ' Size of EEPROM (256 or 1024)

$define cBootloader_Address $eval (cMax_Flash - 264) ' 264 bytes long reserved at the top of flash for the bootloader
'
' Bit names of the NVMCONx SFRs
'
$define cRD      0
$define cWR      1
$define cWREN    2
$define cWRERR   3
$define cFREE    4
$define cNVMREG0 6
$define cNVMREG1 7
'
' Create variables for the bootloader
'
    Dim bWriteBuffer[64] As Byte                    ' Buffer used for holding data
    Dim bCRC             As Byte                    ' Holds the CRC value
    Dim bByteCount       As Byte                    ' Holds the amount of bytes for a block flash write
    Dim bFlags           As Byte                    ' Holds the requirement of which memory area to write
    Dim tEepromWrite     As bFlags.6                ' Set if EEPROM is to be written
    Dim tConfigWrite     As bFlags.7                ' Set if Config memory is to be written

    Dim bTimeoutCounter1 As Byte                    ' \
    Dim bTimeoutCounter2 As Byte                    ' |  Timeout counter for receiving a byte
    Dim bTimeoutCounter3 As Byte                    ' /

    Dim wFSR0 As FSR0L.Word                         ' Convert 8-bit SFRs FSR0L\H into a 16-bit SFR

$ifndef False
    $define False 0
$endif
$ifndef True
    $define True 1
$endif
'-----------------------------------------------------------------------
' Reset
'
    GoTo BootloaderStart                            ' Jump to the start of the bootloader
'
' Set the high flash memory address where the bootloader sits
'
    Org cBootloader_Address
    Nop                                             ' \
    Nop                                             ' | Space to store the first 4 mnemonics from the user program
    Nop                                             ' |
    Nop                                             ' /

BootloaderStart:
    ANSELC = 0                                      ' Set PORTC to digital
'
' Set the microcontroller to internal 64MHz operation with an HFINTOSC_1MHZ fuse
' Notes     : Waits for the oscillator to become stable
'
    OSCCON1 = %01100000
    OSCCON3 = %00000000
    OSCEN   = %00000000
    OSCFRQ  = %00001000
    OSCTUNE = %00000000
    Repeat: Until OSCSTATbits_HFOR = 1
'
' UART1 Actual Baud = 19230.8
' UART1 Baud Error = 0.15625
'
    BAUDCONbits_BRG16 = 0
    SP1BRG = 207                                    ' \
    SP1BRGH = 0                                     ' | Setup USART1 for 19200 Baud at 64MHz operation
    TX1STA  = 36                                    ' |
    RC1STA  = 144                                   ' /
'
' Configure USART1 PPS
'
    PPS_Unlock()                                    ' Unlock PPS
    TX1PPS = Pin_C6                                 ' Set PORTC.6 as TX
    RX1PPS = Pin_C7                                 ' Set PORTC.7 as RX
    RC6PPS = PPS_Fn_TX1                             ' Set PORTC.6 as TX
    RC7PPS = PPS_Fn_RX1                             ' Set PORTC.7 as RX
'
' Wait for computer response
'
    GoSub ReceiveByteInto_WREG                      ' Receive a byte from USART1 into WREG
    If WREG <> $C1 Then GoTo Bootloader_Exit        ' Expect $C1
    TXREG1 = cIdTypePIC                             ' Send the microcontroller type to the PC application

    Do                                              ' Create a loop
        Clrwdt                                      ' Clear the watchdog timer within the loop
        WREG = "C"                                  ' Everything OK so send the character "C" to the PC application
ContinueLoop:
        TXREG1 = WREG                               ' Transmit the contents of WREG
        bCRC = 0                                    ' Clear the CRC value
        GoSub ReceiveByteInto_WREG                  ' Receive the Upper address
        TBLPTRU = WREG
        bFlags = WREG                               ' For EEPROM and Config
        GoSub ReceiveByteInto_WREG                  ' Receive the High address
        TBLPTRH = WREG
        $if (cEepromSize = 1024)
        NVMADRH = WREG                              ' High address of EEPROM
        $endif
        GoSub ReceiveByteInto_WREG                  ' Receive the Low address
        TBLPTRL = WREG
        NVMADR = WREG                               ' Low address of EEPROM

        GoSub ReceiveByteInto_WREG                  ' Receive the byte count required for writing
        bByteCount = WREG                           ' Place it into variable bByteCount

        wFSR0 = AddressOf(bWriteBuffer)             ' FSR0L\H now hold Buffer Beginning address
        Tblrd*-                                     ' Read from flash memory with auto decrement of address in TBLPTR
RcvOct:
        GoSub ReceiveByteInto_WREG                  ' Receive the type of write
        POSTINC0 = WREG                             ' For Config memory
        NVMDAT = WREG                               ' For EEPROM
        TABLAT = WREG                               ' For Flash memory
        Tblwt+*                                     ' Write to flash memory with auto increment of address
        Djnz bByteCount, RcvOct                     ' Loop for the amount of bytes to receive

        GoSub ReceiveByteInto_WREG                  ' Get CRC
CRC_Failed:                                         ' CRC failed
        WREG = "N"                                  ' Get ready to send the character "N"
        If STATUSbits_Z = 0 Then GoTo ContinueLoop

        If tConfigWrite = True Then                 ' Skip if not writing to Config
            GoTo Write_Config                       ' Write Config
        EndIf
        WREG = (1 << cWREN)                         ' Setup for EEPROM write
        If tEepromWrite = False Then                ' Is it an EEPROM write?
            GoSub SetupEraseBlock                   ' No. So setup to erase the block of flash memory
        EndIf
        GoSub SetupWriteByte
        Continue
        '
        ' Write the configuration fuses (if required)
        '
Write_Config:
        Dec FSR0L                                   ' FSR0 = FSR0 - 1
        Clrwdt                                      ' Clear the watchdog timer
        TABLAT = INDF0                              ' Load TABLAT
        WREG = ((1 << cNVMREG1) | (1 << cNVMREG0) | (1 << cWREN))  ' Setup for Config memory
        GoSub SetupWriteByte
        Tblrd*-                                     ' Write to memory and auto decrement address in TBLPTRL\H
        Tstfsz FSR0L                                ' FSR0L = 0?
        GoTo Write_Config                           ' \ Loop until FSR0L = 0
    Loop                                            ' /
'
' Subroutines
'
SetupEraseBlock:
    WREG = $94                                      ' Setup Erase; EEPGD, FREE, WREN = 1, CFGS = 0

SetupWriteByte:
    NVMCON1 = WREG
    NVMCON2 = $55                                   ' \ Unlock memory writes
    NVMCON2 = $AA                                   ' /
    NVMCON1bits_WR = 1
    Retlw ((1 << cNVMREG1) | (1 << cWREN))          ' Setup for writes in WREG when returned

ReceiveByteInto_WREG:
    Clrwdt                                          ' Clear the watchdog timer
    bTimeoutCounter1 = ((_xtal / 2) + 1)            ' For 20MHz => 11 => 1 second delay
Repeat1:
    bTimeoutCounter2 = 0
Repeat2:
    bTimeoutCounter3 = 0
Repeat3:
    If PIR3bits_RC1IF = 0 Then NotReceivedYet       ' Receive a byte from USART1
    WREG = RC1REG                                   ' Place the read data into WREG
    bCRC = bCRC + WREG                              ' Calculate CRC
    Return

NotReceivedYet:
    Clrwdt                                          ' Clear the watchdog timer within the inner loop
    Djnz bTimeoutCounter3, Repeat3
    Djnz bTimeoutCounter2, Repeat2
    Djnz bTimeoutCounter1, Repeat1
'
' Timeout
'
Bootloader_Exit:
    NVMCON1 = 0                                     ' Deactivate EECON
    RCSTA1bits_SPEN = 0                             ' Deactivate USART1
    GoTo(cBootloader_Address)                       ' Jump to the start of the user program

'-----------------------------------------------------------------------------------------------
' Setup the fuses to use the internal oscillator on a PIC18F26K40. With RA6 and RA7 as I/O lines
'
Config_Start
    RSTOSC = HFINTOSC_1MHZ          ' With HFFRQ = 4MHz and CDIV = 4:1
    FEXTOSC = Off                   ' External Oscillator not enabled
    WDTE = On                       ' WDT enabled
    CLKOUTEN = Off                  ' CLKOUT function is disabled
    CSWEN = On                      ' Writing to NOSC and NDIV is allowed
    FCMEN = Off                     ' Fail-Safe Clock Monitor disabled
    MCLRE = EXTMCLR                 ' MCLR pin is MCLR
    PWRTE = On                      ' Power up timer enabled
    LPBOREN = off                   ' ULPBOR disabled
    BOREN = On                      ' Brown-out turned on
    BORV = VBOR_245                 ' Brown-out Reset Voltage (VBOR) set to 2.45V
    ZCD = Off                       ' ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON
    PPS1WAY = Off                   ' PPSLOCK bit can be set and cleared repeatedly (subject to the unlock sequence)
    STVREN = Off                    ' Stack full/underflow will not cause Reset
    Debug = Off                     ' Background debugger disabled
    XINST = Off                     ' Extended Instruction Set and Indexed Addressing Mode disabled
    SCANE = Off                     ' Scanner module is Not available for use. SCANMD bit is ignored
    LVP = Off                       ' Low Voltage programming disabled
    WDTCPS   = WDTCPS_16            ' Watchdog Divider ratio 1:2091752 (64 seconds)
    WDTCWS   = WDTCWS_7             ' Window always open (100%). Software control. Keyed access not required
    WDTCCS   = LFINTOSC             ' WDT reference clock is the 31.2kHz HFINTOSC output
    WRT0 = Off                      ' Block 0 (000800-001FFF) not write-protected
    WRT1 = Off                      ' Block 1 (002000-003FFF) not write-protected
    WRTC = Off                      ' Configuration registers (300000-30000B) not write-protected
    WRTB = Off                      ' Boot Block (000000-0007FF) write-protected
    WRTD = Off                      ' Data EEPROM not write-protected
    Cp = Off                        ' User NVM code protection disabled
    CPD = Off                       ' Data NVM code protection disabled
    EBTR0 = Off                     ' Block 0 (000800-001FFF) not protected from table reads executed in other blocks
    EBTR1 = Off                     ' Block 1 (002000-003FFF) not protected from table reads executed in other blocks
    EBTRB = Off                     ' Boot Block (000000-0007FF) not protected from table reads executed in other blocks
Config_End

The oscillator could be changed to operate at 64MHz as standard, without the SFR setups, by altering the config fuse settings, but this was when I was first learning the new 18F26K40 device, and wanted things to guarantee work. :-) Also, remember, the bootloader code above enables the Watchdog timer, because that was required by the project it was written for. So if the Watchdog is not required in your programs, disable it in the config fuse settings of the bootloader code listing.

You will probably need to change the "cIdTypePIC" value as well, because I wrote the above source code before it was supported by the bootloader PC application. I actually altered the C++ PC application as well, to make it smoother to use, instead of the bloat in the original. However, I cannot release that source code.

I would highly recommend the PIC18FxxK40 devices. They are very reliable, easy to use, and have lots in them for their price. The newer PIC18FxxQxx devices also seem to match them more closer than other devices, in their internal workings.

AlbertoFS

Thank you very much Les.
73's de EA3AGV

midali

Looks here, maybe is usefull for you: Bootloader

AlbertoFS

I have finished to modify the bootloader for the PIC18F25k22.
I think I'm going to use the PIC18F25K40. But I probably need to modify the piccodes.ini file.
$6D, C, 18F w/32KB flash & 1024B EE,   $8000, $400, 264, 128,
$6E, C, 18F w/64KB flash & 1024B EE,  $10000, $400, 264, 128,

What is the significance of the last parameters of these lines, after $8000/$10000?
Than Yoo.
73's de EA3AGV

JonW

Its documented inside the Piccodes.ini file

; device's line structure:
; ID code (as defined in source firmware!), device family, device name, max flash memory (bytes),EE (bytes), bootloader size (bytes), transfert block size (bytes),
;
; The '$' symbol is for hexadecimal numbers.
;
;     Specific to Family "B". The high bit of for the ID Code indicates this is an Enhanced 16F or 12F PIC

AlbertoFS

Thank you JonW,
The bootloader size (bytes) must be a multiple of write Block number or the number of real compiled bytes?
Sorry I am new in this aera (TinyBootloader)
Thank you
73's de EA3AGV

top204

#10
If memory serves me right, because it has been quite a while since I created a bootloader.

The size of the bootloader, and its starting address depends on the erase block size, because the original reset address jump that is held at the start of flash memory, is stored in the bootloader's flash, or just under it. So when the device's flash is being written too, the flash erase block is cleared, then the bytes are written to it, and the original jump address has to be safe.

It is the PC application that re-arranges the address jumps in the HEX file that is being written, so it swaps the original jump with the start address of the bootloader, and the bootloader jumps to what was once held in the device's reset address. i.e. Address $0000.

The original flash PIC devices did not have this problem, because they could be written just like EEPROM, and one word erased, then written too. Instead of having to erase a larger lump of flash, just to write a single word to it, as the later devices had to. However, looking at some of the brand new devices, it seems they may have gone back to the original method of one word erase, and one word write.

AlbertoFS

Thank you very much Les.
73's de EA3AGV

AlbertoFS

#12
Hi all,
I have taken the code written by Les and I have modified it for the PI18F25K22 and testing it with the Amicus18 board
amd making the reset through the DTR output.
I've almost crazy to testing this bootloader.
I noticed that the PC application erased the first 64 bytes of the bootloader when write user code.
It's not the user program's fault, because it takes up 1.5k bytes.
If I shifted the user code 64 bytes, the bootloader wouldn't be erased. But I couldn't set the
interrupt vectors in the main code with Positron.
I have written a workaround that allows to not write to the bootloader and miraculously this works.
I don't know if the original code is incorrect.
Could anyone share their experience with this topic?
Thanks.
'****************************************************************
'*  Name    : Tynibld18F25K22-Ext8MhzPLL-19200.bas              *
'*  Author  : [Les Johnson]                                     *
'*  Notice  : Copyright (c) 2025 []                             *
'*          : All Rights Reserved                               *
'*  Date    : 26/05/2025                                        *
'*  Version : 1.0                                               *
'*  Notes   : Modified by  A. Freixanet                         *
'*          :                                                   *
'****************************************************************
'
' Multi-Tiny+ Serial Bootloader for a PIC18F2XK22 device.
' Operates with a 20/32/64MHz external oscillator (Xtal) and a Baudrate of 19200.
'
' Written by Les Johnson for the Positron8 BASIC compiler.
'
'  The above 8 lines can be changed and buid a bootloader for the desired frequency (and PIC type)

' +---------+--------+------------+------------+-------------+-------------+-----------+--------+------+
' |IdTypePIC| Device | Write_Page | Erase_Page |   TX1/TX2   |   RX1/RX2   | max_flash | EEPROM | PDIP |
' +---------+--------+------------+------------+-------------+-------------+-----------+--------+------+
' |   0x61  |18F23K22|  32 words  |  32 words  |C6(17)/B6(27)|C7(18)/B7(28)|   0x2000  |   256  |  28  |
' |   0x61  |18F43K22|  32 words  |  32 words  |C6(25)/D6(29)|C7(26)/D7(30)|   0x2000  |   256  |  40  |
' |   0x62  |18F24K22|  32 words  |  32 words  |C6(17)/B6(27)|C7(18)/B7(28)|   0x4000  |   256  |  28  |
' |   0x62  |18F44K22|  32 words  |  32 words  |C6(25)/D6(29)|C7(26)/D7(30)|   0x4000  |   256  |  40  |
' |   0x64  |18F25K22|  32 words  |  32 words  |C6(17)/B6(27)|C7(18)/B7(28)|   0x8000  |   256  |  28  |
' |   0x64  |18F45K22|  32 words  |  32 words  |C6(25)/D6(29)|C7(26)/D7(30)|   0x8000  |   256  |  40  |
' |   0x66  |18F26K22|  32 words  |  32 words  |C6(17)/B6(27)|C7(18)/B7(28)|  0x10000  |  1024  |  28  |
' |   0x66  |18F46K22|  32 words  |  32 words  |C6(25)/D6(29)|C7(26)/D7(30)|  0x10000  |  1024  |  40  |
' +---------+--------+------------+------------+-------------+-------------+-----------+--------+------+
' Info taken from the Piccodes.ini
' device's line structure:
' ID code (as defined in source firmware!), device family, device name, max flash memory (bytes),EE (bytes), bootloader size (bytes), transfert block size (bytes),
'
' The '$' symbol is for hexadecimal numbers.
' device's line structure:
' $64, C, 18F w/32KB flash & 256B EE, $8000, $100, default, default,
' $64, C, 18F w/32KB Flash & 256B EE, $8000, $100, 200, 64,
'
' ID code (as defined in source firmware!): 0x64
' device family: C
' device name: 18F
' max flash memory (bytes): w/32KB Flash => 0x8000
' EE (bytes): $0100
' bootloader size (bytes): 320
' transfert block size (bytes): 64
'
' Interrupts + Watchdog => PROGRAM MEMORY BYTES USED in this CODE: 297 (by file.lst) (< 5 blocks of 64 Bytes = 320)
' Then the Bootloader size (bytes) is: 320
' For this PIC18F25K22 the erase block size is 64 bytes.
'/------------------------------------------------------------------------------
' For this bootloader:
' $64, C, 18F w/32KB Flash & 256B EE, $8000, $100, 320, 64,
' Open the Piccodes.ini with the NotePad.
' Disabled the $64, C,... line with a ;  (Not delete)
' Copy the new line:
' $64, C, 18F w/32KB Flash & 256B EE, $8000, $0100, 320, 64,

' Write a new line in Piccodes.in line => $64 in C:/TinyMultiBootLoaderPlus
' Disable the line $64 by a ;
'
' Copy this new line for PIC18F25K22 using a simple NOTE PAD
' $64, C, 18F w/32KB Flash & 256B EE, $8000, $0100, 320, 64,     
' And Save...
'
' THIS BOOTLOADER IS WRITTEN TO THE BOTTOM OF ROM MEMORY:
    Device = 18F25K22                               ' Tell the compiler what device to compile for       24
    Declare Xtal = 32                               ' Tell the compiler what frequency the device is operating at (in MHz)
'
'    Declare Access_Upper_64K = True                 ' Set to true because the 18F27K42 device has 128K of flash memory
    Declare Library_Core = Off                      ' Disable the compiler's Library core and any extra items the compiler adds to a user's program
'
'/  Enable/Disable the function as you want to include to the USER Program. (For Config Fuses & Interrupts)
    $define _Watchdog_
'-------------------------------------------------------------------------------
    $ifdef _Watchdog_
    Declare Watchdog = On 
    $else
    Declare Watchdog = Off
    $endif
'
    $define BootloaderBytes 320   
    $define cIdTypePIC      $64                                 ; The microcntroller type for the PC application
    $define cMax_Flash      _code                               ; The amount of flash memory on the device (in bytes)
    $define cWriteBlockSize _block                              ; (words _block = 32) The size in words of the block write to flash memory
    $define cEepromSize     _eeprom                             ; Size of EEPROM (256 or 1024)
    $define cBootloader_Address $eval (cMax_Flash - BootloaderBytes)        ; 5 x 64 blocks for bootlooader bytes long reserved at the top of flash for the bootloader
    $define cUSerCode_Address 4                                 ; See position of "goto _compiler_main_start_" of the main code.

'
'/ Memory Erase PIC18F25K22 = 64 Bytes
'/ EEPROM Memory PIC18F25K22 = 256 Bytes
'/ ROM instruction 16384 x2 = 32768 Bytes
'/ The Bootloader is placed in the bottom of ROM memory:       
'
' Bit names of the EECONx SFRs
'
    $define cRD    0
    $define cWR    1
    $define cWREN  2
    $define cWRERR 3
    $define cFREE  4
    $define cCFGS  6
    $define cEEPGD 7
'
' Create variables for the bootloader
' Check if the Write Blocks are same value of the Erase Blocks.
    Dim bWriteBuffer[_block * 2] As Byte            ' Buffer used for holding data
    Dim bCRC             As Byte                    ' Holds the CRC value
    Dim bByteCount       As Byte                    ' Holds the amount of bytes for a block flash write
    Dim bFlags           As Byte                    ' Holds the requirement of which memory area to write
    Dim tEepromWrite     As bFlags.6                ' Set if eeprom memory is to be written
    Dim tConfigWrite     As bFlags.7                ' Set if config memory is to be written
'
    Dim bTimeoutCounter1 As Byte                    ' \
    Dim bTimeoutCounter2 As Byte                    ' |  Timeout counter for receiving a byte
    Dim bTimeoutCounter3 As Byte                    ' /
    Dim F_DisableWrite As Bit
'
    Dim wFSR0 As FSR0L.Word                         ' Convert 8-bit SFRs FSR0L\H into a 16-bit SFR
    Dim wTBLPTR As TBLPTRL.Long
'/------------------------------------------------------------------------------
$ifndef False
    $define False 0
$endif
$ifndef True
    $define True 1
$endif
'///////////////////////////////////////////////////////////////////////////////
' Reset
'
' In the user code it must written:
' Declare Bootloader = Off
'
'///////////////////////////////////////////////////////////////////////////////
    GoTo BootloaderStart         ' Jump to the start of the bootloader
'
Declare Warnings = Off
' Set the high flash memory address where the bootloader sits   
'
    Org cBootloader_Address     
    GoTo (cUSerCode_Address)     ' Jump to the start of the user program     
'    Nop                       
'    Nop                       
    Nop                       
    Nop                       
'
'Org cBootloader_Address + 4
Declare Warnings = On
BootloaderStart:
    ANSELC = 0                          ' Set PORTC to digital
'
' Set the microcontroller to external OSC + PLL operation fuse
    OSCCON = 0                          '/Primary clock (determined by FOSC<3:0> in CONFIG1H).
    OSCCON2 = %00000100                 '/Primary Oscillator Drive Circuit ON       
'
'/ Take care of Watchdog
'    $ifndef _Watchdog_
    WDTCONBits_SWDTEN = False       '/ If WDT is turned off
'    $endif
    #ifdef Watchdog_req
    Clrwdt                          ' WDT is turned always on when Declare Watchdog = On
    #endif
'
    $ifdef _LVDCON
    LVDCON = 0
    $else
        $ifdef _HLVDCON
        HLVDCON = 0
        $endif
    $endif
'/------------------------------------------------------------------------------
    PMD0Bits_UART1MD = False        '/ Module URAT1 is enabled, Clock Source is connected, module draws digital power
'
' Configure USART1:
' The USART1 could be configurated by the Positron Code (OR Calculated by EUSARTCALC)
'Calculated Baudrate = 19208 @ Xtal 64MHz, Error = 0,04%
'
'Declare Hserial_RCSTA  = 144 ; Enable continuous receive
'Declare Hserial_TXSTA  = 36  ; Enable transmit, BRGH = 1
'Declare Hserial_Clear  = On  ; Clear overflow automatically
'Declare Hserial_SPBRG  = 64  ; Baud Rate Generator Low Byte Value
'SPBRGH                 = 3   ; Baud Rate Generator High Byte Value
'
'Calculated Baudrate = 19208 @ Xtal 64MHz, Error = 0,04%
    $if _xtal = 64
    RCSTA     = 144 ' Enable continuous receive
    TXSTA     = 36  ' Enable transmit, BRGH = 1
    SPBRG     = 64  ' Baud Rate Generator Low Byte Value
    SPBRGH    = 3   ' Baud Rate Generator High Byte Value
    BAUDCON.3 = 1   ' Enable the 16 bit Baud Rate Generator
    $endif
'/------------------------------------------------------------------------------
' Configure USART1:
' The USART1 could be configurated by the Positron Code (OR Calculated by EUSARTCALC)
'Calculated Baudrate = 19194 @ Xtal 40MHz, Error = -0,03%
'Declare Hserial_RCSTA  = 144 ; Enable continuous receive
'Declare Hserial_TXSTA  = 36  ; Enable transmit, BRGH = 1
'Declare Hserial_Clear  = On  ; Clear overflow automatically
'Declare Hserial_SPBRG  = 8   ; Baud Rate Generator Low Byte Value
'SPBRGH                 = 2   ; Baud Rate Generator High Byte Value
'BAUDCON.3              = 1   ; Enable the 16 bit Baud Rate Generator
'
'Calculated Baudrate = 19194 @ Xtal 40MHz, Error = -0,03%
    $if _xtal = 40
    RCSTA     = 144 ' Enable continuous receive
    TXSTA     = 36  ' Enable transmit, BRGH = 1
    SPBRG     = 8   ' Baud Rate Generator Low Byte Value
    SPBRGH    = 2   ' Baud Rate Generator High Byte Value
    BAUDCON.3 = 1   ' Enable the 16 bit Baud Rate Generator
    $endif
'/------------------------------------------------------------------------------
;Calculated Baudrate = 19185 @ Xtal 32MHz, Error = -0,08%
'Declare Hserial_RCSTA  = 144 ; Enable continuous receive
'Declare Hserial_TXSTA  = 36  ; Enable transmit, BRGH = 1
'Declare Hserial_Clear  = On  ; Clear overflow automatically
'Declare Hserial_SPBRG  = 160 ; Baud Rate Generator Low Byte Value
'SPBRGH                 = 1   ; Baud Rate Generator High Byte Value
'BAUDCON.3              = 1   ; Enable the 16 bit Baud Rate Generator
'
'Calculated Baudrate = 19185 @ Xtal 32MHz, Error = -0,08%
    $if _xtal = 32
    RCSTA     = %10010000   ' Enable continuous receive
    TXSTA     = %00100100   ' Enable transmit, BRGH = 1
    SPBRG     = %10100000   ' Baud Rate Generator Low Byte Value
    SPBRGH    = 1           ' Baud Rate Generator High Byte Value
    BAUDCON.3 = 1           ' Enable the 16 bit Baud Rate Generator
    $endif
'
'    $if _xtal = 32
'    SPBRG = 25
'    TXSTA = 32
'    RCSTA = 144
'    $endif
'--------------------------------------------------------------------------------------------
' Configure USART1:
'Calculated Baudrate = 19231 @ Xtal 20MHz, Error = 0,16%
'Declare Hserial_RCSTA = 144 ; Enable continuous receive
'Declare Hserial_TXSTA = 36  ; Enable transmit, BRGH = 1
'Declare Hserial_SPBRG = 64  ; Baud Rate Generator Value
'Declare Hserial_Clear = On  ; Clear overflow automatically
'BAUDCON.3             = 0   ; Disable the 16 bit Baud Rate Generator
'
'Calculated Baudrate = 19231 @ Xtal 20MHz, Error = 0,16%
    $if _xtal = 20
    RCSTA = 144 ; Enable continuous receive
    TXSTA = 36  ; Enable transmit, BRGH = 1
    SPBRG = 64  ; Baud Rate Generator Value
    BAUDCON.3 = 0   ; Disable the 16 bit Baud Rate Generator
    $endif
'/------------------------------------------------------------------------------
    LATC = 0
'    PinOutput PORTC.6
'    PinInput PORTC.7   
'   TX1 => RC6
'   RX1 => RC7
' for optionnal auto-reset: RTS pin from PC -> MCLR pin on PIC. RTS USB/UART Module => MCLR
'-------------------------------------------------------------------------------
' Wait for computer response:
    GoSub ReceiveByteInto_WREG                      ' Receive a byte from USART1 into WREG
    WREG = RCREG1                                   ' Place the read data into WREG
    If WREG <> $C1 Then GoTo Bootloader_Exit        ' Expect $C1
    TXREG1 = cIdTypePIC                             ' Send the microcontroller type to the PC application
    Repeat :
        #ifdef Watchdog_req
        Clrwdt                                      ' Clear the watchdog timer within the loop
        #endif   
    Until PIR1Bits_TX1IF = False
'
    F_DisableWrite = False                          ' Clear the flag for the workaround
    Do                                              ' Create a loop
        WREG = "C"                                  ' Everything OK so send the character "C" to the PC application
ContinueLoop:
        TXREG1 = WREG                               ' Transmit the contents of WREG
        Repeat 
            #ifdef Watchdog_req
            Clrwdt                                  ' Clear the watchdog timer within the loop
            #endif       
        Until PIR1Bits_TX1IF = False                ' Wait until the byte is sent
        bCRC = 0                                    ' Clear the CRC value
        GoSub ReceiveByteInto_WREG                  ' Receive the Upper address
        TBLPTRU = WREG
        bFlags = WREG                               ' For EEPROM and Config
        GoSub ReceiveByteInto_WREG                  ' Receive the High address
        TBLPTRH = WREG
        $if (cEepromSize = 1024)
        EEADRH = WREG                     '(for EEPROM case) ' High address of EEPROM
        $endif
'
        GoSub ReceiveByteInto_WREG                  ' Receive the Low address
        TBLPTRL = WREG
        EEADR = WREG                     '/ (for EEPROM case) ' Low address of EEPROM
'
        GoSub ReceiveByteInto_WREG                  ' Receive the byte count required for writing
        bByteCount = WREG                           ' Place it into variable bByteCount
'
        '/----------------------------------------------------------------------
        '/ Workaround because the version V0.14.6.5 of the Tiny Bootloader fails.
        If wTBLPTR > (cMax_Flash - BootloaderBytes - 1) Then
            If wTBLPTR < (cMax_Flash + 1) Then
                F_DisableWrite = True   
            EndIf
        EndIf       
        '/----------------------------------------------------------------------
        wFSR0 = AddressOf(bWriteBuffer)             ' FSR0L\H now hold Buffer Beginning address
        Tblrd*-                                     ' Read from flash memory with auto decrement of address in TBLPTR
RcvOct:
        GoSub ReceiveByteInto_WREG                  ' Receive the type of write
        POSTINC0 = WREG                             ' For Config memory
        EEDATA = WREG                               ' For EEPROM
        TABLAT = WREG                               ' For Flash memory
        If F_DisableWrite = 0 Then
            Tblwt+*                                 ' Write to flash memory with auto increment of address
        EndIf
        Decfsz  bByteCount,F                        ' Loop for the amount of bytes to receive
        Bra RcvOct
'
        F_DisableWrite = False                      ' Enable Write again
        GoSub ReceiveByteInto_WREG                  ' Get CRC
CRC_Failed:                                         ' CRC failed
        WREG = "N"                                  ' Get ready to send the character "N"
        If STATUSbits_Z = 0 Then GoTo ContinueLoop
'
        If tConfigWrite = True Then                 ' Skip if not writing to Config
            GoTo Write_Config                       ' Write Config
        EndIf
        WREG = (1 << cWREN)                         ' Setup for EEPROM write
        If tEepromWrite = False Then                ' Is it an EEPROM write?
            GoSub SetupEraseBlock                   ' No. So setup to erase the block of flash memory
        EndIf
        GoSub SetupWriteByte
        Continue
        '
        ' Write the configuration fuses (if required)
        '
Write_Config:
        Dec FSR0L                                   ' FSR0 = FSR0 - 1
        TABLAT = INDF0                              ' Load TABLAT
'        WREG = ((1 << cNVMREG1) | (1 << cNVMREG0) | (1 << cWREN))  ' Setup for Config memory
        WREG = ((1 << cEEPGD) | (1 << cCFGS) | (1 << cWREN))  ' Setup for Config memory
        GoSub SetupWriteByte
        Tblrd*-                                     ' Write to memory and auto decrement address in TBLPTRL\H
        Tstfsz FSR0L                                ' FSR0L = 0?
        GoTo Write_Config                           ' \ Loop until FSR0L = 0
    Loop                                            ' /
'===============================================================================
' Subroutines
'
SetupEraseBlock:
    WREG = %10010100                            ' Setup Erase Program memory; EEPGD, FREE, WREN = 1, CFGS = 0
'
SetupWriteByte:
EECON1 = WREG
    EECON2 = $55                                ' \ Unlock memory writes
    EECON2 = $AA                                ' /
    EECON1Bits_WR = True
    Repeat                                      '/ Waiting ubtil the byte is written. 
        #ifdef Watchdog_req
        Clrwdt
        #endif     
    Until EECON1Bits_WR = False
    Retlw ((1 << cEEPGD) | (1 << cWREN))        ' Setup for writes in WREG when returned: Allows write cycles to Flash program
'-------------------------------------------------------------------------------
ReceiveByteInto_WREG:
'    #ifdef Watchdog_req
'    Clrwdt                                      ' Clear the watchdog timer
'    #endif     
'                                   
'    bTimeoutCounter1 = ((_xtal / 2) + 1)        ' For 20MHz => 11 => 1 second delay
    bTimeoutCounter1 = _xtal                     ' For 32Mhz => 32
Repeat1:
'    #ifdef Watchdog_req
'    Clrwdt
'    #endif
    bTimeoutCounter2 = 0
Repeat2:
'    #ifdef Watchdog_req
'    Clrwdt
'    #endif
    bTimeoutCounter3 = 0
Repeat3:
    #ifdef Watchdog_req
    Clrwdt
    #endif
    If PIR1Bits_RC1IF = 0 Then GoTo NotReceivedYet  ' Receive a byte from USART1
    WREG = RCREG1                                   ' Place the read data into WREG
    bCRC = bCRC + WREG                              ' Calculate CRC
    Return
'-------------------------------------------------------------------------------
NotReceivedYet:
'    #ifdef Watchdog_req
'    Clrwdt
'    #endif                                      ' Clear the watchdog timer within the inner loop
Decfsz bTimeoutCounter3,F
Bra Repeat3
Decfsz bTimeoutCounter2,F
Bra Repeat2
Decfsz bTimeoutCounter1,F
Bra Repeat1
'
'Clear the first position of the Stack because exit of a Gosub routine without Return
STKPTR = 0 'Stack Address pointer 0, last pointer
TOSU = 0 'Clear Stack
TOSH = 0 'Clear Stack
TOSL = 0 'Clear Stack
'
' Timeout
'
Bootloader_Exit:
    EECON1 = 0                  ' Deactivate EECON
'    RCSTA1bits_SPEN = False     ' Deactivate USART1
    RCSTA1 = 0                  ' Deactivate USART1
'
    GoTo (cBootloader_Address)  ' Jump to the start of the user program
'///////////////////////////////////////////////////////////////////////////////
' Setup the fuses to use the external Xtal + PLL on a PIC18F25K22.
'
Config_Start
    Debug = Off       ' Background debugger disabled' RB6 and RB7 configured as general purpose I/O pins
    $ifdef _Watchdog_
    WDTEN = On        ' WDT always enabled
    $else
    WDTEN = OFF       ' WDT disabled (control is placed On SWDTEN Bit)
    $endif
    FOSC = HSMP       ' HS oscillator (low power 4 to 16 MHz)
    PLLCFG = On       ' Oscillator multiplied by 4
    XINST = Off       ' Extra Instruction Set Disabled
    FCMEN = On        ' Fail-Safe Clock Monitor enabled
    IESO = Off        ' Oscillator Switchover mode disabled
    PWRTEN = On       ' PWRT
    BOREN = On        ' Brown-out Reset enabled and controlled by software (SBOREN is enabled)
    BORV = 190        ' VBOR set to 1.9 V nominal
    WDTPS = 128       ' 1:128
    MCLRE = EXTMCLR   ' MCLR pin enabled, RE3 input pin disabled
    HFOFST = Off      ' The system clock is held off until the HF-INTOSC is stable.
    PRICLKEN = On     ' Primary clock enabled
    PBADEN = Off      ' PORTB<4:0> pins are configured as digital I/O on Reset
    CCP2MX = PORTC1   ' CCP2 input/output is multiplexed with RC1
    CCP3MX = PORTB5   ' P3A/CCP3 input/output is multiplexed with RB5
    T3CMX = PORTC0    ' T3CKI is on RC0
    P2BMX = PORTB5    ' P2B is on RB5
    STVREN = On       ' Stack full/underflow will cause Reset
    LVP = OFF         ' Single-Supply ICSP enabled
    Cp0 = Off         ' Block 0 (000800-001FFFh) not code-protected
    CP1 = Off         ' Block 1 (002000-003FFFh) not code-protected
    CPB = Off         ' Boot block (000000-0007FFh) not code-protected
    CPD = Off         ' Data EEPROM not code-protected
    WRT0 = Off        ' Block 0 (000800-001FFFh) not write-protected
    WRT1 = Off        ' Block 1 (002000-003FFFh) not write-protected
    WRTB = Off        ' Boot block (000000-0007FFh) not write-protected
    WRTC = Off        ' Configuration registers (300000-3000FFh) not write-protected
    WRTD = Off        ' Data EEPROM not write-protected
    EBTR0 = Off       ' Block 0 (000800-001FFFh) not protected from table reads executed in other blocks
    EBTR1 = Off       ' Block 1 (002000-003FFFh) not protected from table reads executed in other blocks
    EBTRB = Off       ' Boot block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End
The bootloader seems to work like a charm.
73's de EA3AGV

AlbertoFS

#13
But It is not possible to write code in the block before the bootloader.
'/ Blocks PIC18F25K22 =>  Address 64 x 512 = 32768
'/ Blocks Bootloader Bytes => 64 x 5 = 320 => Block number 507 => address 32448
'/ Code at block 506 x 64 => Address 32384 NO CODE WRITTEN (Erased)
'/ Code at block 505 x 64 => Address 32320 CODE WRITTEN OK

This is how this bootloader works, keep in mind.

Modification:
I put the clear flag after Do...
    Do                                              ' Create a loop
        F_DisableWrite = False                      ' Clear the flag for the workaround
        WREG = "C"                                  ' Everything OK so send the character "C" to the PC application
ContinueLoop:
73's de EA3AGV

AlbertoFS

#14
It is better to avoid erasing the bootloader blocks when there is a wrong address.
It seems to work.
'****************************************************************
'*  Name    : Tynibld18F25K22-ExtXtalMhzPLL-19200.bas           *
'*  Author  : [Les Johnson]                                     *
'*  Notice  : Copyright (c) 2025 []                             *
'*          : All Rights Reserved                               *
'*  Date    : 29/05/2025                                        *
'*  Version : 1.0                                               *
'*  Notes   : Modified by  A. Freixanet                         *
'*          :                                                   *
'****************************************************************
'
' Multi-Tiny+ Serial Bootloader for a PIC18F2XK22 device.
' Operates with a 20/32/64MHz external oscillator (Xtal) and a Baudrate of 19200.
'
' Written by Les Johnson for the Positron8 BASIC compiler.
'
'  The above 8 lines can be changed and buid a bootloader for the desired frequency (and PIC type)
'
' +---------+--------+------------+------------+-------------+-------------+-----------+--------+------+
' |IdTypePIC| Device | Write_Page | Erase_Page |   TX1/TX2   |   RX1/RX2   | max_flash | EEPROM | PDIP |
' +---------+--------+------------+------------+-------------+-------------+-----------+--------+------+
' |   0x61  |18F23K22|  32 words  |  32 words  |C6(17)/B6(27)|C7(18)/B7(28)|   0x2000  |   256  |  28  | - NOT TESTED
' |   0x61  |18F43K22|  32 words  |  32 words  |C6(25)/D6(29)|C7(26)/D7(30)|   0x2000  |   256  |  40  | - NOT TESTED
' | For PicCodes.ini: $61, C, 18F w/8KB Flash & 256B EE, $2000, $100, 320, 64,                         |
' |----------------------------------------------------------------------------------------------------|
' |   0x62  |18F24K22|  32 words  |  32 words  |C6(17)/B6(27)|C7(18)/B7(28)|   0x4000  |   256  |  28  | - NOT TESTED
' |   0x62  |18F44K22|  32 words  |  32 words  |C6(25)/D6(29)|C7(26)/D7(30)|   0x4000  |   256  |  40  | - NOT TESTED
' | For PicCodes.ini: $62, C, 18F w/16KB Flash & 256B EE, $4000, $100, 320, 64,                        |
' |----------------------------------------------------------------------------------------------------|
' |   0x64  |18F25K22|  32 words  |  32 words  |C6(17)/B6(27)|C7(18)/B7(28)|   0x8000  |   256  |  28  | TESTED
' |   0x64  |18F45K22|  32 words  |  32 words  |C6(25)/D6(29)|C7(26)/D7(30)|   0x8000  |   256  |  40  | - NOT TESTED
' | For PicCodes.ini: $64, C, 18F w/32KB Flash & 256B EE, $8000, $0100, 320, 64,                       |
' |----------------------------------------------------------------------------------------------------|
' |   0x66  |18F26K22|  32 words  |  32 words  |C6(17)/B6(27)|C7(18)/B7(28)|  0x10000  |  1024  |  28  | TESTED
' |   0x66  |18F46K22|  32 words  |  32 words  |C6(25)/D6(29)|C7(26)/D7(30)|  0x10000  |  1024  |  40  | - NOT TESTED
' | For PicCodes.ini: $66, C, 18F w/64KB Flash & 1024B EE, $10000, $0400, 320, 64,                     |
' +---------+--------+------------+------------+-------------+-------------+-----------+--------+------+ 
' Info taken from the Piccodes.ini
' device's line structure:
' ID code (as defined in source firmware!), device family, device name, max flash memory (bytes),EE (bytes), bootloader size (bytes), transfert block size (bytes),
'
' The '$' symbol is for hexadecimal numbers.
' device's line structure:
' $64, C, 18F w/32KB flash & 256B EE, $8000, $100, default, default,
' OR
' $64, C, 18F w/32KB Flash & 256B EE, $8000, $100, 200, 64,
'
' FOR PIC1F25K22
' ID code (as defined in source firmware!): 0x64
' device family: C
' device name: 18F
' max flash memory (bytes): w/32KB Flash => 0x8000
' EE (bytes): $0100
' bootloader size (bytes): 320
' transfert block size (bytes): 64
'
' Watchdog => PROGRAM MEMORY BYTES USED in this CODE: 289 (by file.lst) (< 5 blocks of 64 Bytes => 320)
' Then the Bootloader size (bytes) is: 320
' For this PIC18F25K22 the erase block size is 64 bytes.
'/------------------------------------------------------------------------------
' For this bootloader: PIC18F25K22
' $64, C, 18F w/32KB Flash & 256B EE, $8000, $100, 320, 64,
' Open the Piccodes.ini with the NotePad.
' Disabled the $64, C,... line with a ;  (Not delete)
' Copy the new line:
' $64, C, 18F w/32KB Flash & 256B EE, $8000, $0100, 320, 64,

' Write a new line in Piccodes.in line => $64 in C:/TinyMultiBootLoaderPlus
' Disable the line $64 by a ;
'
' Copy this new line for PIC18F25K22 using a simple NOTE PAD   
' $64, C, 18F w/32KB Flash & 256B EE, $8000, $0100, 320, 64,     
' And Save...
' For all PIC18FXXK22 copy & insert all the new lines from the table above.

'
' THIS BOOTLOADER IS WRITTEN TO THE BOTTOM OF ROM MEMORY:
'///////////////////////////////////////////////////////////////////////////////
'    Device = 18F23K22
'    Device = 18F24K22
    Device = 18F25K22
'    Device = 18F26K22
'    Device = 18F43K22
'    Device = 18F44K22
'    Device = 18F45K22
'    Device = 18F46K22
'-------------------------------------------------------------------------------
    Declare Xtal = 32                               ' Tell the compiler what frequency the device is operating at (in MHz)
'
'    Declare Access_Upper_64K = True                 ' Set to true because the 18F27K42 device has 128K of flash memory
    Declare Library_Core = Off                      ' Disable the compiler's Library core and any extra items the compiler adds to a user's program
'
'/  Enable/Disable the function as you want to include to the USER Program. (For Config Fuses & Interrupts)
    $define _Watchdog_
'-------------------------------------------------------------------------------
    $ifdef _Watchdog_
    Declare Watchdog = On 
    $else
    Declare Watchdog = Off
    $endif
'
    $if _device = _18F23K22 Or _device = _18F43K22
    $define cIdTypePIC $61                                 ; The microcntroller type for the PC application
    $endif
    $if _device = _18F24K22 Or _device = _18F44K22
    $define cIdTypePIC $62                                 ; The microcntroller type for the PC application
    $endif 
    $if _device = _18F25K22 Or _device = _18F45K22
    $define cIdTypePIC $64                                 ; The microcntroller type for the PC application
    $endif
    $if _device = _18F26K22 Or _device = _18F46K22
    $define cIdTypePIC $66                                 ; The microcntroller type for the PC application
    $endif
'
    $define cBootloaderBytes 320                                ; 5 Blocks of 64 Bytes (64X5=320Bytes)
    $define cMax_Flash      _code                               ; The amount of flash memory on the device (in bytes)
    $define cWriteBlockSize _block                              ; (words _block = 32) The size in words of the block write to flash memory
    $define cEepromSize     _eeprom                             ; Size of EEPROM (256 or 1024)
    $define cBootloader_Address $eval (cMax_Flash - cBootloaderBytes)        ; 5 x 64 blocks for bootlooader bytes long reserved at the top of flash for the bootloader
    $define cUSerCode_Address 4                                 ; See position of "goto _compiler_main_start_" of the main code.
'
'/ Memory Erase PIC18F25K22 = 64 Bytes / Block
'/ Memory Write PIC18F25K22 = 64 Bytes / Block
'/ EEPROM Memory PIC18F25K22 = 256 Bytes
'/ ROM instruction 16384 x2 = 32768 Bytes
'/ The Bootloader is placed in the bottom of ROM memory:       
'
' Bit names of the EECONx SFRs
'
    $define cRD    0
    $define cWR    1
    $define cWREN  2
    $define cWRERR 3
    $define cFREE  4
    $define cCFGS  6
    $define cEEPGD 7
'
' Create variables for the bootloader
' Check if the Write Blocks are same value of the Erase Blocks.
    Dim bWriteBuffer[_block * 2] As Byte            ' Buffer used for holding data
    Dim bCRC             As Byte                    ' Holds the CRC value
    Dim bByteCount       As Byte                    ' Holds the amount of bytes for a block flash write
    Dim bBldFlags        As Byte                    ' Holds the requirement of which memory area to write
    Dim tEepromWrite     As bBldFlags.6             ' Set if eeprom memory is to be written
    Dim tConfigWrite     As bBldFlags.7             ' Set if config memory is to be written
'
    Dim bTimeoutCounter1 As Byte                    ' \
    Dim bTimeoutCounter2 As Byte                    ' |  Timeout counter for receiving a byte
    Dim bTimeoutCounter3 As Byte                    ' /
'
    Dim tCodeFlags As Byte
    Dim tDisableWrite As tCodeFlags.0
'
    Dim wFSR0 As FSR0L.Word                         ' Convert 8-bit SFRs FSR0L\H into a 16-bit SFR
    Dim wTBLPTR As TBLPTRL.Long
'/------------------------------------------------------------------------------
$ifndef False
    $define False 0
$endif
$ifndef True
    $define True 1
$endif
'///////////////////////////////////////////////////////////////////////////////
' Reset
'
' In the user code it must written:
' Declare Bootloader = Off
'
'///////////////////////////////////////////////////////////////////////////////
    GoTo BootloaderStart         ' Jump to the start of the bootloader
'
Declare Warnings = Off
' Set the high flash memory address where the bootloader sits   
'
    Org cBootloader_Address     
    GoTo (cUSerCode_Address)     ' Jump to the start of the user program     
'    Nop                       
'    Nop                       
    Nop                       
    Nop                       
'
'Org cBootloader_Address + 4
Declare Warnings = On
BootloaderStart:
    ANSELC = 0                          ' Set PORTC to digital
'
' Set the microcontroller to external OSC + PLL operation fuse
    OSCCON = 0                          '/Primary clock (determined by FOSC<3:0> in CONFIG1H).
    OSCCON2 = %00000100                 '/Primary Oscillator Drive Circuit ON       
'
'/ Take care of Watchdog
    WDTCONBits_SWDTEN = False       '/ If WDT is turned off
    #ifdef Watchdog_req
    Clrwdt                          ' WDT is turned always on when Declare Watchdog = On
    #endif
'
'/------------------------------------------------------------------------------
    PMD0Bits_UART1MD = False        '/ Module URAT1 is enabled, Clock Source is connected, module draws digital power
'
' Configure USART1:
' The USART1 could be configurated by the Positron Code (OR Calculated by EUSARTCALC)
'Calculated Baudrate = 19208 @ Xtal 64MHz, Error = 0,04%
'
'Declare Hserial_RCSTA  = 144 ; Enable continuous receive
'Declare Hserial_TXSTA  = 36  ; Enable transmit, BRGH = 1
'Declare Hserial_Clear  = On  ; Clear overflow automatically
'Declare Hserial_SPBRG  = 64  ; Baud Rate Generator Low Byte Value
'SPBRGH                 = 3   ; Baud Rate Generator High Byte Value
'
'Calculated Baudrate = 19208 @ Xtal 64MHz, Error = 0,04%
    $if _xtal = 64
    RCSTA1     = 144    ' Enable continuous receive
    TXSTA1     = 36     ' Enable transmit, BRGH = 1
    SPBRG1     = 64     ' Baud Rate Generator Low Byte Value
    SPBRG1H    = 3      ' Baud Rate Generator High Byte Value
    BAUDCON1.3 = 1      ' Enable the 16 bit Baud Rate Generator
    $endif
'/------------------------------------------------------------------------------
' Configure USART1:
' The USART1 could be configurated by the Positron Code (OR Calculated by EUSARTCALC)
'Calculated Baudrate = 19194 @ Xtal 40MHz, Error = -0,03%
'Declare Hserial_RCSTA  = 144 ; Enable continuous receive
'Declare Hserial_TXSTA  = 36  ; Enable transmit, BRGH = 1
'Declare Hserial_Clear  = On  ; Clear overflow automatically
'Declare Hserial_SPBRG  = 8   ; Baud Rate Generator Low Byte Value
'SPBRGH                 = 2   ; Baud Rate Generator High Byte Value
'BAUDCON.3              = 1   ; Enable the 16 bit Baud Rate Generator
'
'Calculated Baudrate = 19194 @ Xtal 40MHz, Error = -0,03%
    $if _xtal = 40
    RCSTA1     = 144    ' Enable continuous receive
    TXSTA1     = 36     ' Enable transmit, BRGH = 1
    SPBRG1     = 8      ' Baud Rate Generator Low Byte Value
    SPBRG1H    = 2      ' Baud Rate Generator High Byte Value
    BAUDCON1.3 = 1      ' Enable the 16 bit Baud Rate Generator
    $endif
'/------------------------------------------------------------------------------
;Calculated Baudrate = 19185 @ Xtal 32MHz, Error = -0,08%
'Declare Hserial_RCSTA  = 144 ; Enable continuous receive
'Declare Hserial_TXSTA  = 36  ; Enable transmit, BRGH = 1
'Declare Hserial_Clear  = On  ; Clear overflow automatically
'Declare Hserial_SPBRG  = 160 ; Baud Rate Generator Low Byte Value
'SPBRGH                 = 1   ; Baud Rate Generator High Byte Value
'BAUDCON.3              = 1   ; Enable the 16 bit Baud Rate Generator
'
'Calculated Baudrate = 19185 @ Xtal 32MHz, Error = -0,08%
    $if _xtal = 32
    RCSTA1     = %10010000   ' Enable continuous receive
    TXSTA1     = %00100100   ' Enable transmit, BRGH = 1
    SPBRG1     = %10100000   ' Baud Rate Generator Low Byte Value
    SPBRG1H    = 1           ' Baud Rate Generator High Byte Value
    BAUDCON1.3 = 1           ' Enable the 16 bit Baud Rate Generator
    $endif
'-------------------------------------------------------------------------------
' Configure USART1:
'Calculated Baudrate = 19231 @ Xtal 20MHz, Error = 0,16%
'Declare Hserial_RCSTA = 144 ; Enable continuous receive
'Declare Hserial_TXSTA = 36  ; Enable transmit, BRGH = 1
'Declare Hserial_SPBRG = 64  ; Baud Rate Generator Value
'Declare Hserial_Clear = On  ; Clear overflow automatically
'BAUDCON.3             = 0   ; Disable the 16 bit Baud Rate Generator
'
'Calculated Baudrate = 19231 @ Xtal 20MHz, Error = 0,16%
    $if _xtal = 20
    RCSTA1 = 144        ' Enable continuous receive
    TXSTA1 = 36         ' Enable transmit, BRGH = 1
    SPBRG1 = 64         ' Baud Rate Generator Value
    SPBRG1H = 0         ' Baud Rate Generator High Byte Value
    BAUDCON1.3 = 0      ' Disable the 16 bit Baud Rate Generator
    $endif
'-------------------------------------------------------------------------------
'Calculated Baudrate = 19231 @ Xtal 16MHz, Error = 0,16%
'Declare Hserial_RCSTA = 144 ; Enable continuous receive
'Declare Hserial_TXSTA = 32  ; Enable transmit, BRGH = 0
'Declare Hserial_SPBRG = 12  ; Baud Rate Generator Value
'Declare Hserial_Clear = On  ; Clear overflow automatically
'BAUDCON.3             = 0   ; Disable the 16 bit Baud Rate Generator
'
'Calculated Baudrate = 19231 @ Xtal 16MHz, Error = 0,16%
    $if _xtal = 16
    RCSTA1 = 144        ' Enable continuous receive
    TXSTA1 = 32         ' Enable transmit, BRGH = 0
    SPBRG1 = 12         ' Baud Rate Generator Value
    SPBRG1H = 0         ' Baud Rate Generator High Byte Value
    BAUDCON1.3 = 0      ' Disable the 16 bit Baud Rate Generator
    $endif
'/------------------------------------------------------------------------------
'   TX1 => RC6
'   RX1 => RC7
' For optionnal auto-reset: RTS pin from PC -> MCLR pin on PIC. RTS USB/UART Module => MCLR
'-------------------------------------------------------------------------------
' Wait for computer response:
    GoSub ReceiveByteInto_WREG                      ' Receive a byte from USART1 into WREG
    WREG = RCREG1                                   ' Place the read data into WREG
    If WREG <> $C1 Then GoTo Bootloader_Exit        ' Expect $C1
    TXREG1 = cIdTypePIC                             ' Send the microcontroller type to the PC application
    Repeat :
        #ifdef Watchdog_req
        Clrwdt                                      ' Clear the watchdog timer within the loop
        #endif   
    Until PIR1Bits_TX1IF = False
'
    Do                                              ' Create a loop
        tDisableWrite = False                       ' Clear the flag for the workaround
        WREG = "C"                                  ' Everything OK so send the character "C" to the PC application
ContinueLoop:
        TXREG1 = WREG                               ' Transmit the contents of WREG
        Repeat 
            #ifdef Watchdog_req
            Clrwdt                                  ' Clear the watchdog timer within the loop
            #endif       
        Until PIR1Bits_TX1IF = False                ' Wait until the byte is sent
        bCRC = 0                                    ' Clear the CRC value
        GoSub ReceiveByteInto_WREG                  ' Receive the Upper address
        TBLPTRU = WREG
        bBldFlags = WREG                               ' For EEPROM and Config & Flags in bit6, Bit7
'
        GoSub ReceiveByteInto_WREG                  ' Receive the High address
        TBLPTRH = WREG
        $if (cEepromSize = 1024)
        EEADRH = WREG                     '(for EEPROM case) ' High address of EEPROM
        $endif
'
        GoSub ReceiveByteInto_WREG                  ' Receive the Low address
        TBLPTRL = WREG
        EEADR = WREG                     '/ (for EEPROM case) ' Low address of EEPROM
'
        GoSub ReceiveByteInto_WREG                  ' Receive the byte count required for writing
        bByteCount = WREG                           ' Place it into variable bByteCount
'
        '/----------------------------------------------------------------------
        '/ Workaround because the version V0.14.6.5 of the Tiny Bootloader fails.
        '/ This code protect the Bootloader for self erasing.
        '/ Is it a block to erase?
        WREG = bBldFlags & %00000011                   
        If WREG = 0 Then                            '/ Is it erase the Bootloader block address?
            If wTBLPTR > (cMax_Flash - cBootloaderBytes - 1) Then
                If wTBLPTR < (cMax_Flash + 1) Then
                    tDisableWrite = True   
                EndIf
            EndIf       
        EndIf
        '/----------------------------------------------------------------------
        wFSR0 = AddressOf(bWriteBuffer)             ' FSR0L\H now hold Buffer Beginning address
        Tblrd*-                                     ' Read from flash memory with auto decrement of address in TBLPTR
RcvOct:
        GoSub ReceiveByteInto_WREG                  ' Receive the type of write
        POSTINC0 = WREG                             ' For Config memory
        EEDATA = WREG                               ' For EEPROM
        TABLAT = WREG                               ' For Flash memory
        If tDisableWrite = False Then               '/ = False do not write in the Bootloader block address
            Tblwt+*                                 ' Write to flash memory with auto increment of address
        EndIf
        Decfsz  bByteCount,F                        ' Loop for the amount of bytes to receive
        Bra RcvOct
'
        GoSub ReceiveByteInto_WREG                  ' Get CRC
CRC_Failed:                                         ' CRC failed
        WREG = "N"                                  ' Get ready to send the character "N"
        If STATUSbits_Z = 0 Then GoTo ContinueLoop
'
        If tConfigWrite = True Then                 ' Skip if not writing to Config
            GoTo Write_Config                       ' Write Config
        EndIf
'
        WREG = (1 << cWREN)                         ' Setup for EEPROM write
'        If tEepromWrite = False Then                ' Is it an EEPROM write?
'            GoSub SetupEraseBlock                   ' No. So setup to erase the block of flash memory
'        EndIf
'        GoSub SetupWriteByte                        ' Write EEPROM or Flash memory
'        Continue
'
        If tEepromWrite = False Then                ' Is it an EEPROM write?
            If tDisableWrite = False Then
                GoSub SetupEraseBlock               ' No. So setup to erase the block of flash memory
            EndIf
        EndIf
        If tDisableWrite = False Then               '/ = False do not write in the Bootloader block address
            GoSub SetupWriteByte                    ' Write EEPROM or Flash memory
        EndIf
        Continue
        '
        ' Write the configuration fuses (if required)
        '
Write_Config:
        Dec FSR0L                                   ' FSR0 = FSR0 - 1
        TABLAT = INDF0                              ' Load TABLAT
        WREG = ((1 << cEEPGD) | (1 << cCFGS) | (1 << cWREN))  ' Setup for Config memory
        GoSub SetupWriteByte
        Tblrd*-                                     ' Write to memory and auto decrement address in TBLPTRL\H
        Tstfsz FSR0L                                ' FSR0L = 0?
        GoTo Write_Config                           ' \ Loop until FSR0L = 0
    Loop                                            ' /
'===============================================================================
' Subroutines
'
SetupEraseBlock:
    WREG = ((1 << cEEPGD) | (1 << cFREE) | (1 << cWREN))   
'    WREG = %10010100                           ' Setup Erase Program memory; EEPGD = 1, FREE = 1, WREN = 1, CFGS = 0
'
SetupWriteByte:
EECON1 = WREG
    EECON2 = $55                                ' \ Unlock memory writes
    EECON2 = $AA                                ' /
    EECON1Bits_WR = True
    Repeat                                      '/ Waiting ubtil the byte is written. 
        #ifdef Watchdog_req
        Clrwdt
        #endif     
    Until EECON1Bits_WR = False
    Retlw ((1 << cEEPGD) | (1 << cWREN))        ' Setup for writes in WREG when returned: Allows write cycles to Flash program
'-------------------------------------------------------------------------------
ReceiveByteInto_WREG:
'    bTimeoutCounter1 = ((_xtal / 2) + 1)        ' For 20MHz => 11 => 1 second delay
    bTimeoutCounter1 = _xtal                     ' For 32Mhz => 32
Repeat1:
    bTimeoutCounter2 = 0
Repeat2:
    bTimeoutCounter3 = 0
Repeat3:
    #ifdef Watchdog_req
    Clrwdt
    #endif
    If PIR1Bits_RC1IF = 0 Then GoTo NotReceivedYet  ' Receive a byte from USART1
    WREG = RCREG1                                   ' Place the read data into WREG
    bCRC = bCRC + WREG                              ' Calculate CRC
    Return
'-------------------------------------------------------------------------------
NotReceivedYet:
Decfsz bTimeoutCounter3,F
Bra Repeat3
Decfsz bTimeoutCounter2,F
Bra Repeat2
Decfsz bTimeoutCounter1,F
Bra Repeat1
'
'Clear the first position of the Stack because exit of a Gosub routine without Return
STKPTR = 0 'Stack Address pointer 0, last pointer
TOSU = 0 'Clear Stack
TOSH = 0 'Clear Stack
TOSL = 0 'Clear Stack
'
' Timeout exit
'
Bootloader_Exit:
    EECON1 = 0                  ' Deactivate EECON
'    RCSTA1bits_SPEN = False     ' Deactivate USART1
    RCSTA1 = 0                  ' Deactivate USART1
'
    GoTo (cBootloader_Address)  ' Jump to the start of the user program
'///////////////////////////////////////////////////////////////////////////////
' Setup the fuses to use the external Xtal + PLL on a PIC18F25K22.
'
$if _device = _18F25K22 Or _device = _18F24K22 Or _device = _18F26K22 Or _device = _18F23K22
Config_Start
    Debug = Off       ; Background debugger disabled' RB6 and RB7 configured as general purpose I/O pins
    $ifdef _Watchdog_
    WDTEN = On        ; WDT always enabled
    $else
    WDTEN = OFF       ; WDT disabled (control is placed On SWDTEN Bit)
    $endif
    FOSC = HSMP       ; HS oscillator (low power 4 to 16 MHz)
    PLLCFG = On       ; Oscillator multiplied by 4
    XINST = OFF       ; Extra Instruction Set Disabled
    FCMEN = On        ; Fail-Safe Clock Monitor enabled
    IESO = OFF        ; Oscillator Switchover mode disabled
    PWRTEN = On       ; Power up timer enabled
    BOREN = On        ; Brown-out Reset enabled and controlled by software (SBOREN is enabled)
    BORV = 190        ; VBOR set to 1.9 V nominal
    WDTPS = 128       ; 1:128
    MCLRE = EXTMCLR   ; MCLR pin enabled, RE3 input pin disabled
    HFOFST = OFF      ; The system clock is held off until the HF-INTOSC is stable.
    PRICLKEN = On     ; Primary clock enabled
    PBADEN = OFF      ; PORTB<4:0> pins are configured as digital I/O on Reset
    CCP2MX = PORTC1   ; CCP2 input/output is multiplexed with RC1
    CCP3MX = PORTB5   ; P3A/CCP3 input/output is multiplexed with RB5
    T3CMX = PORTC0    ; T3CKI is on RC0
    P2BMX = PORTB5    ; P2B is on RB5
    STVREN = On       ; Stack full/underflow will cause Reset
    LVP = OFF         ; Single-Supply ICSP enabled
    Cp0 = OFF         ; Block 0 (000800-001FFFh) not code-protected
    CP1 = OFF         ; Block 1 (002000-003FFFh) not code-protected
    CPB = OFF         ; Boot block (000000-0007FFh) not code-protected
    CPD = OFF         ; Data EEPROM not code-protected
    WRT0 = OFF        ; Block 0 (000800-001FFFh) not write-protected
    WRT1 = OFF        ; Block 1 (002000-003FFFh) not write-protected
    WRTB = OFF        ; Boot block (000000-0007FFh) not write-protected
    WRTC = OFF        ; Configuration registers (300000-3000FFh) not write-protected
    WRTD = OFF        ; Data EEPROM not write-protected
    EBTR0 = OFF       ; Block 0 (000800-001FFFh) not protected from table reads executed in other blocks
    EBTR1 = OFF       ; Block 1 (002000-003FFFh) not protected from table reads executed in other blocks
    EBTRB = OFF       ; Boot block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End
$endif
'/------------------------------------------------------------------------------
$if _device = _18F43K22  Or _device = _18F44K22 Or _device = _18F45K22 Or _device = _18F46K22
Config_Start
    Debug = off         ; Background debugger disabled' RB6 and RB7 configured as general purpose I/O pins
    $ifdef _Watchdog_
    WDTEN = On          ; WDT always enabled
    $else
    WDTEN = OFF         ; WDT disabled (control is placed On SWDTEN Bit)
    $endif
    FOSC = HSHP         ; HS oscillator (low power 4 to 16 MHz)
    PLLCFG = On         ; Oscillator multiplied by 4
    XINST = OFF         ; Extra Instruction Set Disabled
    FCMEN = On
    IESO = OFF          ; Oscillator Switchover mode disabled
    PWRTEN = On         ; Power up timer enabled
    BOREN = On          ; Brown-out Reset enabled and controlled by software (SBOREN is enabled)
    BORV = 190          ; VBOR set to 1.9 V nominal
    WDTPS = 128         ; 1:128
    MCLRE = EXTMCLR     ; MCLR pin enabled, RE3 input pin disabled
    HFOFST = OFF        ; The system clock is held off until the HF-INTOSC is stable.
    PRICLKEN = On       ; Primary clock enabled
    PBADEN = OFF        ; PORTB<4:0> pins are configured as digital I/O on Reset
    CCP2MX = PORTC1     ; CCP2 input/output is multiplexed with RC1
    CCP3MX = PORTB5     ; P3A/CCP3 input/output is multiplexed with RB5
    T3CMX = PORTC0      ; T3CKI is on RC0
    P2BMX=PORTD2        ; P2B is on RD2
    STVREN = On         ; Stack full/underflow will cause Reset
    LVP = OFF           ; Single-Supply ICSP enabled
    Cp0 = OFF           ; Block 0 (000800-001FFFh) not code-protected
    CP1 = OFF           ; Block 1 (002000-003FFFh) not code-protected
    CPB = OFF           ; Boot block (000000-0007FFh) not code-protected
    CPD = OFF           ; Data EEPROM not code-protected
    WRT0 = OFF          ; Block 0 (000800-001FFFh) not write-protected
    WRT1 = OFF          ; Block 1 (002000-003FFFh) not write-protected
    WRTB = OFF          ; Boot block (000000-0007FFh) not write-protected
    WRTC = OFF          ; Configuration registers (300000-3000FFh) not write-protected
    WRTD = OFF          ; Configuration registers (300000-3000FFh) not write-protected
    EBTR0 = OFF         ; Block 0 (000800-001FFFh) not protected from table reads executed in other blocks
    EBTR1 = OFF         ; Block 1 (002000-003FFFh) not protected from table reads executed in other blocks
    EBTRB = OFF         ; Boot block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End
$endif
73's de EA3AGV

Maxi

Hi Alberto, how can we use it or test it?

AlbertoFS

#16
Hi Maxi,
- I have used an Amicus18 board (equipped with a USB=>UART interface) and an ICSP connector.
- I have connected the DTR output of the USB/UART interface to the Reset pin of the PIC.
  On the Amicus18 board there is a jumper (welding) to solder for this purpose.

- Copy the last code I wrote in the The thread #14 in your IDE and compile with Positron compiler.
  And flash the code (.hex) with a programmer to the PIC18F25K22 via ICSP.

- In the User code you must write at the top: Declare Bootloader = Off
- This leaves the first line free for the bootloader to write "Goto BootloaderStart".
- The Config Fuses in the Bootloader code and the USER code must to be the same.
- Now you can compile the user code.

- Look for the application console from : https://sourceforge.net/projects/tinypicbootload/files/.
- Download the latest "TinyMultiBootloaderPlusIntaller(version V0.14.6.).exe
- Install this programm in your computer.

- Look for the Piccodes.ini file installed into the computer disc "C" into the folder "TinyMultiVootLoaderPlus".
  For me, I am working with Windows7.

- Make a copy of the Piccodes.ini file (always), in to another part.

- Open this file with a simple NotePad.
- Look for the line:
 $64, C, 18F w/32KB flash & 256B EE, $8000, $100, default, default,

- Disable this line with a ; like this. (NO DELETE)
 ;$64, C, 18F w/32KB flash & 256B EE, $8000, $100, default, default,

- Insert this new line and save the file.
 $64, C, 18F w/32KB Flash & 256B EE, $8000, $100, 320, 64,

- Now open the Bootloader application (the icon is like a chip).
- Connect your PCB to the PC via the USB port.
-1 Select the file (USER file .hex) in the firt line above "Browse).

-2 Goto Configuration.
   Above in the left "Force use of "Piccodes.ini file" YES
   Above in the right Browse the "Piccodes.ini" into the C:/ TinyMultiVootLoaderPlus.
   In "Reset Type": If you can use an automatic reset. Choose "DTR"
   If not, you must to make the reset manually. Make reset fisrt and press the button "Write Device"(less than 1 second).

-3 Main screen on the left:
   Choose Baud Rate 19200
   Choose the port (for me was COM5)
   You can use "Detected COM". Your PCB card must connected yet.

-4 In the down bar:
   Choose: Write Flash Program, Write EEPROM (if necessary), Erase EEPROM (If necessary)

-5 Try "Check Device" and make reset if it is manual.
   If error check your system.
   If success, You must have this result:

   Checking device
   Open COM5 serial port at 19200 Baud Rate
      Hardware DTR resetting device
      Device answer: 0x64 0x43='C'
      byte OK, now check ID-code and Family
         idCode = $64
         family = C
         description = 18F w/32KB Flash & 256B EE
         flash mem = 32768 bytes
         EEPROM mem = 256 bytes
         Bootloader size = 320 bytes
        Transfer block size = 64 bytes
   Device detected:18F w/32KB Flash & 256B EE
   Closed COM5 serial port
   TinyMultiBootLoader+ device check completed

Now the PC application and your PCB card is ready to send your USER code

-6 Press "Write Device" and make reset if it is manual.
   The result is:

   Checking device
   Open COM5 serial port at 19200 Baud Rate
      Hardware DTR resetting device (if DTR pin connector to pin reset of the PIC)
      Device answer: 0x64 0x43='C'
       byte OK, now check ID-code and Family
         idCode = $64
         family = C
         description = 18F w/32KB Flash & 256B EE
         flash mem = 32768 bytes
         EEPROM mem = 256 bytes
         Bootloader size = 320 bytes
         Transfer block size = 64 bytes
   Device detected:18F w/32KB Flash & 256B EE
   Source HEX file opened: D:\PROY_pendientes\PositronBootloader\Code\Freq-1.hex
   Uploading PROGMEM
   Uploading program memory
   Uploading EEPROM
   Upload was successful
   Closed COM5 serial port

It's not that difficult...
73's de EA3AGV

JonW

QuoteI uploaded it to the Tiny Bootloader site forum, but it just got ignored and forgotten.

Because it was better and there is no profit in "FREE".

Nice work, Alberto; maybe others will now create Free variants of the original for other MCU.