'Alternative firmware for HK Blueseries 6A/10A Brushless ESCs.
'Controls three outputs (Positionlights, ACL and Landlights) based on
'RC-Channel position transmitted via S-Bus. Includes LiPo low-voltage detection
'and alarm via blinking positionlights.
'After power-up, the LiPo-Voltage is measured intially and the number of cells
'is calculated and shown to the user by blinking positionlights.
'Copyright (C) <2014> <marenb79 (at) gmx.de>
'This program is free software: you can redistribute it and/or modify
'it under the terms of the GNU General Public License as published by
'the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'This program is distributed in the hope that it will be useful,
'but WITHOUT ANY WARRANTY; without even the implied warranty of
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
'GNU General Public License for more details.
'You should have received a copy of the GNU General Public License
'along with this program. If not, see <http://www.gnu.org/licenses/>.
'Any commercial use requires permission of the author.
'*****************************************
'Modify these constants for for your needs
'*****************************************
Const Vmin = 3600 'Zellspannungsuntergrenze für Erkennung leerer LiPo in mV
Const Vmin_time = 3 'Zeit in Sekunden für die Vmin unterschritten sein muss, bevor leerer LiPo erkannt wird (0-255)
Const Refchannel = 13 'Kanal auf den reagiert werden soll (1-16, Kanäle 17&18 erfordern Anpassung, da nur ein/aus)
'Const Board = "Pollin" 'für Betrieb auf dem ESC, diese Zeile deaktivieren.
'Testumgebung war ein Atmel-Evaluationsboard von Pollin, wird dies aktiviert, passt der Compiler Ein- und Ausgänge an
'und aktiviert das Debugging
Const Board = "ESC" 'für Betrieb auf dem ESC, diese Zeile aktivieren
Const Poslightdriver = "HSD" '"HSD" = P-Fets, Alternativ: "LSD" -> nutzt die n-Fets
Const Acldriver = "HSD" '"HSD" = P-Fets, Alternativ: "LSD" -> nutzt die n-Fets
Const Landlightdriver = "HSD" '"HSD" = P-Fets, Alternativ: "LSD" -> nutzt die n-Fets
Const Poslight_threshold = 300 'Kanalwert ab dem die Positionslampen eingeschaltet werden (Mitte ist 1024)
Const Acl_threshold = 1200 'Kanalwert ab dem das ACL eingeschaltet wird
Const Landlight_threshold = 1400 'Kanalwert ab dem die Landescheinwerfer eingeschaltet werden
'**************************
'Eigentlicher Programmstart
'**************************
#if Board = "Pollin" 'auf dem Pollin-Board ist der
$regfile = "m8def.dat" 'µC ein Atmega8
$crystal = 8000000 'mit einer Taktfrequenz von 8Mhz
#elseif Board = "ESC" 'auf dem HK 10A ESC ist der
$regfile = "m8adef.dat" 'µC ein Atmega8a
$crystal = 16000000 'mit einer Taktfrequenz von 16Mhz
#endif
$hwstack = 40
$swstack = 16
$framesize = 32
'Serielle Schnitstelle(n) für S-Bus-Empfang und ggf. zum PC zum Debuggen konfigurieren
#if Board = "Pollin"
$baud #2 = 115200 'Baudrate für Serielle Debug-Schnittstelle zum PC (HW-Uart)
#endif
#if Board = "ESC"
Open "COMD.0:100000,8,E,2, inverted" For Input As #1 'Seriell Software-RX konfigurieren S-BUS (im ESC)
#endif
#if Board = "Pollin"
Open "COMD.5:100000,8,E,2, inverted" For Input As #1 'Seriell Software-RX konfigurieren S-BUS (auf Pollin-Board)
Open "COM1:115200,8,N,1" For Binary As #2 'Seriell Hardware-Tx konfigurieren PC-Debugging
#endif
Echo Off
'ADC für Messung der Versorgungsspannung konfigurieren (LiPo-Überwachung)
Config Adc = Single , Prescaler = Auto , Reference = Avcc
'Timer1 für das ACL konfigurieren
Config Timer1 = Timer , Prescale = 1024 'Timer1 für das Blitzen des ACL konfigurieren
On Timer1 Acl_isr 'bei Überlauf in die ACL-ISR springen
Enable Timer1 'Timer1-Interrupt aktivieren
'Timer2 als Stoppuhr für den S-Bus-Empfang konfigurieren
Config Timer2 = Timer , Prescale = 1024
Stop Timer2 'anhalten
Timer2 = 0 'und auf Null setzen für die erste Messung
'Ausgänge konfigurieren
#if Poslightdriver = "HSD"
Poslight Alias Portd.4 'HSD: Positionslichter auf PD4
Config Portd.5 = Output
Portd.5 = 0
#elseif Poslightdriver = "LSD"
Poslight Alias Portd.5 'LSD: Positionslichter auf PD5
Config Portd.4 = Output
Portd.4 = 0
#endif
#if Acldriver = "HSD"
Acl Alias Portc.5 'HSD: ACL auf PC5
Config Portc.4 = Output
Portc.4 = 0
#elseif Acldriver = "LSD"
Acl Alias Portc.4 'LSD: ACL auf PC4
Config Portc.5 = Output
Portc.5 = 0
#endif
#if Landlightdriver = "HSD"
Landlight Alias Portc.3
Config Portb.0 = Output
Portb.0 = 0 'HSD: Landescheinwerfer auf PC3
#elseif Landlightdriver = "LSD"
Landlight Alias Portb.0 'LSD: Landescheinwerfer auf PB0
Config Portc.3 = Output
Portc.3 = 0
#endif
Config Landlight = Output 'als Ausgang definieren
Config Poslight = Output 'als Ausgang definieren
Config Acl = Output 'als Ausgang definieren
'Variablen für S-Bus-Frame definieren
Dim Startbyte As Byte 'Jeder S-Bus-Frame beginnt mit einem festen Startbyte
Dim Endbyte As Byte 'Jeder S-Bus-Frame endet mit einem festen Endbyte
Dim Databyte(23) As Byte 'Gesamtes Frame besteht aus Startbyte + 23 Datenbytes + Endbyte
Dim Bitmask As Byte 'Hilfvaribale für Zwischenoperationen
Dim Channel(18) As Integer 'Es werden 18 Kanäle übertragen
'Allgemeine Variablen
Dim Statusbyte As Byte 'Ein Byte als gemeinsamer Container für alle Statusflags
Newdata Alias Statusbyte.0 'Flag für "Es liegen neue, gültige Daten vom S-Bus vor"
Framelost Alias Statusbyte.1 'wenn dem Empfänger ein Datenframe verloren gegangen ist
Failsave Alias Statusbyte.2 'Empfänger meldet, wenn er im Failsafe-Modus ist
Lowbatt Alias Statusbyte.3 'wenn niedrige Versorgungsspannung erkannt wurde (LiPo leer)
ACL_enable alias statusbyte.6 'ACL aktiviert oder nicht
Long_period Alias Statusbyte.7 'Flag zur Erkennung der vorangegenagenen Periode für das ACL
Dim I As Byte 'Zaehlervariable
dim vcells_raw as Integer
Dim Vcells As single 'Zellenspannung in mV
Dim Lipo_cells As Integer 'Anzahl der erkannten LiPo-Zellen
Dim Lipo_timeout As Byte 'countdown für unterschrittene LiPo-Spannung
Lipo_timeout = Vmin_time 'Anfangs countdown resetten
'*****************
'Start-Up Prozedur
'*****************
Wait 2 '2 Sek. warten bis System stabil (Stecker vollständig eingesteckt, etc.)
Lipo_cells = 1 'Zunächst auf eine Zelle setzen, damit vcells in Höhe der Gesamtspannung zurückgegeben wird
Gosub Get_vcells 'Einmal LiPo-Zellenspannung messen
Select Case Vcells 'Anzahl der LiPo-Zellen an Hand der Versorgungsspannung ermitteln
Case 0 To 5500 : Lipo_cells = 1
Case 5501 To 9000 : Lipo_cells = 2
Case 9001 To 13000 : Lipo_cells = 3
Case 13001 To 17000 : Lipo_cells = 4
Case Else : Lipo_cells = 5
End Select
For I = 1 To Lipo_cells 'Pro erkannter LiPo-Zelle Ausgang A einmal blinken lassen
Set Poslight
Waitms 500
Reset Poslight
Waitms 500
Next
Enable Interrupts
'********************************
'*** BEGINN DER HAUPTSCHLEIFE ***
'********************************
Do
'*** Auswertung des Referenzkanals ***
If Timer1 < 65220 Then Gosub Read_sbus 'wenn noch min. 20ms bis zum ACL-Interrupt übrig, dann S-Bus lesen
If Newdata = 1 Then Gosub Analyze_channel 'den in der Konst. "Refchannel" angegebenen Kanal auswerten, falls neue, gültige Daten vorliegen
'*** Jetzt kommt die Reaktion auf die Kanalwerte ***
If Lipo_timeout > 0 Then
If Channel(refchannel) > Poslight_threshold Then Poslight = 1 Else Poslight = 0
End If
If Channel(refchannel) > Acl_threshold Then Acl_enable = 1 Else Acl_enable = 0
If Channel(refchannel) > Landlight_threshold Then Landlight = 1 Else Landlight = 0
'*** LiPo-Überwachung ***
Gosub Get_vcells 'LiPo-Spannung messen
If Vcells < Vmin Then
Set Lowbatt 'bei Zellspannung unter 3,6V Flag für leeren LiPo setzen
Else
if lipo_timeout>0 then
Reset Lowbatt 'sonst Flag zurücksetzen
Lipo_timeout = Vmin_time
end if 'und Countdown zurücksetzen
End If
#if Board = "Pollin" 'falls Pollin-Board, Kanalstellung auf HW-Uart zum Debug senden
If Timer1 < 65000 Then 'wenn noch genügend Zeit in einer langen Periode bleibt
Print #2 , Channel(refchannel) 'aktuelle Kanalstellung an PC übertragen
End If
#endif
Loop
'******************
'Es folgen die Subs
'******************
'***ACL Blitzer***
Acl_isr:
#if Board = "Pollin" 'Das Pollinboard läuft nur auf 8Mhz, daher müssen die Timer-Werte angepasst werden
If Long_period = 1 Then 'falls vorangegangene Periode eine lange war
If Acl_enable = 1 Then Acl = 1 'nur falls ACL aktiviert ist, ACL-Ausgang einschalten
Load Timer1 , 234 'timer für 30ms vorladen
Long_period = 0 'ab jetzt folgt eine kurze Periode
Else 'falls vorangegangene Periode eine kurze war
Acl = 0 'ACL-Ausgang so oder so ausschalten
If Lipo_timeout = 0 Then Toggle Poslight 'falls LiPo leer, Positionsleuchten toggeln
If Lowbatt = 1 And Lipo_timeout > 0 Then Decr Lipo_timeout 'eins runterzählen, falls Unterspannung vorliegt
Load Timer1 , 7585 'timer für 970ms vorladen
Long_period = 1 'ab jetzt folgt eine lange Periode
End If
#endif
#if Board = "ESC" 'Der Esc läuft auf 16Mhz , daher zählt Timer2 In Der gleichen Zeit doppelt so weit
If Long_period = 1 Then 'falls vorangegangene Periode eine lange war
If Acl_enable = 1 Then Acl = 1 'nur falls ACL aktiviert ist, ACL-Ausgang einschalten
Load Timer1 , 469 'timer für 30ms vorladen
Long_period = 0 'ab jetzt folgt eine kurze Periode
Else 'falls vorangegangene Periode eine kurze war
Acl = 0 'ACL-Ausgang so oder so ausschalten
If Lipo_timeout = 0 Then Toggle Poslight 'falls LiPo leer, Positionsleuchten toggeln
If Lowbatt = 1 And Lipo_timeout > 0 Then Decr Lipo_timeout 'eins runterzählen, falls Unterspannung vorliegt
Load Timer1 , 15169 'timer für 970ms vorladen
Long_period = 1 'ab jetzt folgt eine lange Periode
End If
#endif
Return
'*** Messen der Versorgungsspannung in mV ***
Get_vcells:
Vcells_raw = Getadc(2) 'ADC2 einlesen (=Versorgungsspannung über Teiler 220k/51k)
vcells=vcells_raw
Vcells = Vcells * 25.082
Vcells = Vcells + 955.61 'Umrechnung: U=ADC2*25.082+955,61 /mV
Vcells = Vcells / Lipo_cells 'macht pro Zelle xy mV
Return
'*** Einlesen eines S-Bus-Frames ***
Read_sbus: 'Sprungmarke
Inputbin #1 , Startbyte 'Ein Byte von der SW-UART abholen
#if Board = "Pollin" 'Das Pollinboard läuft nur auf 8Mhz, daher müssen die Timer-Werte angepasst werden
If Startbyte = 15 Then 'falls es sich um das Startbyte (0x0F)handelt...
Timer2 = 0 'Stoppuhr zurücksetzen
Start Timer2 'Stoppuhr starten
Inputbin #1 , Databyte(1) , 23 'Die 23 Datenbytes abholen
Inputbin #1 , Endbyte 'Das letzte Byte im Frame = hoffentlich das Endbyte empfangen
Stop Timer2
If Endbyte = 0 And Timer2 > 19 And Timer2 < 28 Then 'wenn mehr als 2,5 und weniger als 3,5ms für die letzten 24 Bytes vergangen sind und
Newdata = 1 'falls am Ende ein korrektes Endbyte (=0) empfangen wurde ist der neue Datenstream gültig!
Else
Newdata = 0 'andernfalls ist er nicht gültig und muss ignoriert werden
End If
End If
#endif
#if Board = "ESC" 'Der ESC läuft auf 16Mhz, daher zählt Timer2 in der gleichen Zeit doppelt so weit
If Startbyte = 15 Then 'falls es sich um das Startbyte (0x0F)handelt...
Timer2 = 0 'Stoppuhr zurücksetzen
Start Timer2 'Stoppuhr starten
Inputbin #1 , Databyte(1) , 23 'Die 23 Datenbytes abholen
Inputbin #1 , Endbyte 'Das letzte Byte im Frame = hoffentlich das Endbyte empfangen
Stop Timer2
If Endbyte = 0 And Timer2 > 38 And Timer2 < 56 Then 'wenn mehr als 2,5 und weniger als 3,5ms für die letzten 24 Bytes vergangen sind und
Newdata = 1 'falls am Ende ein korrektes Endbyte (=0) empfangen wurde ist der neue Datenstream gültig!
end if
Else
Newdata = 0 'andernfalls ist er nicht gültig und muss ignoriert werden
End If
#endif
Return
'*** Auswertung der Kanäle ***
'Compiler implementiert nur den jeweiligen in der Konstanten "Refchannel" angegebenen Kanal,
'die übrigen Kanäle werden ignoriert, da nicht von Bedeutung
Analyze_channel:
#if Refchannel = 1
Channel(1) = Databyte(2) And &B00000111 'Nur die Ersten Drei Bits Vom 2. Datenbyte
Shift Channel(1) , Left , 8 'Platz machen für die unteren 8 Bits, die vom 1. Datenbyte kommen
Channel(1) = Channel(1) + Databyte(1) 'das 1. Datenbyte hinzuaddieren
#elseif Refchannel = 2
Channel(2) = Databyte(3) And &B00111111 'nur die ersten 6 Bits vom 3. Datenbyte
Shift Channel(2) , Left , 5 'Platz machen für die verbliebenen 5 Bits vom 2. Datenbyte
Bitmask = Databyte(2) And &B11111000 'nur die 5 Bits vom 2. Datenbyte
Shift Bitmask , Right , 3 'auf Anfang zurechtrücken
Channel(2) = Channel(2) + Bitmask 'und alle addieren
#elseif Refchannel = 3
Channel(3) = Databyte(5) And &B00000001 '....usw usf
Shift Channel(3) , Left , 8
Channel(3) = Channel(3) + Databyte(4)
Shift Channel(3) , Left , 2
Bitmask = Databyte(3) &B11000000
Shift Bitmask , Right , 6
Channel(3) = Channel(3) + Bitmask
#elseif Refchannel = 4
Channel(4) = Databyte(6) And &B00001111
Shift Channel(4) , Left , 7
Bitmask = Databyte(5) And &B11111110
Shift Bitmask , Right , 1
Channel(4) = Channel(4) + Bitmask
#elseif Refchannel = 5
Channel(5) = Databyte(7) And &B01111111
Shift Channel(5) , Left , 4
Bitmask = Databyte(6) And &B11110000
Shift Bitmask , Right , 4
Channel(5) = Channel(5) + Bitmask
#elseif Refchannel = 6
Channel(6) = Databyte(9) And &B00000011
Shift Channel(6) , Left , 8
Channel(6) = Channel(6) + Databyte(8)
Shift Channel(6) , Left , 1
Bitmask = Databyte(7) And &B10000000
Rotate Bitmask , Left , 1
Channel(6) = Channel(6) + Bitmask
#elseif Refchannel = 7
Channel(7) = Databyte(10) And &B00001111
Shift Channel(7) , Left , 7
Bitmask = Databyte(9) And &B11111100
Shift Bitmask , Right , 2
Channel(7) = Channel(7) + Bitmask
#elseif Refchannel = 8
Channel(8) = Databyte(11) And &B01111111
Shift Channel(8) , Left , 4
Bitmask = Databyte(10) And &B11110000
Shift Bitmask , Right , 4
Channel(8) = Channel(8) + Bitmask
#elseif Refchannel = 9
Channel(9) = Databyte(13) And &B00001111
Shift Channel(9) , Left , 7
Bitmask = Databyte(12) And &B01111111
Channel(9) = Channel(9) + Bitmask
#elseif Refchannel = 10
Channel(10) = Databyte(14) And &B01111111
Shift Channel(10) , Left , 4
Bitmask = Databyte(13) And &B11110000
Shift Bitmask , Right , 4
Channel(10) = Channel(10) + Bitmask
#elseif Refchannel = 11
Channel(11) = Databyte(16) And &B00000001
Shift , Channel(11) , Left , 8
Channel(11) = Channel(11) + Databyte(15)
Shift Channel(11) , Left , 1
Bitmask = Databyte(14) And &B10000000
Rotate Bitmask , Left , 1
Channel(11) = Channel(11) + Bitmask
#elseif Refchannel = 12
Channel(12) = Databyte(17) And &B00001111
Shift Channel(12) , Left , 7
Bitmask = Databyte(16) And &B11111110
Channel(12) = Channel(12) + Bitmask
#elseif Refchannel = 13
Channel(13) = Databyte(18) And &B01111111
Shift Channel(13) , Left , 4
Bitmask = Databyte(17) And &B11110000
Shift Bitmask , Right , 4
Channel(13) = Channel(13) + Bitmask
#elseif Refchannel = 14
Channel(14) = Databyte(20) And &B00000011
Shift Channel(14) , Left , 8
Channel(14) = Channel(14) + Databyte(19)
Shift Channel(14) , Left , 1
Bitmask = Databyte(18) And &B10000000
Rotate Bitmask , Left , 1
Channel(14) = Channel(14) + Bitmask
#elseif Refchannel = 15
Channel(15) = Databyte(21) And &B00011111
Shift Channel(15) , Left , 6
Bitmask = Databyte(20) And &B11111100
Shift , Bitmask , Right , 2
Channel(15) = Channel(15) + Bitmask
#elseif Refchannel = 16
Channel(16) = Databyte(22)
Shift Channel(16) , Left , 3
Bitmask = Databyte(21) And &B11100000
Rotate Bitmask , Left , 3
Channel(16) = Channel(16) + Bitmask
#elseif Refchannel = 17
Channel(17) = Databyte(23).7 'Kanal 17 ist ein Flag auf Byte 23, Bit 7
#elseif Refchannel = 18
Channel(18) = Databyte(23).6 'Kanal 18 ist ein Flag auf Byte 23, Bit 6
#endif
Framelost = Databyte(23).5 'Framelost-Flag liegt auf letztem Datenbyte vor dem Endbyte
Failsave = Databyte(23).4 'Failsave-Flag liegt auf letztem Datenbyte vor dem Endbyte
Newdata = 0
Return
End