AVR-Bootloader angepasst       


Elektronik-Labor  Projekte  AVR 

 

Das ES-M32 von Modul-Bus programmiere ich meist in Bascom.  Das Hex-File wird dann mit einem speziellen Tool über die USB-Schnittstelle gebrannt. Vor einiger Zeit bin ich aber auf die Vorzüge des MCS-Bootloaders gestoßen: Man muss nur noch Bascom geöffnet haben, das das dann gleich auch sein eigener Brenner ist. Dazu muss man nur den richtigen Programmer und die korrekte Baudrate einstellen und ein Boot-Programm in den Controller laden.



Der Bootlader selbst befindet sich unter den Beispielen, die mit Bascom mitgeliefert werden. Meist ist eine kleine Modifikation nötig, damit der korrekte Chip angesprochen wird. Hier sollte es der ATmega32 sein. Außerdem wird eine bestimmte Badrate (19200) und eine Taktfrequenz von 11,059 MHz gewünscht. Also diese kleinen Anpassungen rein und dann geflasht.  

$crystal = 11059200
$baud = 19200 'this loader uses serial com
'It is VERY IMPORTANT that the baud rate matches the one of the boot loader
'do not try to use buffered com as we can not use interrupts

'$regfile = "m8def.dat"
'Const Loaderchip = 8

'$regfile = "m168def.dat"
'Const Loaderchip = 168

'$regfile = "m16def.dat"
'Const Loaderchip = 16

$regfile = "m32def.dat"
Const Loaderchip = 32

'$regfile = "m88def.dat"
'Const Loaderchip = 88

(Bootoader.bas)

Zusätzlich müssen die Fuses passend eingestellt werden. Insbesondere muss ein Boot-Flash-Bereich von 1024 Words eingerichtet werden. Bei Start springt der Controller zuerst die Adresse 3C00 an um eventuelle ein neues Programm hochzuladen. Wenn nichts anliegt, verzweigt das Boot-Programm auf den normalen Startvektor bei 0000.




Nach dieser Vorbereitung kann man einen beliebigen Quelltext für den Mega32 in Bascom laden, kompilieren und mit Program/Send to chip in den Controller übertragen. Es funktioniert wahlweise über USB oder RS232, kein externes Programm mehr!  Ist bereits ein Programm geladen, muss beim nächsten mal zuerst mit Program/Send to chip der Bootvorgang beauftragt werden und dann einmal kurz auf den Resetknopf des ES-M32 gedrückt werden. Dann startet der Ladevorgang. Nach dem Laden startet das neue Programm von allein.



So weit, so gut. Aber dann gab es ein Problem. Die geladene Firmware verwendete ebenfalls die serielle Schnittstelle, und ebenfalls mit 19200 Baud. Wenn nun ein PC-Programm schon vor dem Start des Controllers versucht, Daten auszutauschen, dann kann es passieren, dass der Bootloader sich angesprochen fühlt. Das gibt dann Chaos, weil er antwortet, aber nicht verstanden wird. 

Also was genau läuft da ab? Ich habe einmal die Aktion des Bascom über eine zweite Schnittstelle belauscht. Bascom sendet einfach nur beliebig lange das Zauberbyte 123. Sobald der Bootlader ebenfalls mit 123 antwortet, ist er erkannt und kann angesprochen werden.




Im Quelltext des Bootloaders kann man sich ansehen, wie die Verbindung hergestellt wird. Der Bootlaoder empfängt alles was kommt und sendet auch jedes Byte zurück. Wenn dabei ein Byte 123 auftaucht, verzweigt der Controller in den Bootloader (Goto Loader). Wenn nach fünf Bytes bzw. nach fünfmal Timeout keine 123 dabei war, geht es zum Start der Firmware (Goto _reset).

$timeout = 400000                                           'we use a timeout
'When you get LOADER errors during the upload, increase the timeout value
'for example at 16 Mhz, use 200000

Bretries = 5 'we try 5 times
Testfor123:
#if Cdebug
Print "Try " ; Bretries
Print "Wait"
#endif
Bstatus = Waitkey() 'wait for the loader to send a byte
#if Cdebug
Print "Got "
#endif

Print Chr(bstatus);

If Bstatus = 123 Then 'did we received value 123 ?
Bkind = 0 'normal flash loader
Goto Loader
Elseif Bstatus = 124 Then ' EEPROM
Bkind = 1 ' EEPROM loader
Goto Loader
Elseif Bstatus <> 0 Then
Decr Bretries
If Bretries <> 0 Then Goto Testfor123 'we test again
End If

For J = 1 To 10 'this is a simple indication that we start the normal reset vector
Toggle Portb.2 : Waitms 100
Next

#if Cdebug
Print "RESET"
#endif
Goto _reset 'goto the normal reset vector at address 0
Auszug aus dem originalen Bootlader (Bootoader.bas)

Das Problem ist, dass jedes Byte beantwortet wird. Das wollte ich ändern. Es wird jetzt im Hauptprogramm nur noch zweimal abgefragt, ob ein Byte 123 kommt. Wenn ja, geht es in den Boot-Bereich. Und da wird noch einmal genauso auf 123 getestet. Wenn also nach dem ersten Booten das Hauptprogramm in der Form nicht mehr existiert und nur noch der Bootbereich verantwortlich ist, muss die PC-Software (in diesem Fall Bascom) trotzdem erst mal beweisen, dass sie einen Bootvorgang beabsichtigt. Falls eine ganz andere Software am anderen Ende ruft und entsprechend andere Bytes kommen, dann passiert gar nichts. Der Controller startet einfach nur bei Adresse 0000. Damit tatsächlich der Bootvorgang startet, muss Bascom beim erstenmal vier Bytes 123 senden, ab dem zweiten mal nur noch zweimal. Soviel Zeit muss sein, es dauert ja nicht mal eine Sekunde.

Testfor123:
Bstatus = Waitkey() 'wait for the loader to send a byte
If Bstatus = 123 Then 'did we received value 123 ?
Print Chr(bstatus);
Bkind = 0 'normal flash loader
Goto Loader
Else 'try again
Bstatus = Waitkey() 'wait for the loader to send a byte
If Bstatus = 123 Then 'did we received value 123 ?
Print Chr(bstatus);
Bkind = 0 'normal flash loader
Goto Loader
End If
End If
Goto _reset 'goto the normal reset vector at address 0


'this is the loader routine. It is a Xmodem-checksum reception routine
Loader:
Bstatus = Waitkey() 'wait for the loader to send a byte
If Bstatus = 123 Then 'did we received value 123 ?
Print Chr(bstatus);
Bkind = 0 'normal flash loader
Goto Loader2
Else
Bstatus = Waitkey() 'wait for the loader to send a byte
If Bstatus = 123 Then 'did we received value 123 ?
Print Chr(bstatus);
Bkind = 0 'normal flash loader
Goto Loader2
End If
End If
Goto _reset 'goto the normal reset vector at address 0

Loader2:
Do
Bstatus = Waitkey()
Loop Until Bstatus = 0

If Bkind = 0 Then
Spmcrval = 3 : Gosub Do_spm ' erase the first page
Spmcrval = 17 : Gosub Do_spm ' re-enable page
End If

Der modifizierte Einstieg (Bootoader32.bas)

Der angepasste Bootloader funktioniert wie geplant. Man kann nun auch Software verwenden, die selbst mit der seriellen Schnittselle arbeitet. Aber eines stört mich noch, dass man nämlich ab dem zweiten mal Reset drücken muss. Um das System komplett fernsteuern zu können, muss auch die Firmware selbst damit umgehen können, dass eine neue Version geladen werden soll. Als Beispiel habe ich das Programm ESM32SIOS  aus meinem Buch Basiskus BASCOM-AVR  genommen. Es verwendet einen intensiven Datenaustausch mit dem PC und simuliert ein SIOS-Interface. Das Programm lauscht ohnehin laufend auf ankommende Bytes, die dann als Kommandos interpretiert werden. Da reicht also eine kleine Erweiterung, die auch das Kommando 123 erkennt und auswertet. Zur Sicherheit müssen zwei Bytes 123 hintereinander kommen, dann startet das Programm den Bootloader.

'--------------------------------------------------------------
' ESM32 SIOS
' ATMEGA32, 11,0592 MHz
' Erweiterung: Start des Bootladers mit 123, 123
'--------------------------------------------------------------

$regfile = "m32def.dat"
$baud = 19200
$crystal = 11059200
$hwstack = 32
$swstack = 64
$framesize = 64


Dim Command As Byte
Dim N As Byte
Dim L As Byte
Dim H As Byte
Dim D As Byte
Dim Dd As Word

Open "com1:" For Binary As #1

Config Portd = Output
Portc = 255


Config Adc = Single , Prescaler = Auto , Reference = Off ' Pre 8...32
Start Adc

Config Timer1 = Pwm , Prescale = 8 , Pwm = 10 , Compare A Pwm = Clear Down , Compare B Pwm = Clear Down
Start Timer1

Do
Get #1 , Command
'SIOS Device 10
If Command = 1 Then Put #1 , 10

'Firmware V2.1
If Command = 2 Then Put #1 , 21

'Output Port D
If Command = 16 Then
Get #1 , D
Portd = D
End If

'Input Port C
If Command = 32 Then
D = Pinc
Put # 1 , D
End If

'ADC 8 Bit
If Command = 48 Then
Dd = Getadc(0)
Shift Dd , Right , 2
D = Dd
Put #1 , D
End If

...

'ADC 10 Bit
If Command = 56 Then
Dd = Getadc(0)
D = High(dd)
Put #1 , D
Get #1 , D
D = Low(dd)
Put #1 , D
End If

If Command = 57 Then
Dd = Getadc(1)
D = High(dd)
Put #1 , D
Get #1 , D
D = Low(dd)
Put #1 , D
End If

...

'PWM Output 10 Bit
If Command = 72 Then
Get #1 , H
Dd = 256 * H
Get #1 , L
Dd = Dd + L
Pwm1a = Dd
End If
If Command = 73 Then
Get #1 , H
Dd = 256 * H
Get #1 , L
Pwm1b = Dd
End If

If Command = 123 Then 'Bootloader
Get #1 , D
If D = 123 Then Jmp $3c00
End If


Loop

End

Mit Bascom gebootet, es funktioniert. Und nun kann ohne Reset immer wieder neu gebootet werden. Nur wenn einmal versehentlich ein Programm ohne diese Erweiterung geladen wird, dann muss man doch wieder Reset gedrückt werden. Hoffentlich ist das ES-M32 dann nicht gerade als Wetterstation  in die Spitze eines Kirchturms eingebaut, wo man so schlecht hinkommt.

Download: Alle drei Programme: Bootloader32.zip


Bascom-Bootloader für Arduino



Siehe auch:  Ein Arduino Shield

Der Arduino hat zwar seinen eigenen Bootloader. Über den ISP-Stecker kann man aber sehr leicht den Basocm-Bootloader aufspielen und macht dann aus der Hardware ein vollwertiges Bascom-System. Hier wurde die Franzis-Version der Platine mit einem ATmega168 verwendet. Der Bootloader wurde für den Mega168 mit 16 MHz angepasst.

Download: Bootloader168_16.zip

Anders als beim Arduiono-Bootloader muss man für jeden Bootvorgang einmal auf den Reset-Taster drücken. Damit steht der Software-Entwicklung mit Bascom nichts mehr im Wege. Man muss eigentlich nur die Zuordnung der Ports kennen. Das folgende Testprogramm verwendet die LED auf dem Board und die serielle Schnittstelle.

'Arduino/Bascom-Test

$regfile = "m168def.dat"
$hwstack = 32
$swstack = 64
$framesize = 64

$crystal = 16000000
Baud = 19200
Config Portb.5 = Output

'Ports 0...7 = PortD, Ports 9...13 = Port B
'13-LED = PortB.5
Do
Toggle Portb.5
Waitms 500
Print "Hallo"
Loop
End

Das Ergebnis sieht man an der blinkenden LED auf der Arduino-Platine und im Bascom-Terminal:





Arduino-Bootloader
, ein Hinweis von Heinz D.: 



BasCom ab Version 2.0.7.1 kann als Programmer der Arduino gewählt werden. Dabei wird der Arduino NICHT als ISP-Adapter benutzt sondern das Kompilat mit der Hilfe des Arduino-Bootladers geflasht. Auf diese Weise bleibt der Arduino für beide Compiler unmittelbar verwendbar! Es funktionieren anscheinend nur Arduino mit FTDI-Chip (bei mir FTDI+M168 LP-Arduino).Nicht funktionieren: M8u2+M328 (Arduino uno) und M32u4 (Leonardo + Arduino mikro).


Elektronik-Labor  Projekte  AVR