PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Codebeispiel für Lesen von RC5 Code mit Interrupt-Routine



-tomas-
25.05.2006, 23:04
Die hier bisher vorgeschlagene Lösung mittels eines Levels-Interrupts Intx GetRC5 anzustoßen hat den Nachteil, dass:
- die ISR für die ganze Zeit des Codempfangs (bis zu 130ms) den MC blockiert
- GetRC5 viele Fehler liest, da die Synchronisation wegen der fehlenden ersten Flanke wackelt
- zwei Interrupts belegt werden (Timer0 und INTx)

hier der alte Code: siehe https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=18140

$regfile = "m16def.dat" 'Controllertyp,
$framesize = 32 'Stackanweisungen
$swstack = 32
$hwstack = 64
$crystal = 8000000 'Die Frequenz des verwendeten Quarzes
$baud = 9600 'Die Baudrate für RS232 Ausgabe.

$lib "mcsbyte.lbx"

'RC5 benötigt Timer0 Interrupt !
Config Rc5 = Pind.3
On Int1 Int1_int 'Nosave würde 52 Takte = 6,5uS sparen
Enable Int1
Config Int1 = Falling
Enable Interrupts

'Rückgabewerte der ISR
Dim Address_rc5 As Byte , Command_rc5 As Byte , Rc5_flag As Bit

Do
If Rc5_flag = 1 Then
Reset Rc5_flag
Print "toggle:" ; Command_rc5.7;
'clear the toggle bit
Command_rc5 = Command_rc5 And &B01111111
Print " Adresse:" ; Address_rc5 ; " Code:" ; Command_rc5
End If
'Waitms 100
Loop

End

'Lesen der RC5 Codes
Int1_int: 'Interrupt Handler For Int1
Disable Int1
Enable Interrupts 'für Timer0 Overflow GetRC5
Getrc5(address_rc5 , Command_rc5)
Set Rc5_flag 'Flag für RC5 Daten
Gifr = Gifr Or &H80 'clear Flag Int1
Enable Int1
ReturnCodelänge: 444Word = 888Byte


Hier eine Lösung, die den BASCOM-Befehl GetRC5 nur mit der Timer0-Overflow-ISR nachbildet.
Vorteile:
- ISR dauert nur ca. 8µs alle 178µs (Timer0-Overflow), d.h. ca. 5% MC-Last (keine Blockierung)
- liest den RC5 Code fehlerfrei, da er sich auf jede Flanke synchronisiert
- belegt nur den Timer0-Interrupt

'Decodierung eines RC5-Codes

$regfile = "m16def.dat" 'Controllertyp
$framesize = 32 'Stackanweisungen
$swstack = 32
$hwstack = 64 'Achtung ISR=32 Byte
$crystal = 8000000 'bei Änderung den Timer0 neu einstellen!!
$baud = 9600 'Die Baudrate für RS232 Ausgabe.

$lib "mcsbyte.lbx" 'ACHTUNG:numeric<>string conversion routines only for bytes

Config Portb.0 = 0
Input_pin Alias Pinb.0 'Pin für TSOP1736

Config Timer0 = Timer , Prescale = 8
On Timer0 Timer_irq
Const Timervorgabe = 78 'Timeraufruf alle 178µs (10 Samples = 1 Bit = 1,778ms)
Enable Timer0 'Hier werden die Timer aktiviert
Enable Interrupts

'Timing für 10 Samples Per Bit = 1,778ms
Const Samples_early = 8 'Flanke Frühestens Nach 8 Samples
Const Samples_late = 12 'Flanke Spätestens Nach 12 Samples
Const Samples_min = 3 'Flanke Vor 3 Samples - > Paket Verwerfen

'Variablen der ISR
Dim Sample As Byte 'eigentlich Bit, spart aber 46Byte ROM
Dim Ir_lastsample As Byte 'zuletzt gelesenes Sample
Dim Ir_bittimer As Byte 'zählt die Aufrufe von Timer_IRQ
Dim Ir_data_tmp As Word 'Bitstream
Dim Ir_bitcount As Byte 'Anzahl gelesener Bits


'Rückgabewerte der ISR
Dim Address_rc5 As Byte , Command_rc5 As Byte , Rc5_flag As Bit

Do
If Rc5_flag = 1 Then
Reset Rc5_flag
Print "toggle:" ; Command_rc5.7;
'clear the toggle bit
Command_rc5 = Command_rc5 And &B01111111
Print " Adresse:" ; Address_rc5 ; " Code:" ; Command_rc5
End If
'Waitms 100
Loop

End


Timer_irq:
Timer0 = Timervorgabe
Sample = Not Input_pin

'bittimer erhöhen (bleibt bei 255 stehen)
If Ir_bittimer < 255 Then Incr Ir_bittimer

'flankenwechsel erkennen
If Ir_lastsample <> Sample Then

If Ir_bittimer <= Samples_min Then
'flanke kommt zu früh: paket verwerfen
Ir_bitcount = 0
Else
'nur Flankenwechsel in Bit-Mitte berücksichtigen
If Ir_bittimer >= Samples_early Then
If Ir_bittimer <= Samples_late Then
'Bit speichern
Shift Ir_data_tmp , Left , 1
Ir_data_tmp = Ir_data_tmp + Sample
Incr Ir_bitcount
Else
'Flankenwechsel zu spät: Neuanfang mit gemessener Flanke
Ir_bitcount = 1
Ir_data_tmp = Sample
End If
'bittimer zurücksetzen wenn Timer > Samples_early
Ir_bittimer = 0
End If
End If

'Kontrolle des Startbits auf 1
If Ir_bitcount = 1 Then Ir_bitcount = Ir_data_tmp.0

'Alle 14 Bits gelesen?
If Ir_bitcount >= 14 Then
Command_rc5 = Ir_data_tmp 'Bit 6 und 7 siehe unten
Shift Ir_data_tmp , Right , 6
Address_rc5 = Ir_data_tmp And &B00011111
'For extended RC5 code, the extended bit is bit 6 of the command.
Command_rc5.6 = Not Ir_data_tmp.6
'The toggle bit is stored in bit 7 of the command
Command_rc5.7 = Ir_data_tmp.5
'Paket erfolgreich gelesen
Set Rc5_flag
'paket zurücksetzen
Ir_bitcount = 0
End If

End If
'sample im samplepuffer ablegen
Ir_lastsample = Sample

ReturnCodelänge: 483Word = 966 Byte, d.h. zusätzlich nur 78 Byte

-tomas-
31.05.2006, 00:28
da ich mich wunderte, warum Bascom bei den paar Codezeilen 966 Byte verbraucht, habe ich den Bascom-Code mal 1:1 in Avr-gcc umgeschrieben:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h> // wg. char-print

// interner RC-Oszillator
#define F_CPU 8.0E6

//'Timeraufruf alle 178µs (10 Samples = 1 Bit = 1,778ms)
#define INTERRUPT_PRELOADER 78

#define UART_BAUD_RATE 9600 // die gewünschte Baudrate
#define UART_BAUD_SELECT (F_CPU/(UART_BAUD_RATE*16L)-1)

//UART -----------------------------------------------------------
char myCharBuffer[10];
char *myCharPtr;

void UART_init(void){
UBRRL |= (uint8_t) UART_BAUD_SELECT;
UCSRB = (1<<RXEN)|(1<<TXEN); //Sender & Empfänger aktivieren
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); //Asynchron 8N1
}

void UART_transmit(uint8_t c){
while(!(UCSRA & (1<<UDRE)));
UDR = c;
}

void UART_transmit_string(uint8_t *string){
while(!(UCSRA & (1<<UDRE)));
while( *string){ UART_transmit (*string++); }
}

void UART_transmit_byte(uint8_t b){
myCharPtr = myCharBuffer;
myCharPtr = itoa( b,myCharPtr,10);
UART_transmit_string(myCharBuffer);
}

//Timing für 10 Samples Per Bit = 1,778ms
#define Samples_early 8 //Flanke Frühestens Nach 8 Samples
#define Samples_late 12 //Flanke Spätestens Nach 12 Samples
#define Samples_min 3 //Flanke Vor 3 Samples - > Paket Verwerfen

//Variablen der ISR
volatile uint8_t Sample; //eigentlich Bit
volatile uint8_t Ir_lastsample; //zuletzt gelesenes Sample
volatile uint8_t Ir_bittimer; //zählt die Aufrufe von Timer_IRQ
volatile uint16_t Ir_data_tmp; //Bitstream
volatile uint8_t Ir_bitcount; //Anzahl gelesener Bits

//Rückgabewerte der ISR
volatile uint8_t Address_rc5;
volatile uint8_t Command_rc5;
volatile uint8_t Rc5_flag; //eigentlich Bit

// Initialisierung der Hardware
void ioinit()
{
// Initialisiert Timer0
TCCR0 |= (1<<CS01); //Prescale=8
// TCNT0 = INTERRUPT_PRELOADER; //Counter
TIMSK |= (1<<TOIE0); //Timer Overflow Interrupt
}

// Das Hauptprogramm
int main()
{
// Peripherie initialisieren
ioinit();
UART_init();

//Pin für TSOP1736
DDRB &= ~(1<<DDB0); // Eingang eh vorhanden

// Interrupts aktivieren
sei();

// Eine Endlosschleife.
while (1)
{
if (Rc5_flag == 1)
{
Rc5_flag = 0;
UART_transmit_string("\r\ntoggle:");
UART_transmit_byte((Command_rc5 & (1<<7))>>7);

//clear the toggle bit
Command_rc5 &= 0b01111111;

UART_transmit_string(" Adresse:");
UART_transmit_byte(Address_rc5);
UART_transmit_string(" Code:");
UART_transmit_byte(Command_rc5);
}
}
}

// Die Interrupt Service Routine (ISR)
SIGNAL(SIG_OVERFLOW0)
{
TCNT0 = INTERRUPT_PRELOADER;
Sample = !(PINB & (1<<PINB0));

//'bittimer erhöhen (bleibt bei 255 stehen)
if (Ir_bittimer < 255) Ir_bittimer += 1;

//flankenwechsel erkennen
if (Ir_lastsample != Sample)
{
if (Ir_bittimer <= Samples_min)
{
//flanke kommt zu früh: paket verwerfen
Ir_bitcount = 0;
}
else
{
//nur Flankenwechsel in Bit-Mitte berücksichtigen
if (Ir_bittimer >= Samples_early)
{
if (Ir_bittimer <= Samples_late)
{
//Bit speichern
Ir_data_tmp = (Ir_data_tmp << 1);
Ir_data_tmp = Ir_data_tmp + Sample;
Ir_bitcount +=1;
}
else
{
//Flankenwechsel zu spät: Neuanfang mit gemessener Flanke
Ir_bitcount = 1;
Ir_data_tmp = Sample;
}
//bittimer zurücksetzen wenn Timer > Samples_early
Ir_bittimer = 0;
}
}

//Kontrolle des Startbits auf 1
if (Ir_bitcount == 1) Ir_bitcount = Ir_data_tmp & 1; //Bit 0

//Alle 14 Bits gelesen?
if (Ir_bitcount >= 14)
{
Command_rc5 = Ir_data_tmp & 0x3F ; //Bit 6 und 7 siehe unten
Ir_data_tmp = (Ir_data_tmp >> 6);
Address_rc5 = Ir_data_tmp & 0b00011111;
//For extended RC5 code, the extended bit is bit 6 of the command.
if ((Ir_data_tmp & (1<<6))==0) Command_rc5 |= (1<<6);
//The toggle bit is stored in bit 7 of the command
Command_rc5 |= ( (Ir_data_tmp & (1<<5)) <<2 );
//Paket erfolgreich gelesen
Rc5_flag=1;
//paket zurücksetzen
Ir_bitcount = 0;
}

}
//sample im samplepuffer ablegen
Ir_lastsample = Sample;
}

Trotz Compiler-Direktive "OPTIMIZE = -O2" beträgt die Codelänge in C erstaunliche 912Byte (gegenüber 966Byte in Bascom).

Fazit: Bascom macht hier keine schlechte Figur. Das Hauptproblem von Bascom ist das fehlende Registerrechnen. Ständig wird alles vom RAM hin- und hergeschaufelt. Da in gcc die Variablen für die ISR mit Volatile definiert werden müssen, kommt diese WINAVR-Optimierung nicht zum Zuge. (Volatile erfordert ca. 82 Byte zusätzlichen Code).

Markant ist in AVR-gcc die fehlende Push-Pop Orgie in der ISR:

¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ISR SIG_OVERFLOW0 in AVR-GCC ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
ROM:00BD push r1
ROM:00BE push r0
ROM:00BF in r0, SREG
ROM:00C0 push r0
ROM:00C1 clr r1
ROM:00C2 push r18
ROM:00C3 push r24
ROM:00C4 push r25
...
ROM:016D pop r25
ROM:016E pop r24
ROM:016F pop r18
ROM:0170 pop r0
ROM:0171 out SREG, r0
ROM:0172 pop r0
ROM:0173 pop r1
ROM:0174 reti


bekanntlich langt hier Bascom ordentlich zu

¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ISR Timer0 in BASCOM ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
ROM:008F push r0
ROM:0090 push r1
ROM:0091 push r2
ROM:0092 push r3
ROM:0093 push r4
ROM:0094 push r5
ROM:0095 push r7
ROM:0096 push r10
ROM:0097 push r11
ROM:0098 push r16
ROM:0099 push r17
ROM:009A push r18
ROM:009B push r19
ROM:009C push r20
ROM:009D push r21
ROM:009E push r22
ROM:009F push r23
ROM:00A0 push r24
ROM:00A1 push r25
ROM:00A2 push r26
ROM:00A3 push r27
ROM:00A4 push r28
ROM:00A5 push r29
ROM:00A6 push r30
ROM:00A7 push r31
ROM:00A8 in r24, SREG
ROM:00A9 push r24
...
ROM:0154 pop r24
ROM:0155 out SREG, r24
ROM:0156 pop r31
ROM:0157 pop r30
ROM:0158 pop r29
ROM:0159 pop r28
ROM:015A pop r27
ROM:015B pop r26
ROM:015C pop r25
ROM:015D pop r24
ROM:015E pop r23
ROM:015F pop r22
ROM:0160 pop r21
ROM:0161 pop r20
ROM:0162 pop r19
ROM:0163 pop r18
ROM:0164 pop r17
ROM:0165 pop r16
ROM:0166 pop r11
ROM:0167 pop r10
ROM:0168 pop r7
ROM:0169 pop r5
ROM:016A pop r4
ROM:016B pop r3
ROM:016C pop r2
ROM:016D pop r1
ROM:016E pop r0
ROM:016F reti

Marco78
20.06.2006, 22:05
Codelänge: 483Word = 966 Byte

Der Code belegt nur noch 956Bytes, wenn in Bascom unter Options/Compiler/Output die Option Optimize Code ausgewählt wird.

Soll keine Kritik sein, ich wollte es nur als Tipp erwähnen.

-tomas-
20.06.2006, 22:20
Schalter ist bekannt - was BASCOM Optimize nennt ist mit Blick auf die WINAVR-Compilerdirektiven peinlich.
Habe in den Foren eher schlechtes zu den Bascom-Schalter gelesen, darum ist er bei mir als Standard deaktiviert.

themaddin
21.06.2006, 17:11
Hallo!

Ich habe heute versucht dein Beispiel zu verwenden. Leider funktioniert es nicht. Ich verwende einen Mega32 mit 16MHz und einen TSOP1738
Den Timer0 habe ich wie folgt eingestellt:

Config Timer0 = Timer , Prescale = 64
On Timer0 Timer_irq
Const Timervorgabe = 846 'Timeraufruf alle 178µs (10 Samples = 1 Bit = 1,778ms)
Enable Timer0 'Hier werden die Timer aktiviert
Enable Interrupts

'Timing für 10 Samples Per Bit = 1,778ms
Const Samples_early = 8 'Flanke Frühestens Nach 8 Samples
Const Samples_late = 12 'Flanke Spätestens Nach 12 Samples
Const Samples_min = 3 'Flanke Vor 3 Samples - > Paket Verwerfen

Kommt das so hin?

Ach ja, mit dem Bascom GetRc5-Befehl funktioniert alles.

MfG
Martin

-tomas-
22.06.2006, 08:57
nimm zur Not zur Berechnung von
Const Timervorgabe = das Tool Avr Timer-Berechnung von Frank
https://www.roboternetz.de/phpBB2/dload.php?action=file&file_id=169
Das Tool berücksichtigt leider nicht die 52 Takte aus der PUSH-Orgie von 26 Registern (siehe Code oben) und kommt dann
bei 8 Mhz auf Const Timervorgabe = 81 (anstatt 78 ) bzw
bei 16 Mhz auf Const Timervorgabe = 212.

eine Erklärung zur Timerberechnung findest Du hier:
https://www.roboternetz.de/wissen/index.php/Avr unter 4. Timer/Counter

SprinterSB
22.06.2006, 09:08
da ich mich wunderte, warum Bascom bei den paar Codezeilen 966 Byte verbraucht, habe ich den Bascom-Code mal 1:1 in Avr-gcc umgeschrieben [...]Trotz Compiler-Direktive "OPTIMIZE = -O2" beträgt die Codelänge in C erstaunliche 912Byte (gegenüber 966Byte in Bascom).


Mal abgesehen davon, daß du durch deine Programmierung ineffizenten Code erzwingst:

Wenn man auf Laufzeit optimiert und nicht auf Größe, darf man auch nicht erwarten, daß auf Größe optimiert wird... (BTW, mit -=O2 bekomm ich mit deinem Code 834 Bytes).

Bereits das entfernen eines überflüssigen(!) Qualifiers bringt ne Codeersparnis von über 40 Bytes... Wieviel weiter noch überflüssig sind ist wohl müssig nachzuschauen. Innerhalb von einigen Minuten hätte man den Code locker unter 700 Bytes

-tomas-
22.06.2006, 09:32
@SprinterSB
ich war selber erschrocken, wie ineffizent der Bascom-Code in WINAVR ist:

Bascom-Code mal 1:1 in Avr-gcc umgeschrieben
Die Unterschiede zwischen Bascom und GCC liegen nicht nur in den Sprachelementen - die Intelligenz sollte VOR dem Rechner sitzen :-)

... aber bitte jetzt keine weitere Grundsatz-Diskussion zu Bascom<->WINAVR !

SprinterSB
22.06.2006, 09:44
Darum geht es auch nicht.

Wenn du es 1:1 übertragen möchtest, dann musst du auch die lokalen Variablen volatile machen. Versuch das mal, und gcc macht Code, der locker doppelt so groß ost wie der von BASCOM...

themaddin
22.06.2006, 10:48
Oh mann! Hatte wohl gestern wieder ein Brett vorm Kopf!!! Was hab ich da ausgerechnet???? :D

Vielen Dank! Mit Const Timervorgabe = 212 funktioniert es einwandfrei!
Das Tool kannte ich noch nicht, wieder was gelernt! :)

MfG
Martin

Dirk
22.06.2006, 11:55
Hallo -tomas-,


Das Tool berücksichtigt leider nicht die 52 Takte aus der PUSH-Orgie von 26 Registern (siehe Code oben)

1. Frage: Was hat die Berechnung der Timerwerte mit dem Sichern der Register bei der ISR (also auch mit der Länge des Codes in der ISR) zu tun?

2. Mit CALL und RETI sind es sogar je 57 Zyklen am Anfang und Ende jeder ISR!

Gruß Dirk

-tomas-
22.06.2006, 15:00
@Dirk
zu 1: wenn der Timer ein Prescale von 64 hat und Du erst ca. 60 Takte mit Push und andere Dingen verbrauchst, bevor Du den Timer mit

Timer0 = Timervorgabe neu belädst, dann wäre eigentlich richtig
Timer0 = (Timervorgabe+1) 'Pseudocode

zu 2. Die Takte am Ende beeinflussen nicht mehr das Timing des Timers

@SprinterSB
wie ich schon sagte, es war nicht meine Absicht 1:1 zu programmieren.
Ich konnte es nicht besser (es war meine erste Begegnung mit WinAVR :oops: )
Das gut programmierter gcc-Code eher an ASM als an Bascom heranreicht ist unstrittig.

Dirk
27.06.2006, 20:24
@-tomas-:


Das Tool berücksichtigt leider nicht die 52 Takte aus der PUSH-Orgie von 26 Registern (siehe Code oben) und kommt dann
bei 8 Mhz auf Const Timervorgabe = 81 (anstatt 78 )...

... das hat mich als BASCOM-User noch weiter beschäftigt. Die Abweichungen gegenüber der "normalen" Berechnung von Timer-Startwerten sind ja teilweise gigantisch.

Ich habe da 'mal einen "Rechner" für Timer-Startwerte für BASCOM in Excel geschrieben. Dabei bin ich von 53 Takten ausgegangen, die am Anfang jeder ISR für das Sichern der Register gebraucht werden.

Könntest du das 'mal testen und mir evtl. Fehler schreiben!?

Gruß Dirk

roboterheld
07.10.2007, 19:22
hallo, welche aufgabe hat eigentlich die : $lib "mcsbyte.lbx" ?

danke.

mfg

roboterheld
07.10.2007, 21:34
das heutige bascom macht ungefähr 1,3kbyte , also knapp 300 byte mehr.
also ist schon ganz schön verschwenderisch geworden.

mfg

roboterheld
07.10.2007, 21:46
also bei mir funktioniert das obige neue originalprogramm ohne änderung.



mfg

roboterheld
07.10.2007, 21:55
....Const Timervorgabe = 846.....

ausserdem geht timer0 nur bis 255.

flipp86
16.06.2009, 15:47
hey leute.

sorry das ich diesen thread nochmal rauskrame, aber ich benötige genau diese rc5 funktion.
es klappt auch alles wunderbar wenn ich nen atmega16 nehme.

ich wollte aber 4 PWM kanäle benutzen und das glaub ich ging mit der funktion nicht, da der timer0 ja schon benutzt wird. jedenfalls hab ich es nicht hinbekommen.

deshalb, und weil ich sowieso eigentlich nen atmega88 benutze, wollte ich den code jetzt auf diesen übertragen.

eigentlich müsste doch auch alles gleich bleiben, bis auf das ich den regfile und den pin für den rc5 empfang anpassen muss oder ??

tue ich das, passiert aber leider nix mehr. der atmega88 läuft auch auf 8mhz, also müsste sich bei der Timervorgabe nix ändern.

hab ich was übersehen ?? hat jemand ne idee ??

mfg Flipp



EDIT: hat sich geklärt.
man muss natürlich das ClockDiv8-Fuse abschalten.
vielen dank an michael

LIGHT_MASTER
08.03.2011, 11:03
Moin...

Könnte mir jemand erklären warum das command_rc5 nicht wieder auf null gesetzt wird wenn die die Taste auf der FB los lasse bzw. wie ich es wieder auf null setzen kann?!


Vielen Dank...


LIGHT_MASTER

Bumbum
25.05.2011, 12:54
Hallo,

ich habe den oben genannten Code gerade versucht, leider (noch) ohne Erfolg.
Als µC habe ich einen ATTiny4313 mit einem 8Mhz Quarz. (Exakt 8000000Hz). Die Fuse mit dem Clock-Teiler um den Faktor 8 ist gesetzt.
Die Werte für den Timer müssten also gleich bleiben.
Es ändert sich beim drücken einer Taste auf der Fernbedienung auch die Werte Ir_bittimer und Ir_bitcount. Allerdings kommt der Wert Ir_bitcount nie über 3 Hinaus. (Mit folgender Erweiterung ermittelt: if (Ir_bitcount > Ir_bitcountMax) Ir_bitcountMax = Ir_bitcount;

Woran kann das liegen? Als Fernbedienung verwende ich eine von Technics zum Receiver SA-DX950. Auf der Fernbedienung steht EUR7702030. Laut Google müsste das eine RC5-Code-Fernbedienung sein.
Als Empfänger habe ich einen SFH5110-36 von Reichelt.

Wie kann ich den Fehler weiter eingrenzen?

Viele Grüße
Andreas