Operation of Luna bootloader
Summary of the contribution to the explanation of the boot loader functionality from the Luna-Forum.
A so-called bootloader is a small program that is executed beforce starting the user program, dependent on the fusebits of the controller. A boot loader is also stored in the same flash memory of the controller as the user program, but with the difference that it is placed just before the end of the flash memory on certain boundary addresses. The address limit is the einspringpunkt, the controller starts after a hardware reset if the Fusebit BOOTRST has been activated. Which of the usually four different border areas is started, determine the fusebits BOOTSZ0 and BOOTSZ1. The special thing about it is that a program from the boot loader section can writing flash pages (except for the area in which the boot loader itself is located).
Depending on the controller put the two fusebits BOOTSZ0 and BOOTSZ1 determine what the size should be the bootloader area. The size of the bootloader program here determined what size to choose. The available sizes vary between the different controllers, thus it is necessary to look up the values in the data sheet.
Is a boot loader on the controller uploaded, it looks in the flash memory of the controller then, for example like this:
S----------------------------------------|-----|xxxxx|xxx|x--e : : : : : : : +FIRSTBOOTSTART : : +SeCONDBOOTSTART : +-THIRDBOOTSTART +FOURTHBOOTSTART S = start of flash memory e = end of flash memory | = bootloader borders x = bootloader programm
If the boot loader is flashed normally, the controller starts from the set address, in this case, as in the screenshot above with activated Fusebit BOOTSZ1 THIRD BOOT START.
The Luna boot loader checks 10x with very short breaks if a command has arrived that puts him in the flash mode, otherwise the boot loader jumps via the command „ReSeT“ to address 0x0000 and thus to a stored there, the user program. If there is none to be found, the program counter moves through the entire flash to the bootloader where it is running again. This is because is written in flashing in the unused areas of the value 0xff, which is the machine instruction „nop“ (do nothing).
From now on his user program must be no longer with z.Bsp. Avrdude to upload the controller, otherwise the bootloader would be deleted / overwritten. It now loads the user program z.Bsp. the boot uploader to the controller. Once this is done, it looks in the store then like this:
Spppppppppppppppppppppppppp--------------|-----|xxxxx|xxx|x--e : : : : : : : +FIRSTBOOTSTART : : +SeCONDBOOTSTART : +-THIRDBOOTSTART +FOURTHBOOTSTART S = start of flash memory e = ende of flash memory | = bootloader borders x = bootloader programm p = user programm
If one wants to implement from the user program the function „Firmware supports reset command“ in the boat uploader, we must not jump to address 0x0000 (happens with Luna command reset) with the command to restart. This would even start only the user program again, nothing else. it is necessary for the „reset“ support in the boot uploader only to jump to the bootloader: jump THIRD BOOT START so this is also carried out.
Example
This example contains the boot loader, adapted for the Atmega168, as well as a matching user program. The controller board then requires a serial connection to the PC.
The necessary to be activated fuse bits are:
[x] BOOTRST
[x] BOOTSZ0
[x] BOOTSZ1
The execution start point (or bootloader address) in this case is then FOURTHBOOTSTART.
method
- Read fusebits and enable (checked) above fusebits.
- Write fusebits.
- Compile bootloader and upload into the controller.
- Test program only compile.
- boat uploader start, select compiled test program, set the baud rate and interface & Connect.
- With „Upload Flash“ upload the test program.
Files
- bootloader-m168.luna
'****************************************************************************** ' Author : rgf, avr@myluna.de, http://avr.myluna.de ' Title : bootloader example ' Last updated: 09.02.2015 ' Protocol : v1.0 ' Description : flash and eeprom write with crc check, retry and error handling ' Tools : Uploader software "bootUploader" http://avr.myluna.de '****************************************************************************** ' Compiler : LunaAVR 2015r1 or newer '****************************************************************************** avr.device = atmega168 avr.clock = 20000000 avr.stack = 64 #library "Library/Crc8.interface" '****************************************************************************** '*** CHANGE CODE START ADDRESS TO BOOT LOADER SECTION ************************* '****************************************************************************** 'Luna Bootloader requires 2 kb bootloader section avr.CodeStartAddr = nrww_start_addr const MAX_PAGES = nrww_start_addr/pagesize '****************************************************************************** uart.baud = 19200 uart.Recv.enable uart.Send.enable #define SLED as portd.6 'status led #define WLED as portd.7 'write action led SLED.mode = output,low 'status led WLED.mode = output,low 'write led '****************************************************************************** 'luna bootloader protocol (universal) '****************************************************************************** ' input commands const PROTO_RDY = 0x01 'access bootloader and keep alive when accessed const PROTO_CBV = 0x02 'read controller base values, answer: const PROTO_EFP = 0x04 'erase flash page, command: <word:pagenum>, answer: <ack> const PROTO_WFP = 0x05 'erase and write flash page, command: <word:pagenum><bytes:data><byte:crc8>, answer: <ack> const PROTO_EEP = 0x06 'erase eeprom page, command: <word:pagenum>, answer: <ack> const PROTO_WEP = 0x07 'erase and write eeprom page, command: <word:pagenum><bytes:data><byte:crc8>, answer: <ack> ' long flashsize_bytes ' long bootloaderStartAddr_bytes ' word eepromSize_bytes ' word pagesizeFlash_bytes ' word pagesizeEeprom_bytes ' byte crc8 ' byte <ack> const PROTO_END = 0xff 'end of processing, reset controller ' input/output commands const PROTO_ACK = 0xfd 'acknowledge const PROTO_NAK = 0xfe 'not acknowledge ' memory types const MEMTYPE_FLASH = 0 const MEMTYPE_EEPROM = 1 ' misc values const COMMAND_TIMEOUT = 10000 'command timeout in ms const PAGE_BYTES = PageSize*2 'page size flash in bytes const PAGE_BYTES_EE = 8 'page size eeprom in bytes (do not change!) '****************************************************************************** '****************************************************************************** ' main program '****************************************************************************** dim i,cnt,cmd,buffer(PAGE_BYTES-1),type as byte dim ctcnt,page,eadr as word dim rtcnt as long main() procedure main() if CheckBootloaderAccess() then ack() do cmd=uart.GetByte if cmd then clr ctcnt select case cmd case PROTO_RDY ack() case PROTO_CBV uart.writelong avr.flashsize uart.writelong avr.CodeStartAddr*2 uart.writeword avr.eramsize uart.writeword PAGE_BYTES uart.writeword PAGE_BYTES_EE crc8.Start crc8.AddLong avr.flashsize crc8.AddLong avr.CodeStartAddr*2 crc8.AddWord avr.eramsize crc8.AddWord PAGE_BYTES crc8.AddWord PAGE_BYTES_EE uart.WriteByte crc8.value ack() case PROTO_EFP ack() type=MEMTYPE_FLASH ErasePage() case PROTO_WFP ack() type=MEMTYPE_FLASH EraseWritePage() case PROTO_EEP ack() type=MEMTYPE_EEPROM ErasePage() case PROTO_WEP ack() type=MEMTYPE_EEPROM EraseWritePage() case PROTO_END reset default clr ctcnt nak() end select end if delay(1) incr ctcnt loop until ctcnt=COMMAND_TIMEOUT end if 'jump to main program reset endproc '****************************************************************************** '****************************************************************************** 'sub routines '****************************************************************************** '****************************************************************************** procedure delay(a as byte) waitms a endproc '****************************************************************************** 'check if bootloader called function CheckBootloaderAccess() as byte for i=0 to 9 SLED.toggle if uart.GetByte=PROTO_RDY then return true end if delay(50) next SLED = 1 delay(1000) SLED = 0 return false endfunc '****************************************************************************** '****************************************************************************** 'erase/write page '****************************************************************************** '****************************************************************************** procedure ErasePage() page=uart.readword if MAX_PAGES>=page then 'prevent overwriting bootloader section decr page 'page number for writing is zero-based WLED=1 if type=MEMTYPE_FLASH then for i=0 To buffer().Ubound buffer(i) = 0xff next flash.PageWrite buffer(),page 'write the page data to flash else eadr=page*PAGE_BYTES_EE for i=0 To PAGE_BYTES_EE-1 eeprom.bytevalue(eadr) = 0xff incr eadr next endif ack() WLED=0 return end if nak() endproc procedure EraseWritePage() page=uart.readword if MAX_PAGES>=page then 'prevent overwriting bootloader section crc8.start 'initialize crc calculation crc8.AddWord page 'add page number to crc stream if type=MEMTYPE_FLASH then cnt=buffer().Ubound else cnt=PAGE_BYTES_EE-1 end if for i=0 To cnt 'read page data buffer(i) = uart.ReadByte 'add to buffer crc8.AddByte buffer(i) 'add to crc stream next if uart.ReadByte = crc8.value then 'equate received and calculated crc decr page 'page number for writing is zero-based WLED=1 'indicate write if type=MEMTYPE_FLASH then flash.PageWrite buffer(),page 'write the page data to flash else eadr=page*PAGE_BYTES_EE for i=0 To PAGE_BYTES_EE-1 eeprom.bytevalue(eadr) = buffer(i) incr eadr next endif WLED=0 ack() return end if end if nak() endproc procedure ack() uart.WriteByte PROTO_ACK 'say OK to transmitter endproc procedure nak() uart.WriteByte PROTO_NAK 'failed endproc
- testprogramm.luna
' 'Testprogramm fuer Bootloader-Aufrufe aus dem Programm heraus ' 'compiler: '--------- 'luna 2015r1 or newer ' 'history: '-------- '2013-02-12 demel start '2015-02-09 Anpassung an 2015r1 or newer ' 'references: '----------- 'Luna Bootloader: http://avr.myluna.de/doku.php?id=de:bootloader 'TaskMgr: http://avr.myluna.de/doku.php?id=de:taskmgr 'ATmega88/168/328/32 doku: http://www.atmel.com 'LunaAVR doku: http://avr.myluna.de/doku.php?id=de:download ' avr.device = atmega168 avr.clock = 20000000 avr.stack = 64 #library "Library/TaskTimer.interface" #library "Library/TaskMgr.interface" const USE_SERIAL = 1 'Die Baudrate muss mit der im Bootloader uebereinstimmen!! #if USE_SERIAL uart.baud = 19200 uart.Recv.enable uart.Send.enable #endif 'Je nach Kontroller START_BOOTLOADER und die Fuses anpassen #if avr.device = "atmega88" const START_BOOTLOADER = FOURTHBOOTSTART #endif #if avr.device = "atmega168" const START_BOOTLOADER = FOURTHBOOTSTART #endif #if avr.device = "atmega328p" const START_BOOTLOADER = THIRDBOOTSTART #endif #if avr.device = "atmega32" const START_BOOTLOADER = THIRDBOOTSTART #endif #define STATUS_LED as portd.5 'Status LED gruen, nur zum testen STATUS_LED.mode = output,low 'Benutzereingabe RS232 Konsole Dim chars as string part TaskManager_initialisieren 'Task usw. fuer TaskMgr definieren #define TIMED_TASKS as 2 '2 Tasks im TaskTimer #define TIMED_TIMESLICE as 1 'TaskTimer lauft mit 1 ms #define MAXIMUM_TASKS as 2 '2 Tasks im TaskManager const FLASH_TIME1 = 150 '150 * 1ms = 150ms 'TaskMgr initialisieren TaskMgr.Init(MAXIMUM_TASKS) TaskMgr.TaskAdd( task_led1_toggle().Addr, FLASH_TIME1) 'TaskMgr.TaskAdd( task_led_gruen_toggle().Addr, FLASH_TIME2) 'TaskTimer initialisieren mit 1 Task und einem Intervall von 1 ms. TaskTimer.Init(Timer2, TIMED_TASKS, TIMED_TIMESLICE) '1 Tasks, 1 ms 'TaskTimer-ISR soll nur die selbst veraenderten Register sichern. 'Dies weil wir eine Methode aus einer Library setzen, welche ihrerseits ebenfalls 'ihre veraenderten Register sichert. Dies spart Stack-Speicher. TaskTimer.SaveMode = selfused 'Die PollRoutine des TaskMgr in den TaskTimer eintragen TaskTimer.Task(0) = TaskMgr.Poll().Addr endpart ' Enable all Interrupt Avr.Interrupts.Enable #if USE_SERIAL print "Test Bootloader" print avr.device #endif do 'Konsolenfunktion fuer Bootloader-Bearbeitung 'hier koennen bei Bedarf natuerlich noch mehr Befehle stehen. Print "Bootloader Demo > " chars = uart.InpStr() select case chars case "reset" jump START_BOOTLOADER case "cls" print 12; case "help" call help() default print "Kein gueltiges Komamndo" endselect loop idle 'Eigenen Programmcode hier einfuegen if TaskMgr.Tick then '1ms Takt vom TaskManager TaskMgr.Schedule() 'Scheduler aufrufen endif endidle procedure task_led1_toggle() STATUS_LED.toggle taskmgr.TaskAdd( task_led1_toggle().Addr, FLASH_TIME1 ) endproc procedure Help() print " Kommando Funktion" print "--------------------------------------------" print " help Befehlsuebersicht" print " cls Bildschirm loeschen" print " reset Neustart durchfuehren" print endproc