'*******************************************************************************
'********************** Rollladensteuersender **************************
'*******************************************************************************
'name : Rollladensteuersender V1.0.bas
'copyright : (c) 07.12.2018 jep
'purpose : Rollladensteuerung
'micro : Atmel 328P-PU
'
'Changelog: : v0.1a: statt bed. Assemblierung PortC.3 als Debug-Wahl
' : V0.2: komplett neu strukturiert, kleiner Loop, mehr Sub's
' : V0.3: Abfrage DIP in separater Sub
' : V1.0: Zuweisung Tastencodes geändert und aufgeräumt
'offene Probleme :
'-------------------------------------------------------------------------------
$regfile = "m328Pdef.dat"
$crystal = 16000000 '16MHz-Oszillator
'$crystal = 8000000 '8MHz-Oszillator
$hwstack = 80
$swstack = 80
$framesize = 100
$baud = 115200 '19200
Declare Sub Rfm12_init
Declare Function Spitransfer(byval Dataout As Word) As Word
'################################## ADRESSEN ##################################
' ##
' ' ##
Const Rollladensteuerempfaengeradresse = 31 'ist fix ##
' ##
'###############################################################################
'================================ Atmel328P als SPI MASTER =====================
Dim Moduladresse As Byte '1....4
'Adresseingabe des Senders: A0...A3 = 16 Adressen
A0 Alias Pinc.0
Config A0 = Input
Set PORTC.0
A1 Alias Pinc.1
Config A1 = Input
Set Portc.1
A2 Alias Pinc.2
Config A2 = Input
Set Portc.2
Debugpin Alias Pinc.3
Config Debugpin = Input
Set Portc.3
'-------------------------------
T1 Alias PIND.4
Config PinD.4 = Input 'Taste 1
Set Portd.4 'Pullup
T2 Alias PIND.5
Config PinD.5 = Input 'Taste 2
Set Portd.5 'Pullup
T3 Alias PIND.6
Config PinD.6 = Input 'Taste 3
Set Portd.6 'Pullup
T4 Alias PIND.7
Config PinD.7 = Input 'Taste 4
Set Portd.7 'Pullup
Ledg Alias PortB.0 'grüne LED
Config Ledg = Output
Set Portb.0
Ledr Alias Portb.1 'rote LED
Config Ledr = Output
Set Portb.1
'======================== SPI des ATmega328P ====================================
Ss Alias Portb.2
Miso Alias Pinb.4
Mosi Alias Portb.3
Sck Alias Portb.5
Config Ss = Output 'Slave select
Config Sck = Output 'SCK ----> SCK (RFM12B)
Config Mosi = Output 'MOSI ----> SDI (RFM12B)
Config Miso = Input 'MISO <---- SDO (RFM12B)
Set PortB.2 'Pullup
'CONFIG SPI = soft , MISO = PinB.4 , MOSI = PortB.3 , clock = PortB.5 , SS = PortB.2
'CONFIG SPI = HARD , INTERRUPT = ON , DATA_ORDER = MSB , MASTER = YES , POLARITY = LOW , PHASE = 0 , CLOCKRATE = 16 , NOSS = 0 , SPIIN = 0
'spiinit
'---------Variables-------------------------------------------------------------
Dim D As Word
Dim N As Byte
Dim r as word 'wird im Interrupt genutzt
Dim W as Word
Dim x as Byte
Dim y as Byte
Dim z as Byte
Dim T as String * 4
Dim Debuggen as Byte
Dim Debouncetime as Byte
Debouncetime = 10 '10 ms Wartezeit
Dim AW as Byte 'Wiederholzähler
AW = 2 'Initialwert
'==================== ADC zur Messung der Betriebsspannung =====================
Config Adc = Single , Prescaler = Auto , Reference = Internal_1.1
Const Adc_multiplier = 5.75964 ' (1.1 * 1470) /(1024.0 * 270) * 1000 (etwas korrigiert)
Dim Adcwert As Word 'Wandlerwert
Dim Betriebsspannung As Single 'Betr_spg siehe bei Sendebytes
'========= Hier werden die zu sendenten Bytes (9, max 50) abgelegt ============
' 1 2 3 4 5 6 7 8 9
'+----+----+----+----+----+----+----+----+----+
'| Ausgangsbytes |
'+----+----+----+----+----+----+----+----+----+
'|LENs|DABs|SABs|CDBs|Code|Batterie |CRC2|CRC1|
'+----+----+----+----+----+----+----+----+----+
'
Dim Sendebytes(9) As Byte 'Anzahl ist fix (mit CRC)
Dim Anzahlbyte_s As Byte 'Anzahl zu sendende Bytes
Dim LENs As Byte At Sendebytes(1) Overlay 'Längenbyte
Lens = 8 'Datenmode, fix 9 Bytes Daten (8 da LEN NICHT mitgezählt wird)
Dim DABs As Byte At Sendebytes(2) Overlay 'Adresse an den die Daten geschickt werden (DAB)
Dabs = Rollladensteuerempfaengeradresse 'ist Rollladensteuerempfaengeradresse
Dim SABs As Byte At Sendebytes(3) Overlay 'eigene Adresse (SAB)
Dim CDBs As Byte At Sendebytes(4) Overlay 'Kommandobyte
CDBs = &B01100000 'Datenmode, fordert ACK
Dim Code as Byte At Sendebytes(5) Overlay
Dim Betr_spg As Word At Sendebytes(6) Overlay
Dim Crc_16s As Word At Sendebytes(8) Overlay 'CRC16 für Senden
Dim Paketzaehler As Byte
Paketzaehler = 1
'
'die Quittung besteht aus 4 Bytes
' 1 2 3 4
'+----+----+----+----+
'| Eingangsbytes( ) |
'+----+----+----+----+
'|LENe|DABe|SABe|CDBe|
'+----+----+----+----+
'
Const Maxanzahlempfangsbytes = 9 'zu empfangende Datenmenge (maximal total 66 Byte)
Dim Irq_empfangsbytes(Maxanzahlempfangsbytes + 2) As Byte 'Empfangspuffer während Empfang (etwas grösser)
Dim Irq_anzahlbytemax as Byte
Dim Irq_Akt_byte As Byte
Dim Funkdaten_vorhanden As Bit
Dim Empfangsbytes(maxanzahlempfangsbytes) As Byte 'Empfangspuffer nach Empfang
Dim Lene As Byte At Empfangsbytes(1) Overlay 'Längenbyte
Dim DABe as Byte at Empfangsbytes(2) Overlay
Dim SABe as Byte at Empfangsbytes(3) Overlay
DIM CDBe as Byte at Empfangsbytes(4) Overlay
'================== Interrupt für den Empfang aktivieren =======================
Config PinD.2 = Input 'Int0
PortD.2 = 1 'Pullup
Nirq Alias PinD.2
Config Int0 = Falling
On Int0 RFM_Funkirq
Enable Int0
'======== Timerinterupt für Zeitbegrenzung Interruptempfang einstellen ===========
Config Timer0 = Timer , Prescale = 1024 'https://www.rmc-sachsen.de/?nav=bascom-timer-berechnung
Const Timer0_preload = 255 -(8 * 8000000 / 4800 * 1024) 'für 2 Bytes: gilt für 8 MHz und 4800 Baud Quarzabhängig
On Timer0 Timer0_irq 'beim Interrupt Empfang stoppen
Enable Timer0 'Timer0 Interrupt freigeben
Stop Timer0
'_______________________________________________________________________________
' ***************
' **********************************
' ********************************************************
'*******************************************************************************
'***************************** PROGRAMMSTART ***********************************
'*******************************************************************************
'************************** Initialisierung **********************************
Print
Print "File: " ; Version(3)
Print
Gosub Welches_Modul
Portd = &B11111000 'mit Pullups Eingänge anbinden
Print PINC.3 'Debuggen ein/aus
Print
Gosub Rfm12_init 'RFM12-Modul initialisieren
Enable Interrupts 'alle Interrupts freigeben
Gosub Schlafen0
'*******************************************************************************
'************************ Das Hauptprogramm startet hier ***********************
Do
Gosub Welche_Taste 'Welche Moduladresse/Taste
if Code > &H00 then Gosub Senden
gosub T_noch_gedrueckt
if Code = &HFF then gosub geh_schlafen
Loop
'****************** dient zum Aufwecken des Prozessors ***********************
'*******************************************************************************
PCInt2_isr: 'Pin-Ausscheidung nicht nötig
Return
'******************** stellt fest welche Taste gedrückt ist *******************
Welche_Taste:
gosub Welches_Modul
Sabs = Moduladresse Or &B00010000 'und auf richtige Funkadresse ergänzen (ab h11)
N = PIND and &B11110000 'welche Taste wurde gedrückt?
if debuggen = 1 then ' Tastenanurdnung
Print ' +---+---+
Print "T gedr.: " ; Bin(N) ' |T1u|T2u|
Print ' +---+---+
End If ' |T1d|T2d|
T = "0" ' +---+---+
If Moduladresse = 1 then 'Ausscheidung Moduladresse
Select Case N 'Ausscheidung Taste
Case &B11100000 : Code = &H10 : T = "T2u" 'Code zuweisen
Case &B11010000 : Code = &H11 : T = "T2d"
Case &B10110000 : Code = &H12 : T = "T1u"
Case &B01110000 : Code = &H13 : T = "T1d"
Case else : Code = &H00
End Select
End if
If Moduladresse = 2 then 'Ausscheidung Moduladresse
Select Case N 'Ausscheidung Taste
Case &B11100000 : Code = &H14 : T = "T2u" 'nicht vorhanden
Case &B11010000 : Code = &H15 : T = "T2d" 'nicht vorhanden
Case &B10110000 : Code = &H14 : T = "T1u" 'Code zuweisen
Case &B01110000 : Code = &H15 : T = "T1d"
Case else : Code = &H00
End Select
End if
If Moduladresse = 3 then 'Ausscheidung Moduladresse
Select Case N 'Ausscheidung Taste
Case &B11100000 : Code = &H16 : T = "T2u" 'nicht vorhanden
Case &B11010000 : Code = &H17 : T = "T2d" 'nicht vorhanden
Case &B10110000 : Code = &H16 : T = "T1u" 'Code zuweisen
Case &B01110000 : Code = &H17 : T = "T1d"
Case else : Code = &H00
End Select
End if
If Moduladresse = 4 then 'Ausscheidung Moduladresse
Select Case N 'Ausscheidung Taste
Case &B11100000 : Code = &H16 : T = "T2u" 'Code zuweisen
Case &B11010000 : Code = &H17 : T = "T2d"
Case &B10110000 : Code = &H14 : T = "T1u"
Case &B01110000 : Code = &H15 : T = "T1d"
Case else : Code = &H00
End Select
End if
Return
'**************** Moduladresse und Debugg-Status feststellen ******************
Welches_Modul:
PortC = &B00000111 'Adress-Pullup's einschalten
waitus 2
Moduladresse = Pinc And &B00000111 'Moduladresse ab DIP holen und ausmaskieren
PortC = Moduladresse 'und Pullup's wenn Eingang 0 dann ausschalten
Portc.3 = 1 'Pullup einschalten
Debuggen = PINC.3
If Debuggen = 0 then portc.3 = 0 'falls 0 Pullup ausschalten
Return
'************** überprüft ob die Taste wieder losgelassen ist ******************
T_noch_gedrueckt:
Do
N = PIND 'zuerst warten ob Taste immer noch gedrückt
N = N and &B11110000 'Tasten ausmaskieren
if N = &B11110000 then 'Tasten sind losgelassen
if debuggen = 1 then
Print "T los: " ; Bin(N)
Print
End If
waitms 5 'falls es noch prellt
Code = &HFF
Goto Ist_los 'und zurück
Else 'immer noch eine gedrückt
if debuggen = 1 then
Print "t gedr.: " ; Bin(N)
Print
End If
waitms 5
End if
Loop
Ist_los:
Return
'******* Sendet das Datenpaket und wartet auf ACK/NACK/keine Antwort ********
' und wiederholt (AW) notfalls das Datenpaket
Senden:
AW = 2 'eine Sendewiederholung
Portc.3 = 1 'Pullup einschalten
Debuggen = PINC.3
portc.3 = 0 'Pullup ausschalten
ADCSRA.7 = 1
waitms 1
Adcwert = Getadc(7) 'Wandlerwert als Word
Betriebsspannung = Adcwert * Adc_multiplier 'als Single konvertieren und multiplizieren
Betr_spg = Betriebsspannung 'zum Senden wieder in ein Integer umwandeln
if debuggen = 1 then
Print "Moduladresse: " ; Str(Moduladresse)
Print "Funkadresse: " ; Str(sabs)
Print "Sende Code: " ; Hex(Code)
Print "Multiplier: " ; Adc_multiplier
Print "Betriebsspannung ist: " ; Betr_spg ; " /1000"
End If
Ss = 1
Sck = 0
Rfm12_init 'RFM12B nochmals initialisieren
Incr Paketzaehler
If Paketzaehler >= 16 Then Paketzaehler = 1
Cdbs = Cdbs and &B1111_0000 'PN freistellen
Cdbs = Paketzaehler Or CDBs 'und PN mit dem Rest verknüpfen
Crc_16s = Crc16(sendebytes(1) , 7) 'CRC rechnen über 7 Bytes
if debuggen = 1 then
Print "Sendedaten: ";
For n = 1 to 9
Print Hex(Sendebytes(n)) ; " ";
next n
Print
Print
End If
NeuSenden: 'die Wiederholung erfolgt mit der gleichen PN
gosub Rfm12_senden '
Waitms 1
For n = 1 to 30 'Zeit in der ACK/NACK empfangen sein sollte,
waitms 1 'Vorsicht wenn Printausgaben aktiviert sind
if Funkdaten_vorhanden = 1 then
if debuggen = 1 then
Ledg = 0 'LEDg ein
Print
Print Hex(DABe) ; " " ; Hex(SABe) ; " " ; Hex(CDBe)
end if
Y = &B1110_0000 or Paketzaehler 'ACK und letzte gesendete PN
if DABe = SABs and SABe = Rollladensteuerempfaengeradresse then 'richtig adressiert
if CDBe = Y then '= ACK´
For z = 1 to 4
Empfangsbytes(z) = &HFF 'Empfangspuffer löschen
next z
if debuggen = 1 then
Print "QP"
LEDg = 1 'LEDg aus
End if
Funkdaten_vorhanden = 0 'Merker löschen
Goto Quittung_erhalten
else '= NACK; wurde nicht verstanden
For z = 1 to 4
Empfangsbytes(z) = &HAA 'Empfangspuffer löschen
next z
if debuggen = 1 then
Print "QN"
LEDg = 1 'LEDg aus
End if
Funkdaten_vorhanden = 0 'Merker löschen
goto sofort_senden 'Sendung wiederholen
end if
end if 'war andere Meldung, wird nicht berücksichtigt
end if
Next n
sofort_senden:
AW = AW - 1 'nichts, also Wiederholung
if AW > 0 then
goto NeuSenden
else
Funkdaten_vorhanden = 0 'Merker löschen, Pech gehabt, ging nicht durch resp. keine Quittung
End if
Quittung_erhalten:
Return
'********************* uP und RFM schlafen schicken ****************************
Schlafen0:
if debuggen = 1 then
Print "Startdurchgang"
Print 'LED aus
End If
goto Geh_schlafen 'dient nur zum überspringen beim debuggen
nichts_gedrueckt:
if debuggen = 1 then
Print "Keine Taste gedrückt"
Print 'LED aus
End If
Geh_schlafen:
if debuggen = 1 then
Print
Print ">>>>> Gehe Schlafen"
End If
if debuggen = 1 then
Ledg = 1 'LED aus
end if
D = Spitransfer(&Hb800) 'Senderinterrupt löschen
D = Spitransfer(&H0000) 'eventuelle weitere Interrupts löschen
D = Spitransfer(&H8201) 'Sender aus, Empfänger aus, Wake up Timer aus; Oszi aus
Disable Int0 'liegt auch an Port D
Disable Int1 'liegt auch an Port D
Disable PCINT0 'zur Vorsicht
Disable PCINT1 'zur Vorsicht
Portd = &B11111000 'mit Pullups nochmals Eingänge anbinden; NICHT für den Interrupt & Rx/Tx
PortC = Moduladresse 'dito für die DIP-Schalter
Didr1 = Bits(ain1d , Ain0d) 'Disable digital input buffer on the AIN1/0 pin
Set Acsr.acd 'Switch off the power to the Analog Comparator
Reset Acsr.acbg 'Disable Analog Comparator Bandgap Select
Reset Adcsra.aden 'Switch off ADC
Enable PCINT2 'PCIE2 Interrupt eingesschaltet
PCMSK2 = Bits(PCINT20 , PCINT21 , PCINT22 , PCINT23) 'PCINT20 - 23 eingeschaltet
Enable Interrupts
Config Powermode = Powerdown 'und nun los, ATmega328 schlafen legen
'>>>>>>>>>>>>>>>>>>>> Schlafen <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Waitus 20 'bin wieder da
PCIFR.2 = 1 'Interruptflagregister zurückstellen
waitms Debouncetime 'hoffentlich Tastenprellen vorbei
' Gosub Rfm12_init 'zur Vorsicht RFM12-Modul neu initialisieren
if debuggen = 1 then
Print
Print "********* bin wieder da ********"
Print
End If
Return
'*******************************************************************************
'======================== Sendet das Datenpaket ================================
'Die Senderoutine ist für Polling des Senders geschrieben,
'es wird gewartet bis ein Byte gesendet ist
Rfm12_senden:
Disable Int0
if debuggen = 1 then
Ledr = 0 'LED rot ein
end if
Anzahlbyte_s = Sendebytes(1) + 1 'damit alle Bytes übertragen werden (+ LEN)
D = Spitransfer(&H8238) 'Enable Transmitter; enable Synthesizer ;enable Crystal Osc
Gosub Rfm12_warten
D = Spitransfer(&Hb8aa)
Gosub Rfm12_warten
D = Spitransfer(&Hb8aa)
Gosub Rfm12_warten
D = Spitransfer(&Hb82d)
Gosub Rfm12_warten
D = Spitransfer(&Hb8d4)
For N = 1 To Anzahlbyte_s
Gosub Rfm12_warten
D = &HB800 + Sendebytes(n)
D = Spitransfer(d)
Next N
Gosub Rfm12_warten
D = Spitransfer(&Hb8aa)
Gosub Rfm12_warten
D = Spitransfer(&Hb8aa)
Gosub Rfm12_warten
D = Spitransfer(&H0000) 'Tx-Interrupt löschen
D = Spitransfer(&H8281) 'Tx aus, Rx ein
D = Spitransfer(&HCA81) 'FIFO Reset
D = Spitransfer(&HCA83) 'FIFO Start
Eifr.intf0 = 1 'Eventuell anstehenden Interrupt löschen
Enable Int0
Funkdaten_vorhanden = 0 'nur zur Vorsicht
if debuggen = 1 then
Ledr = 1 'LED rot aus
end if
Return
Rfm12_warten:
Ss = 0
Do
Loop Until Miso = 1
Return
'***************** Empfangs-Interrupt des Funkmoduls RFM12 *******************
Rfm_funkirq:
Disable Int0 'während Interruptbehandlung ausschalten
Timer0 = Timer0_preload
Start Timer0 'Start Zeitüberwachung (Byteweise)
Incr Irq_akt_byte 'aktueller Bytezähler erhöhen (Startwert ist 0)
Irq_empfangsbytes(irq_akt_byte) = Spitransfer(&Hb000) 'Empfangsbyte holen und abspeichern
If Irq_akt_byte = 1 Then 'ist erstes Byte
If Irq_empfangsbytes(1) < 3 or Irq_empfangsbytes(1) >= Maxanzahlempfangsbytes then 'Wenn LEN grösser ist als die max. erwartete
Stop Timer0 'Anzahl Bytes --> abbrechen
Irq_akt_byte = 0 'Bytezähler löschen
R = Spitransfer(&Hca81) 'FIFO FILL zurücksetzen
R = Spitransfer(&Hca83) 'FIFO FILL aktivieren; auf neue Sync-Sequenz warten
Goto Interrupt_ende
Else
Irq_anzahlbytemax = Irq_empfangsbytes(1) + 1 'im ersten Byte steht die Anzahl folgender Datenbytes
End if
End If
If Irq_akt_byte = Irq_anzahlbytemax Then 'max. Anzahl Bytes erreicht
Stop Timer0
R = Memcopy(irq_empfangsbytes(1) , Empfangsbytes(1) , Irq_anzahlbytemax) 'umkopieren
Funkdaten_vorhanden = 1 'Merker setzen
Irq_akt_byte = 0 'Bytezähler löschen
R = Spitransfer(&Hca81) 'FIFO FILL zurücksetzen
R = Spitransfer(&Hca83) 'FIFO FILL aktivieren; auf neue Sync-Sequenz warten
End If
Interrupt_ende:
Eifr.intf0 = 1 'Eventuell anstehenden Interrupt löschen
Enable Int0
Return
Timer0_irq:
Stop Timer0 'Interrupt-Ueberwachung abgelaufen --> Stop
Irq_akt_byte = 0 'Bytezähler löschen
Funkdaten_vorhanden = 0 'Eventuellen Merker löschen
R = Spitransfer(&Hca81) 'FIFO FILL zurücksetzen
R = Spitransfer(&Hca83) 'FIFO FILL aktivieren; auf neue Sync-Sequenz warten
Eifr.intf0 = 1 'Eventuell anstehenden Interrupt löschen
if debuggen = 1 then
Print "Timeout"
end if
Return
'_______________________________________________________________________________
Function Spitransfer(byval Dataout As Word) As Word
Local Nspi As Integer
Local Dspi As Integer
Local Dmiso As Word
Ss = 0
Dmiso = 0
For Nspi = 1 To 16
Dspi = Dataout And &H8000
If Dspi = 0 Then
Mosi = 0
Else
Mosi = 1
End If
Dataout = Dataout * 2
Dmiso = Dmiso * 2
Dmiso = Dmiso + Miso
Sck = 1
Waitus 5
Sck = 0
Next Nspi
Ss = 1
Spitransfer = Dmiso
End Function
'_______________________________________________________________________________
'RFM-Initialisierung
Sub Rfm12_init
Local Wert As Word
X = 0
Restore Datainit3 'Initialisierungsfolge
Do
Read Wert
D = Spitransfer(Wert)
Incr X
Loop Until Wert = 0
Waitms 200
R = Spitransfer(&H0000)
End Sub
'_______________________________________________________________________________
End
'*******************************************************************************
' Programmende
'---------------------------------------------------
'Funkmodul Initialisierungsdaten mit 9600 Baud
Datainit3:
Data &H80E8% ' Enable: 868 Mhz;XTAL cap=12pf; TX-Register; RX-Fifo
Data &H82D9% ' Enable: Receiver; Crystal Osc; Base Band Block; Synthesizer, Disable Low-bat Detector; Transmitter; Wake-Up-Timer; Clock output Pin
Data &HA67C% ' Frequenz 868.3 MHz
' Data &HA6F4% ' Frequenz 868.9 MHz
Data &HC647% ' &Hc647=Datenrate '4.8kbps; C623=9600kbps; C611 =19200
Data &H95C0% ' Vdi , Fast , 67 kHz , 0db , -79dbm !!!!!!!!!!
Data &HC2AD% ' Fiter=Digital; Recover Mode=Auto; Quality Threshold=4; Recovery Speed=Slow
Data &HCA81% ' FIFO INT Level=8; Sync on=2;Fifo Fill Start=Sync; Reset Sensitivity=High; Disable:FIFO Fill Enabled
Data &HC483% ' Enable: AFC Mode; AFC; Frequency Offset Register Disable: High Accuracy; Strobe
Data &H9820% ' Frequenz Shift=POS; Power Out=0 dB; Deviation=45 khz
Data &HE5A8% ' WakeUp-Timer=5s
Data &HC800% ' Duty Cycle = Infinity % OFF
Data &HC000% ' Low batterie=2,2V; Clock Pin=1 Mhz
Data &HCED4% ' Synchron Pattern
Data &HCC76% ' PLL Settings
Data &H0000% ' Status lesen, irqs zurückstellen
Data 0% ' Ende initialisierung
'_______________________________________________________________________________