 Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Hallo Zusammen, ich habe folgendes Problem:
Ich möche den RC5 Code mit einem Attiny2313 decodeieren. Dazu habe ich
aus dem Forum den Code von Peter Dannegger genommen und an den
Attiny2313 angepsst. Leider bekomme ich keine Ausgaben an der seriellen
Schnittstelle.
Ich verwende ein STK500 und habe einen externen 4MHz Quarz
angeschlossen.
Hier meine Code:
Main.c:
/***********************************************************************
*/
/*
*/
/* RC5 Remote Receiver
*/
/*
*/
/* Author: Peter Dannegger
*/
/* danni@specs.de
*/
/*
*/
/***********************************************************************
*/
#include "main.h"
#include <avr/io.h>
#ifndef F_CPU
#define F_CPU 4000000UL
#endif
#include<util/delay.h>
#include<stdint.h>
#include<avr/interrupt.h>
#include<stdlib.h>
void putchar( char c )
{
while ( !(UCSRA & (1<<UDRE)) )
UDR = c;
}
void puts( char *s )
{
while( *s )
putchar( *s++ );
}
int main( void )
{
DDRD |= (1<<PD6) ; // PD6 Ausgang
PORTD |= (1<<PD6); // DEBUG-LED LED1 an
unsigned int i;
char s[30];
TCCR0B = 1<<CS02; //divide by 256
TIMSK = 1<<TOIE0; //enable timer interrupt
// UBRRL = 12; //set baud rate
// UBRRH = 0x00;
UBRRL = (bauddivider); //set baud rate
UBRRH = (bauddivider >> 8);
UCSRA = 0; //no U2X, MPCM
UCSRC = 1<<UMSEL^1<<UCSZ1^1<<UCSZ0; //8 Bit
UCSRB = (1<<TXEN) | (1<<RXCIE);
sei();
puts( "RC5-Dekoder:\n\r" );
for(;;){ // main loop
cli();
i = rc5_data; // read two bytes from interrupt !
rc5_data = 0;
sei();
if( i ){
//DDRB = i; // LED output
putchar(( i >> 11 & 1) + '0'); // Toggle Bit
putchar(' ');
itoa( i >> 6 & 0x1F, s, 10); // Device address
puts( s );
putchar(' ');
itoa((i & 0x3F) | (~i >> 7 & 0x40), s, 10); // Key Code
puts( s );
puts( "\n\r" );
}
}
}
main.h:
/***********************************************************************
*/
/*
*/
/* RC5 Remote Receiver
*/
/*
*/
/* Author: Peter Dannegger
*/
/* danni@specs.de
*/
/*
*/
/***********************************************************************
*/
#include <avr/io.h>
#include <avr/interrupt.h>
//#include <avr/signal.h>
#include <avr/include/stdlib.h>
#ifndef F_CPU
#define F_CPU 4000000UL
#endif
#define uchar unsigned char
#define uint unsigned int
#define xRC5_IN PIND
#define xRC5 PD2 // IR input low active
//#define XTAL F_CPU
//#define XTAL 11.0592e6
#define XTAL 4000000UL
//#define XTAL 5e6
#define BAUD 19200
#define bauddivider (uint)((XTAL/(16UL * BAUD)-1))
extern uint rc5_data; // store result
rc5.c:
/***********************************************************************
*/
/*
*/
/* RC5 Remote Receiver
*/
/*
*/
/* Author: Peter Dannegger
*/
/* danni@specs.de
*/
/*
*/
/***********************************************************************
*/
#include "main.h"
#include <avr/io.h>
#include <avr/include/stdlib.h>
#include <avr/delay.h>
#define RC5TIME 1.778e-3 // 1.778msec
#define PULSE_MIN (uchar)(XTAL / 512 RC5TIME 0.4 + 0.5)
#define PULSE_1_2 (uchar)(XTAL / 512 RC5TIME 0.8 + 0.5)
#define PULSE_MAX (uchar)(XTAL / 512 RC5TIME 1.2 + 0.5)
uchar rc5_bit; // bit value
uchar rc5_time; // count bit time
uint rc5_tmp; // shift bits in
uint rc5_data; // store result
SIGNAL (SIG_OVERFLOW0)
{
uint tmp = rc5_tmp; // for faster access
TCNT0 = -2; // 2 * 256 = 512 cycle
if( ++rc5_time > PULSE_MAX ){ // count pulse time
if( !(tmp & 0x4000) && tmp & 0x2000 ) // only if 14 bits received
rc5_data = tmp;
tmp = 0;
}
if( (rc5_bit ^ xRC5_IN) & 1<<xRC5 ){ // change detect
rc5_bit = ~rc5_bit; // 0x00 -> 0xFF -> 0x00
if( rc5_time < PULSE_MIN ) // to short
tmp = 0;
if( !tmp || rc5_time > PULSE_1_2 ){ // start or long pulse time
//eingefügt
#if 1
/*
Anschluss PD6----###--->|---- GND
*/
DDRD |= (1<<PD6); // PD6 auf Ausgang
// _delay_ms(500);
PORTD ^= (1<<PD6); // LED1 an PD6 togglen
#endif
if( !(tmp & 0x4000) ) // not to many bits
tmp <<= 1; // shift
if( !(rc5_bit & 1<<xRC5) ) // inverted bit
tmp |= 1; // insert new bit
rc5_time = 0; // count next pulse time
}
}
rc5_tmp = tmp;
}
Kann mir jemand sagen, was ich falsch mache?
Vielen Dank im Voraus.
Gruß
Jonanova
Kann es sein, dass du nicht den Originalcode von Peter genommen hast,
sondern Teile der Attiny2313-Anpassung für das [[Pollin
Funk-AVR-Evaluationsboard]]. Ich meine in main() und in der SIGNAL
(SIG_OVERFLOW0) hardwarespezifische Anweisungen dafür zu erkennen.
Funktioniert dein UART ohne den RC5 Teil überhaupt? Das wäre ein Hinweis
wo du nach dem Fehler suchen kannst. Ich würde mit dieser Zeile
anfangen:
UCSRB = (1<<TXEN) | (1<<RXCIE);
Es ist nämlich übel, wenn man keinen Signalhandler (ISR) für den
Zeichen-Empfangen-Interrupt hat und diesen Interrupt enabled.
1:
http://www.mikrocontroller.net/articles/Pollin_Funk-AVR-Evaluationsboard#RC5_Empf.C3.A4nger
Sieh dir auch mal diesen Code GENAU an
1 | void putchar( char c )
| 2 | {
| 3 | while ( !(UCSRA & (1<<UDRE)) )
| 4 | UDR = c;
| 5 | }
|
und dann überlege (auch mal mit dem Datenblatt abklären, was hier
eigentlich warum gemacht wird) ob das so stimmen kann.
Hallo Zusammen,
@ Stefan: Ja ich habe auch etwas aus dem Pollin teil genommen und
angepasst. Ich habe den Code ohne RC5 nicht getestet.
Wie könnte ich das tun ohne den code nicht löschen zu müssen?
@ Karl Heinz
Ich muss sagen, dass ich noch ziemlich am Anfang bin, was
C-Programmierung betrifft und deswegen nicht weiß was der Code macht.
while ( !(UCSRA & (1<<UDRE)) )
UDR = c;
Wenn ich statt UDRE RXC reinschreibe, dann Bekomme ich laufend ein "R",
was aus diesem
puts( "RC5-Dekoder:\n\r" );
kommt.
Kann mir jemand sagen, was ich dazu schreiben könnte um die serielle
Schnittstelle zu testen unabhängig vom RC5??
Vielen Dank im Voraus
Gruß
Jonanova
avr-gcc-Tutorial
Abschnitt "UART"
Du hättest dir auch den Teil UART beim Attiny2313 anschauen sollen. Das
Bit UMSEL hat der/braucht der nicht. Da weicht die Initialisierung der
UART von dem AVR ab, den Peter benutzte.
Wenn ich größere Änderungen machen muss und den Restcode nicht verlieren
möchte, verwende ich eine von diesen Methoden
1) Neues Projekt anlegen und altes Projekt rüberkopieren. Dann nach
Herzenslust im neuen Projekt löschen oder ändern. Änderungen nach Erfolg
in das alte Projekt zurückführen.
2) Mit #if 0 .... #endif Codeteile per C-Präprozessor auskommentieren.
Mit einem zusätzlichen #else kann man einfach zwischen Varianten
umschalten. Im finalen Code sollten möglicht wenige dieser Reste stehen,
weil sie die Übersicht nehmen.
ich habe mir jetzt folgendes Projekt angelegt um etwas auf die Serielle
Schnittstelle zu bekommen, leider funktioniert das auch nicht. Könnt ihr
mir sagen warum nicht???
#include <avr/io.h>
#ifndef F_CPU
#define F_CPU 4000000UL
#endif
#include<util/delay.h>
#include<stdint.h>
#include<avr/interrupt.h>
#include<stdlib.h>
#define uint unsigned int
#define BAUD 19200
#define bauddivider (uint)(F_CPU BAUD 16 - 1)
int main ()
{
UBRRL = (bauddivider & 0x0ff); //set baud rate
UBRRH = bauddivider>>8;
UCSRA = 0; //no U2X, MPCM
UCSRC |= (0 << USBS) | (3 << UCSZ0);
UCSRB |= (1<<RXEN) | (1<<TXEN) ;//| (1<<RXCIE); //enable RX, TX
while (1)
{
printf ("hallo");
}
return 0;
}
Vielen Dank im Voraus!!
Gruß
Jonanova
Und was veranlasst dich zu der Vermutung, dass printf seine Ergüsse über
die serielle Schnittstelle ausgeben wird?
Warum sollte printf nicht auf 'dem LCD' ausgeben. Oder über SPI?
Nochmal: Schau ins Tutorial. Da gibt es einen Abschnitt über UART. Dort
steht alles drinn, was du wissen musst und dort gibt es auch ein paar
oft benötigte Routinen. Selbst wenn man die nicht 1:1 benutzen kann
(weil zb ein paar Register anders heißen), so gibt es doch genügend
Hinweise, dass man sich die Dinge auf seine Gegebenheiten anpassen kann.
Sorry aber ich steh voll auf dem Schlauch. Ich hab den Code laut dem Tut
so abgeändert aber es tut sich immer noch nichts.
#include <avr/io.h>
#ifndef F_CPU
#define F_CPU 4000000UL
#endif
#include<util/delay.h>
#include<stdint.h>
#include<avr/interrupt.h>
#include<stdlib.h>
#include <stdio.h>
#define uint unsigned int
#define BAUD 19200
#define bauddivider (uint)(F_CPU BAUD 16 - 1)
int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))) /* warten bis Senden moeglich */
{
}
UDR = c; /* sende Zeichen */
return 0;
}
int main ()
{
UBRRL = (bauddivider & 0x0ff); //set baud rate
UBRRH = bauddivider>>8;
UCSRA = 0; //no U2X, MPCM
UCSRC |= (0 << USBS) | (3 << UCSZ0);
UCSRB |= (1<<RXEN) | (1<<TXEN) ;//| (1<<RXCIE); //enable RX, TX
while (1)
{
uart_putc ("hallo \0");
}
return 0;
}
Danke für die Geduld.
Gruß
jonanova
Was mich immer wieder verblüfft ist, das der Compiler hier
uart_putc ("hallo \0");
keine Warnung gibt, bzw. dass Programmierer diese Warnung ignorieren.
uart_putc gibt ein einzelnes Zeichen (einen einzelnen char) aus.
Du hast aber kein einzelnes Zeichen. Du hast einen String!
http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F
1 | #include <avr/io.h>
| 2 |
| 3 | #ifndef F_CPU
| 4 | #define F_CPU 4e6
| 5 | #endif
| 6 |
| 7 | // Baudratenfehler bei 19200 Baud und 4 MHz im Datenblatt nachsehen!
| 8 | #define BAUD 19200
| 9 | #define bauddivider (unsigned int)((F_CPU/(16UL * BAUD)-1))
| 10 |
| 11 | void uart_putc(unsigned char c)
| 12 | {
| 13 | while (!(UCSRA & (1<<UDRE))) /* warten bis Senden moeglich */
| 14 | {
| 15 | }
| 16 | UDR = c; /* sende Zeichen */
| 17 | }
| 18 |
| 19 |
| 20 | void uart_puts(char * s)
| 21 | {
| 22 | while (*s) /* so lange Zeichen vorhanden */
| 23 | {
| 24 | uart_putc(*s++);
| 25 | }
| 26 | }
| 27 |
| 28 |
| 29 | int main(void)
| 30 | {
| 31 | UBRRL = (bauddivider & 0x0ff); //set baud rate
| 32 | UBRRH = bauddivider>>8;
| 33 |
| 34 | UCSRA = 0; // no U2X, MPCM
| 35 | UCSRC = 3<<UCSZ0; // Frame format: 8N1
| 36 | UCSRB = (1<<RXEN) | (1<<TXEN);
| 37 |
| 38 | while(1)
| 39 | {
| 40 | uart_puts("Hallo\r\n");
| 41 | }
| 42 | return 0;
| 43 | }
|
Hallo Stefan,
Vielen Dank für deinen Code.
Leider bekomme ich auch hier keine Anzeige auf dem Terminal.
Woran könnte das liegen?
Ich habe ein STK500 mit einem 4MHZ Quarz. Auf dem Board habe ich PD0 und
PD1 mit RXD und TXD verkabelt und das Serielle Kabel ist an RS232 Spare
angeschlossen.
Vielen Dank.
Hallo,
ich nehm alles zurück. Der Code macht genau das was er soll. Ich hatte
im AVR die HEX-Datei vom RC5 code drin.
sorry.
Jetzt weiß ich dass die USART tut.
Weiß jemand von euch was ich am RC5 anpassen muss damit er das auch
ausgibt was er da misst?
Vielen Dank im Voraus.
Gruß
Jonanova
Was spricht dagegen, die beiden Code-Quellen zu kombinieren?
Du hast ein Projekt mit funktionierenden UART Routinen und du hast
eines, bei dem sie nicht funktionieren. Also nimmst du die
funktionierenden und ersetzt die nichtfunktionierenden. Dann passt du
das Hauptprogramm noch an eventuell veränderte Funktionsnamen an und
.... siehst nach, was dann die Ausgabe ist.
Alternativ kannst du natürlich auch deinen nichtfunktionierenden
UART-Code mit dem funktionierenden vergleichen und darauf achten, was du
beim Abtippen vergessen hast :-)
Meine optimistische Vermutung: Den RC5 Code baust du jetzt in das
funktionierende UART Beispiel ein und er wird grundsätzlich
funktionieren.
Oder du korrigierst die 2 wesentlichen Punkte in deinem ersten Beitrag.
Vorausgesetzt die Hardwareschaltung dahinter ist richtig angeschlossen
und deine Testfernbedienung schickt auch einen RC5-Code.
Und das Timing passt zu dem 4 MHz Quarz. Peters Original hatte ja
11.paarzerquetschte MHz, mein Beispiel auf dem Pollinboard hatte 8 MHz
(ging), du versuchst es jetzt mit 4 MHz...
Die Debug-LED aus meinem Beispiel an PD6 fand ich ganz nützlich, um zu
sehen, ob der IR-Empfänger was zum µC schaufelt, hast du sowas auch?
Das habe ich soeben gemacht, aber irgendetwas verhindert, dass er zum
senden kommt.
ich habe den Code in der main.c jetzt so angepasst:
#include "main.h"
#include <avr/io.h>
#include<util/delay.h>
#include<stdint.h>
#include<avr/interrupt.h>
#include<stdlib.h>
#ifndef F_CPU
#define F_CPU 4e6
#endif
// Baudratenfehler bei 19200 Baud und 4 MHz im Datenblatt nachsehen!
#define BAUD 19200
#define bauddivider (unsigned int)((F_CPU/(16UL * BAUD)-1))
void putchar( char c )
{
while ( !(UCSRA & (1<<UDRE)))
UDR = c;
}
void puts( char *s )
{
while( *s )
putchar( *s++ );
}
int main( void )
{
DDRD |= (1<<PD6) ; // PD6 Ausgang
PORTD |= (1<<PD6); // DEBUG-LED LED1 an
uint i;
char s[30];
TCCR0B = 1<<CS02; //divide by 256
TIMSK = 1<<TOIE0; //enable timer interrupt
UBRRL = (bauddivider & 0x0ff); //set baud rate
UBRRH = bauddivider>>8;
UCSRA = 0; //no U2X, MPCM
UCSRC |= (3 << UCSZ0);
UCSRB |= (1<<RXEN) | (1<<TXEN) ; //enable RX, TX
puts( "RC5-Dekoder:\n\r" );
sei();
puts( "RC5-Dekoder:\n\r" );
for(;;){ // main loop
cli();
i = rc5_data; // read two bytes from interrupt !
rc5_data = 0;
sei();
if( i ){
//DDRB = i; // LED output
putchar(( i >> 11 & 1) + '0'); // Toggle Bit
putchar(' ');
itoa( i >> 6 & 0x1F, s, 10); // Device address
puts( s );
putchar(' ');
itoa((i & 0x3F) | (~i >> 7 & 0x40), s, 10); // Key Code
puts( s );
puts( "\n\r" );
}
}
}
leider gibt er mir das nicht aus: puts( "RC5-Dekoder:\n\r" );
Gruß
Valentin S. schrieb:
> Das habe ich soeben gemacht, aber irgendetwas verhindert, dass er zum
> senden kommt.
>
> ich habe den Code in der main.c jetzt so angepasst:
Nein, hast du nicht. Da ist immer noch derselbe Fehler
void putchar( char c )
{
while ( !(UCSRA & (1<<UDRE)))
UDR = c;
}
Vergleich doch mal mit der uart_putc.
Das ist die korrespondierende Funktion in den funktionierenden Routinen.
Und noch ein Hinweis: Dem Compiler ist es völlig egal wie du einrückst.
Das beeinflusst ihn nicht im geringsten, welche Anweisung von der while
Schleife abhängt und welche nicht.
> void putchar( char c )
> {
> while ( !(UCSRA & (1<<UDRE)))
> UDR = c;
> }
immer noch falsch. Weiter habe ich nicht geschaut.
Ich habe auch die Debug LED dran.
Wenn ich die Fernbedienung drücke, dann flackert die LED. Die LED ist
ansonsten immer an.
Mit nem Oszi habe ich auch den Ausgang gemessen und die Schaltung vom IR
Empfänger hatte ich auch schon an nem Atmega 8 laufen.
Sorry,
ihr habt recht.
Jetzt spuckt er auch "RC5-Dekoder:" aus.
Das mit dem Code leider noch nicht.
tut mir leid, dass ich mich so dämlich anstelle.
Valentin S. schrieb:
> tut mir leid, dass ich mich so dämlich anstelle.
Bitte versteh auch, dass wir dir zwar gerne Hinweise geben aber nach
Möglichkeit keinen fix fertign Code. Es sei denn die Funktionalität ist
so schwer zu beschreiben, dass Code tatsächlich die einfachste Lösung
ist.
Du hast jetzt hoffentlich gelernt, dass es in C oftmals auf jedes
einzelne Zeichen im Quelltext ankommt. Alles und jedes hat seine
Bedeutung. Und die kann extrem wichtig sein.
Ändere in main.h und rc5.c
1 | extern volatile uint rc5_data; // store result
| 2 | uint rc5_data; // store result
|
in
1 | extern volatile uint rc5_data; // store result
| 2 | volatile uint rc5_data; // store result
|
Bei meinem uralten WinAVR beeinflusst das Weglassen des volatile die
Funktion nicht. Modernere WinAVR sind da aber pingeliger.
Danke soweit,
find ich echt Klasse, dass ihr soviel Geduld mit habt.
ich hab das volatile jetzt hinzugefügt und sihe da es tut. Jetzt spuckt
er mir ab und zu den Code Raus. Der Code ist auch richtig, aber leider
macht er das nicht mit jedem Tastendruck.
waran könnte das liegen?
Da kann ich nur raten bzw. würde ich experimentieren.
Und zwar würde ich versuchen die Herabsetzung des Taktes 11,x => 4 MHz
durch eine Verringerung des Prescalers 256 => 64 "auszugleichen".
Im Prinzip also darauf spekulieren, dass bei deinem langsamer laufenden
Timer gelegentlich RC5-Kommandosequenzen nicht oder als nicht legal
empfangen werden und dass sich das bei schneller laufendem Timer
verbessert.
Stefan B. schrieb:
> Da kann ich nur raten bzw. würde ich experimentieren.
>
> Und zwar würde ich versuchen die Herabsetzung des Taktes 11,x => 4 MHz
> durch eine Verringerung des Prescalers 256 => 64 "auszugleichen".
Auch müsste man in der ISR anpassen
TCNT0 = -2; // 2 * 256 = 512 cycle
Das wird bei einer Veränderung der Taktfrequenz nicht mehr stimmen.
Der Code muss wirklich schon sehr alt sein. Normalerweise baut PeDa das
alles so, dass sich eine Änderung der Taktfrequenz nicht auswirkt.
Ich habe den Prescaler auf 64 gesetzt in dem ich das so aus dem
Datenblatt entnommen habe
TCCR0B |= (1<<CS00) | (1<<CS01);
ist das richtig so?
Leider gibt er den Code gar nicht mehr aus.
Wie müsste ich den Wert bei TCNT0 wählen???
Ich schrieb ja, dass ich raten/experimentieren würde. Ich möchte
keinesfalls sbehaupten, dass das das Problem ist.
Peter hat schon Makros für die RC5-Pulslängen im Code, die F_CPU bzw.
XTAL berücksichtigen!
Ich habe nur nicht genau aufgedröselt, bei ob das bei 4 MHz noch
funktioniert. Denn es wäre schneller getestet als studiert :)
Ich muss das leider für eine fertige Schaltung anpassen und da habe ich
leider nur einen 4MHz Quarz zur Verfügung.
Wie könnte ich das testen? Habt ihr da ne Idee für mich?
Ich muss jetzt los werde mich dann morgen nochmal melden.
Vielen Vielen Dank an euch Beide ihr habt mir echt weiter geholfen.
Gruß
jonanova
Im Original bei 11.0592 MHz wird der Timer mit Prescaler 256 und
Vorladewert -2 alle 46,3µs aufgerufen. Das sind 512 Zyklen. Die 512
stecken auch in den Makros für die Pulsdauern
Wenn man bei 4 MHz eine ähnlich hohe Aufruffrequenz haben will, kann man
den Precaler auf 64 runtersetzen und den Vorladewert -3 benutzen. Das
ergibt dann 48 µs (64 und -2 ergäbe einen schnelleren Timer mit 32 µs,
schadet auch nicht).
Man muss dann aber die Makros für die Pulsdauern anpassen, damit die
Zyklenzahl stimmt und die Pulsdauern richtig gezählt werden. Die
richtige Zahl ist Prescaler * Betrag(Vorladewert), also bei 64 mit -3
ist der Wert fürs Makro 192 und bei 64 mit -2 ist der Wert fürs Makro
128.
Hallo Stefan,
der Tipp war goldrichtig. Vielen Vielen Dank.
Wie kann ich mich dafür bedanken?
Gruß
Jonanova
Hast du doch schon. Mich freut es, wenn eine Rückmeldung kommt "es
geht", nachdem man an einem Problem geknabbert hat.
Eine Kurze Frage habe ich noch. Wie kann ich einen Taster realisieren,
d.h. Ich möchte eine LED (zum testen erstmal) so lange leuchten lassen
wie ich Taste x gedrückt halte. Ich habe das mit einer while Schleife
probiert, bin aber gescheitert. Mit meiner jetzigen lösung toggelt er
das. Hast du eine Idee wie man das Lösen kann?
for(;;)
{ // main loop
cli();
i = rc5_data; // read two bytes from interrupt !
rc5_data = 0;
sei();
char v[14];
uint k = i;
if (k)
{
//itoa( k, s, 10 );
if(strcmp(itoa( k, s, 10) , "15376") == 0)
{
PORTB &=~(1<<PB4); // PB4 auf Low
puts("LED PB4 \n\r");
//PORTB |= (1<<PB4);
}
if (strcmp(itoa( k, s, 10) , "13329") == 0)
{
PORTB &= ~(1<<PB5); // PB5 auf Low
puts("LED PB5 \n\r");
//PORTB |= (1<<PB5);
}
PORTB |= ((1<<PB4) | (1<<PB5));
}
Wenn ich aus der If eine While Schleife mache, dann er da nicht mehr
raus.
Gruß
Jonanova
Das ist DIE Gelegenheit dich mit dem Entprellen bekannt zu machen! Peter
Dannegger hat da kürzlich eine Routine "Entprellen für Anfänger" in der
Codesammlung veröffentlicht.
Je nach Hardwareschaltung der Taster und kleinen Änderungen im Code kann
man das Leuchten-bei-Tastendruck einfach und sicher implementieren.
Vielleicht magst du meine Anpassung für Attiny2313 und active high
(Bedeutung siehe AVR-GCC-Tutorial) geschalteten Taster als Anregung
nehmen:
http://www.mikrocontroller.net/articles/Pollin_Funk-AVR-Evaluationsboard#Tasty_Reloaded
Mir ist noch etwas anderes eingefallen:
if(strcmp(itoa( k, s, 10) , "15376") == 0)
{
PORTB &=~(1<<PB4); // PB4 auf Low
//puts("LED PB4 \n\r");
_delay_ms(100);
PORTB |= (1<<PB4);
}
Gibt es da eine ellegantere Lösung?
Gruß
Jonanova
Valentin S. schrieb:
> Eine Kurze Frage habe ich noch. Wie kann ich einen Taster realisieren,
> d.h. Ich möchte eine LED (zum testen erstmal) so lange leuchten lassen
> wie ich Taste x gedrückt halte. Ich habe das mit einer while Schleife
> probiert, bin aber gescheitert. Mit meiner jetzigen lösung toggelt er
> das. Hast du eine Idee wie man das Lösen kann?
Wie wäre es. wenn du nach der Auswertung das k, bzw i bzw. rc5_data
einfach auf 0 setzt? Wenn ich mich recht erinnere, macht das auch der
PeDa Code. Schon vergessen: fremden Code genau studieren!
(PS: Zuviele Variablen mit derselben Bedeutung verwirren dich nur. Du
weisst dann nicht mehr welche eigentlich gilt, welche welchen Wert
enthält etc.
Das hier
if(strcmp(itoa( k, s, 10) , "15376") == 0)
ist wohl ein Kandidat für den OCCC in der Kategorie: Beste
Verschleierung der ursprünglichen Absicht.
Stefan B. schrieb:
> Das ist DIE Gelegenheit dich mit dem Entprellen bekannt zu machen!
Ich denke mit Taste meint er
'Taste auf der Fernsteuerung, deren Code über IR gesendet wird' und
nicht die klassische Taste, die an einem Port hängt.
Valentin S. schrieb:
> Gibt es da eine ellegantere Lösung?
Sicher.
Du hast doch einen Timer mitlaufen, dessen ISR in bestimmten
Zeitabständen (die du kennst) aufgerufen wird.
Diese ISR kann gleich auch noch das Zeitmanagement für die LED
mitmachen.
Wird die ISR zb alle 10ms aufgerufen, dann ist nach dem 100-ten Aufruf
genau 1 Sekunde vergangen
ISR( ...
{
cnt++;
if( cnt == 100 ) {
cnt = 0;
xxxx
}
...
}
Der Codeteil xxxx wird dann genau jede 1 Sekunde ausgeführt. Wenn du
hier deine LED einfach umschaltest, ist sie daher 1 Sekunde an, 1
Sekunde aus, 1 Sekunde an, 1 Sekunde aus ...
Edit: Oder hab ich dich misverstanden und du willst gar nicht Blinken
haben?
Vielleicht solltest Du auch hier mal querlesen, das betrifft auch den
Pollin-IR8-Bausatz in Version 1.2, also mit UART.
Hier für RECS80-Code:
Beitrag "Re: Quellcode für den Pollin Fernsteuer Bausatz"
Und hier für IR60-Code:
Beitrag "Re: Quellcode für den Pollin Fernsteuer Bausatz"
Durch Analyse der Kommentare (den ASM-Code musst Du nichtmal verstehen)
müsstest Du erkennen können, wie man unterschiedliches Verhalten auf
Tastendrücke realisieren kann.
Hier gibt es übrigens noch eine (unvollständige) Liste geeigneter, bei
Pollin billig erhältlicher Fernbedienungen:
Beitrag "Re: Quellcode für den Pollin Fernsteuer Bausatz"
...
FB-Taste, klar... sorry war im falschen Film. Der Tipp von Hannes ist
gut. An vorhandenem Code kann man prima lernen.
Bei "Dauerfeuer" auf einer FB-Taste ist zu beachten, dass bei
Wiederholungen des RC5 Codes die FB ein Bit im Code wechselt. D.h. für
die gleiche FB-Taste und "Dauerfeuer" bekommst du verschiendene RC5
Codes anwechselnd.
In deinem Code oben ist es sehr zeitraubend und unnötig, die Vergleiche
mit Strings zu machen. Das kann man direkt als Zahlenvergleich
programmieren.
Du willst pro FB-Taste exklusiv eine Aktion haben und die so lange, wie
die FB-Taste gedrückt wird. D.h. wenn die FB-Taste losgelassen wird,
soll die Aktion beendet werden. Richtig?
Ich würde es so probieren (switch/case nur um was Neues reinzubringen
statt des langweiligen if :)
1 | #define WARTEZYKLEN 250 // 250 * ca. 2ms = ca. 500ms
| 2 |
| 3 | for(;;)
| 4 | {
| 5 | static uint timeout = 0;
| 6 | uint neues_kommando;
| 7 |
| 8 | cli();
| 9 | neues_kommando = rc5_data; // read two bytes from interrupt !
| 10 | rc5_data = 0;
| 11 | sei();
| 12 |
| 13 | // Auswerten
| 14 | switch( neues_kommando )
| 15 | {
| 16 | case 0:
| 17 | // Kein gültiger RC5 Code empfangen
| 18 | // wenn das WARTEZYKLEN-mal hintereinander passiert
| 19 | // wurde die Taste wohl losgelassen...
| 20 | if ( timeout == WARTEZYKLEN )
| 21 | {
| 22 | // Alle Kommandos pauschal annulieren
| 23 | PORTB |= (1<<PB4) | (1<<PB5);
| 24 | puts("timeout == WARTEZYKLEN\r\n");
| 25 | } else
| 26 | timeout += 1;
| 27 | break;
| 28 | case 15376:
| 29 | // Exklusiv betätigte Taste
| 30 | // d.h. ältere Kommandos annulieren
| 31 | PORTB |= 1<<PB5;
| 32 | // Kommando ausführen
| 33 | PORTB &= ~(1<<PB4);
| 34 | puts("LED PB4\r\n");
| 35 | timeout = 0;
| 36 | break;
| 37 | case 13329:
| 38 | PORTB |= 1<<PB4;
| 39 | PORTB &= ~(1<<PB5);
| 40 | puts("LED PB5\r\n");
| 41 | timeout = 0;
| 42 | break;
| 43 | default:
| 44 | // Sonstiger RC5 Code
| 45 | // Alle Kommandos pauschal annulieren
| 46 | PORTB |= (1<<PB4) | (1<<PB5);
| 47 | // RC5 Code anzeigen
| 48 | puts("RC5 = ");
| 49 | {
| 50 | char s[6];
| 51 | utoa(neues_kommando, s, 10);
| 52 | puts(s);
| 53 | }
| 54 | puts("\r\n");
| 55 | timeout = WARTEZYKLEN + 1;
| 56 | break;
| 57 | }
| 58 | }
|
Um keinen Blinkeffekt durch den wechselnden Dauerfeuer RC5 Code zu haben
sollte man das switch oben um die zusätzlichen Codes in den case: Fällen
erweitern z.B. so (wobei im folgenden Schnippsel die Zahlen willkürlich
sind):
1 | case 15376:
| 2 | case 12345:
| 3 | // Rest wie oben
| 4 | break;
| 5 | case 13329:
| 6 | case 54321:
| 7 | // Rest wie oben
| 8 | break;
|
"Dauerfeuer" gibt es übrigens bei meinem Code parasitär, wenn man im
Repeat-Mode die Haltezeit kleiner wählt als die Telegramm-Wiederholzeit
ist, also Werte im einstelligen Bereich einsetzt.
...
Das mit dem Case ist genaial, aber er sendet jetzt permanent daten über
die USART. Kann ich die Case-Fälle auch so verwenden mit den
Zahlenwerten oder muss ich die umrechnen? Der wert "15376" ist ja nach
der itoa funktion gekommen. Ich weiß leider nicht wie ich den RC5 code
auslesen kann und dann direkt vegleichen.
Was meinst du mit dem zweiten Teil:
case 15376:
case 12345:
// Rest wie oben
break;
case 13329:
case 54321:
// Rest wie oben
break;
@Hannes: Vielen Dank für die Links ich werde sie mir am Wochenende zu
Gemüte führen.
Gruß
jonanova
Valentin S. schrieb:
> Das mit dem Case ist genaial, aber er sendet jetzt permanent daten über
> die USART. Kann ich die Case-Fälle auch so verwenden mit den
> Zahlenwerten oder muss ich die umrechnen? Der wert "15376" ist ja nach
> der itoa funktion gekommen.
BIst du dir sicher, dass du weißt, was itoa eigentlich macht?
> Ich weiß leider nicht wie ich den RC5 code
> auslesen kann und dann direkt vegleichen.
if( neues_kommando == 12345 ) {
}
Hmm. Ich habe keine Erfahrung mit diesen Codes. Daher die Frage an die
Leute, die das schon benutzt haben:
Gibt es da eigentlich Schema?
Worauf ich hinaus will: Sind in diesem Fall Dezimalzahlen wirklich
schlau, oder würde sich bei einer Hex-Schreibweise nicht eine
generelles, leicht zu durchschauendes Schema ergeben?
Ich habe zwar keine Ahnung von C, aber Itoa macht ASCII aus Integer,
Integer TO Ascii... ;-)
Man gut, dass es Assembler gibt... :-P
...
Hannes Lux schrieb:
> Ich habe zwar keine Ahnung von C,
du lernst aber durch mitlesen.
Nicht mehr lange, und ich hab dich bekehrt :-)
Karl heinz Buchegger schrieb:
> Hannes Lux schrieb:
>> Ich habe zwar keine Ahnung von C,
>
> du lernst aber durch mitlesen.
Stimmt. Ich verfüge auch über den K&R und kann norfalls nachlesen.
> Nicht mehr lange, und ich hab dich bekehrt :-)
Das glaube ich nicht. Ich tu mich mein Leben lang mit Sprachen schwer.
Egal ob Russisch, Englisch, der Einstieg in Basic und 6502- bzw.
8085-ASM, es war alles sehr sehr mühsam.
Nun bin ich froh dass ich AVR-ASM einigermaßen verstehe und nur noch
selten ins Befehlsverzeichnis schauen muss und dass ich auch die
Architektur einigermaßen verstanden (und schätzen gelernt) habe. Da tu
ich mir mit sechzig Jahren C nicht mehr an, das ist mir einfach zu
kryptisch. Das bissel, was ich zu basteln habe, bekomme ich mit AVR-ASM
ganz gut in die Reihe. Da sehe ich was ich mache, die Befehle sind
absolut eindeutig und unmissverständlich, da habe ich keinen Bock drauf,
um eine weitere Ecke denken zu müssen, nur um meine Programme dem
Mainstream gemäß in C zu formulieren. Da ich meist sehr hardwarenah
programmiere, greift das Argument der angeblichen Portabilität nicht.
Karl-Heinz, Dein Vorhaben (mich zu bekehren) ehrt Dich, aber selbst
T.S., der ein verdammt guter C-Programmierer ist und gelegentlich auf 'n
Käffchen bei mir vorbei kommt, schafft es nicht, mich davon zu
überzeugen.
Beste Grüße,
Hannes
@ Karl heinz
Bestimmt wäre die Hexdarstellung oder gar die Binärdarstellung
aussagekräftiger. Es ist ja ein RC5 Telegramm
(http://www.sbprojects.com/knowledge/ir/rc5.htm) und da stecken die
Geräteadresse, das Bit für "Dauerfeuer" und das eigentliche Kommando
drin. Besonders bei der Binärdarstellung sollte man das eine klappernde
Bit3 schnell sehen.
@ jonanova
> Kann ich die Case-Fälle auch so verwenden mit den
> Zahlenwerten oder muss ich die umrechnen? Der wert "15376" ist ja nach
> der itoa funktion gekommen. Ich weiß leider nicht wie ich den RC5 code
> auslesen kann und dann direkt vegleichen.
Direkt so verwenden! Das Umwandeln der Zahl 12345 in den String "12345"
mit itoa() und der Vergleich von Strngs mit strcmp() fressen Zeit und
kostbare RAM-Bytes.
An die RC5-Codes kommst du, wenn du die Tasten der FB mal
durchprobierst. Da ist die Ausgabe im default-Fall ganz nützlich. Wenn
die Ausgaben stören, ändere das Programm und kommentiere die störenden
puts() Zeilen einfach aus - ist ja dein Programm, da kannst du machen
was du willst.
Beim Anschauen der Ausgaben wirst du merken, dass ein und dieselbe Taste
verschiedene Werte liefert, wenn die gedrückt und dann losgelassen und
dann wieder gedrückt wird. Wenn du diese FB-Taste im Programm auswerten
willst, musst du auf beide Werte reagieren können. Deshalb die doppelten
case-Fälle...
Aus http://www.sbprojects.com/knowledge/ir/rc5.htm
"The 3rd bit is a toggle bit. This bit is inverted every time a key is
released and pressed again. This way the receiver can distinguish
between a key that remains down, or is pressed repeatedly.
...
As long as a key remains down the message will be repeated every 114ms.
The toggle bit will retain the same logical level during all of these
repeated messages. It is up to the receiver software to interpret this
auto repeat feature."
Ich habe das also im vorherigen Beitrag falsch beschrieben. Das Bit
toggelt nur von Tastendruck zu Tastendruck bei der gleichen Taste.
Während die Taste gedrückt ist, bleibt es konstant. Man kann so
erkennen, ob die Taste zwischenzeitlich losgelassen wurde (Bit wechselt)
oder nicht (Bit wechselt nicht).
Man kann das auch mit je einem case-Fall lösen, wenn man das Toggle-Bit
ausmaskiert (immer 1 oder immer 0 setzt) und das Ergebnis im switch
testet. Wie das Ausmaskieren grundsätzlich geht, ist im Artikel
Bitmanipulation beschrieben.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|