Hallo Forum. Da mir hier so gut geholfen wurde habe ich ja inzwischen hinbekommen diverse Dimmer und Funksteckdosen mit dem RFM12 zu schalten. Nun wollte ich dafür eine anständige Schaltbox bauen. Dazu kommt ein Mega48 zum Einsatz. Das RFM12 hängt an PC0-PC3 und 8 Schalter hängen am PORTB. Mit dem Debounce Makro von Peter funktioniert die Sache auch einwandfrei. Ich kann alle 8 Taster stabil abfragen und entsprechende Werte senden. Nun würde ich gerne diese Komfortroutine von Peter Dannegger verwenden: http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29 Einfach weil die Option bei langem Tastendruck was anderes zu machen für sehr toll nutzbar wäre. Wenn ich den Code in mein Projekt integriere habe ich was merkwürdiges: Sobald ich die Funktion get_key_short oder get_key_long in der Mainloop abfrage schein es einen Reset zu geben. Hier mal mein Code der in der entwicklung ist und daher noch nicht ganz aufgeräumt. #include <avr/io.h> #include <avr/interrupt.h> #include <avr/pgmspace.h> #include <avr/eeprom.h> #include <stdlib.h> #include <stdio.h> #include "global.h" #include "rf12.h" #include "../EigeneHeader/funkcodes.h" //Da steht drin was das RFM12 senden soll #define F_CPU 8000000UL #include <util/delay.h> #define BAUD 9600UL // Baudrate #define UART_FIFO_LENGTH 9U // Berechnungen #define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1) // clever runden #define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) // Reale Baudrate #define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler. #if ((BAUD_ERROR<990) || (BAUD_ERROR>1010)) #error Systematischer Fehler der Baudrate grˆsser 1% und damit zu hoch! #endif // debounce macro das funktionier #define debounce( port, pin ) \ ({ \ static uint8_t flag = 0; /* new variable on every macro usage */ \ uint8_t i = 0; \ \ if( flag ){ /* check for key release: */ \ for(;;){ /* loop ... */ \ if( !(port & 1<<pin) ){ /* ... until key pressed or ... */ \ i = 0; /* 0 = bounce */ \ break; \ } \ _delay_us( 98 ); /* * 256 = 25ms war 98 */ \ if( --i == 0 ){ /* ... until key >25ms released */ \ flag = 0; /* clear press flag */ \ i = 0; /* 0 = key release debounced */ \ break; \ } \ } \ }else{ /* else check for key press: */ \ for(;;){ /* loop ... */ \ if( (port & 1<<pin) ){ /* ... until key released or ... */ \ i = 0; /* 0 = bounce */ \ break; \ } \ _delay_us( 98 ); /* * 256 = 25ms war 98*/ \ if( --i == 0 ){ /* ... until key >25ms pressed */ \ flag = 1; /* set press flag */ \ i = 1; /* 1 = key press debounced */ \ break; \ } \ } \ } \ i; /* return value of Macro */ \ }) //Komfortroutine #define KEY_DDR DDRB #define KEY_PORT PORTB #define KEY_PIN PINB #define KEY1 0 #define KEY2 1 #define KEY3 2 #define KEY4 3 #define KEY5 4 #define KEY6 5 #define KEY7 6 #define KEY8 7 #define ALL_KEYS (1<<KEY1 | 1<<KEY2| 1<<KEY3| 1<<KEY4| 1<<KEY5| 1<<KEY6| 1<<KEY7| 1<<KEY8) #define REPEAT_MASK (1<<KEY1) // repeat: key1, key2 #define REPEAT_START 50 // after 500ms #define REPEAT_NEXT 20 // every 200ms volatile uint8_t key_state; // debounced and inverted key state: // bit = 1: key pressed volatile uint8_t key_press; // key press detect volatile uint8_t key_rpt; // key long press and repeat void uart_init() { UCSR0B |= (1<<TXEN0); // UART TX einschalten UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00); UBRR0H = UBRR_VAL >> 8; UBRR0L = UBRR_VAL & 0xFF; UCSR0B |= (1<<RXEN0)|(1<<RXCIE0); } int uart_putc(unsigned char c) { while (!(UCSR0A & (1<<UDRE0))) // warten bis Senden moeglich { } UDR0 = c; // sende Zeichen return 0; } void uart_puts (char *s) { while (*s) { // so lange *s != '\0' also ungleich dem "String-Endezeichen" uart_putc(*s); s++; } } ISR( TIMER0_OVF_vect ) // every 10ms { static uint8_t ct0, ct1, rpt; uint8_t i; TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload for 10ms i = key_state ^ ~KEY_PIN; // key changed ? ct0 = ~( ct0 & i ); // reset or count ct0 ct1 = ct0 ^ (ct1 & i); // reset or count ct1 i &= ct0 & ct1; // count until roll over ? key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key press detect if( (key_state & REPEAT_MASK) == 0 ) // check repeat function rpt = REPEAT_START; // start delay if( --rpt == 0 ){ rpt = REPEAT_NEXT; // repeat delay key_rpt |= key_state & REPEAT_MASK; } } /////////////////////////////////////////////////////////////////// // // check if a key has been pressed. Each pressed key is reported // only once // uint8_t get_key_press( uint8_t key_mask ) { cli(); // read and clear atomic ! key_mask &= key_press; // read key(s) key_press ^= key_mask; // clear key(s) sei(); return key_mask; } /////////////////////////////////////////////////////////////////// // // check if a key has been pressed long enough such that the // key repeat functionality kicks in. After a small setup delay // the key is reported beeing pressed in subsequent calls // to this function. This simulates the user repeatedly // pressing and releasing the key. // uint8_t get_key_rpt( uint8_t key_mask ) { cli(); // read and clear atomic ! key_mask &= key_rpt; // read key(s) key_rpt ^= key_mask; // clear key(s) sei(); return key_mask; } /////////////////////////////////////////////////////////////////// // uint8_t get_key_short( uint8_t key_mask ) { cli(); // read key state and key press atomic ! return get_key_press( ~key_state & key_mask ); } /////////////////////////////////////////////////////////////////// // uint8_t get_key_long( uint8_t key_mask ) { return get_key_press( get_key_rpt( key_mask )); } int main(void) { char s[32]; uart_init(); uart_puts("Hallo. Ich teste das senden:\n\r"); DDRD |= (1<<PD7); PORTD |= (1<<PD7); for (unsigned char i=0; i<10; i++) _delay_ms(10); // wait until POR done rf12_ports(); sprintf( s, "Vor Init: 0x%04X\n\r",rf12_trans(0x0000)); uart_puts(s); rf12_init(); // ein paar Register setzen (z.B. CLK auf 10MHz) // sprintf( s, "Nach Init: 0x%04X\n\r",rf12_trans(0x0000)); // uart_puts(s); rf12_setfreq(RF12FREQ(433.92)); // Sende/Empfangsfrequenz auf 433,92MHz einstellen // sprintf( s, "Nach SetFreq: 0x%04X\n\r",rf12_trans(0x0000)); // uart_puts(s); rf12_setbandwidth(4, 1, 4); // 200kHz Bandbreite, -6dB Verst‰rkung, DRSSI threshold: -79dBm // sprintf( s, "Nach Bandwidth: 0x%04X\n\r",rf12_trans(0x0000)); // uart_puts(s); rf12_setbaud(19200); // 19200 baud // sprintf( s, "Nach Baud: 0x%04X\n\r",rf12_trans(0x0000)); // uart_puts(s); rf12_setpower(0, 6); // 1mW Ausgangangsleistung, 120kHz Frequenzshift // sprintf( s, "Nach Power: 0x%04X\n\r",rf12_trans(0x0000)); // uart_puts(s); rf12_txdata(S_ROT_H); sprintf( s, "Nach TX: 0x%04X\n\r",rf12_trans(0x0000)); uart_puts(s); KEY_DDR &= ~ALL_KEYS; // konfigure key port for input KEY_PORT |= ALL_KEYS; // and turn on pull up resistors TCCR0B = (1<<CS02)|(1<<CS00); // divide by 1024 TIMSK0 |= 1<<TOIE0; // enable timer interrupt while (1) { if ((debounce (KEY_PIN, KEY2))) { uart_puts("Key2"); rf12_txdata(S_TV_2); sprintf( s, "Nach Power: 0x%04X\n\r",rf12_trans(0x0000)); uart_puts(s); _delay_ms(100); } // dieses Statement macht Probleme if (get_key_long(1<<KEY1)) { uart_puts("Key1"); rf12_txdata(S_ROT_H); _delay_ms(100); } // Ende Problemstatement } } Wenn das Statement welches Probleme bereitet nicht auskommentiert ist, sehe ich auf dem Terminal (der Uart dient nur zum debuggen) den Text "Hallo. Ich teste das senden durchlaufen". Bei Druck von Taste 2 schreibt er "Key2Hallo. Ich teste das senden". Bei Druck von Taste 1 passiert gar nichts. Uch wenn ich weniger Tastern oder gar nur eine definiere, dieses Verhalten kommt, sobald ich get_key_long oder get_key_short aufrufe. Wo kann ich ansetzen?
Auch wenn es dir bei dem jetzigen Problem nicht hilft, hast du schonmal an eine einfache Hardwareentprellung gedacht? Ein RC Glied pro Taster ist doch schnell angelötet und kostet auch nicht die Welt. Der Platzbedarf ist zumindest bei SMD-Bauteilen vernachlässigbar.
Ja das hab ich mir überlegt, und könnte es auch unterbringen. Allerdings hätte ich dann ja immer noch keine Lösung für das auswerten eines langen Tastendrucks um da einen anderen Befehl auszuführen. Und dann kann ich ja auch genauso gut beim debounce Macro bleiben, da dies ja prima funktioniert. Und der Code wird ja von vielen eingesetzt daher muss es ja irgendwei auch bei mir gehen. Es ist halt für mich extrem schwer zu debuggen, da ich nicht weiss warum er wieder an den Anfang springt. Das hatte ich noch nie.
Hallo Christoph, kann es sein das noch irgendein anderer Interrupt auftritt, für den du keine ISR geschrieben hast? So ein Problem hatte ich mal..... Gruß Falko
Hmmm. Kann natürlich schon sein, aber wie könnte ich das herausfinden? Ausserdem dachte ich, ein C Compiler würde einem da die Arbeit abnehmen. Soll ich einfach mal in allen Interruptvektoren eine UART Ausgabe machen? Wäre auf jeden Fall ein Ansatz.
Noch was für die Profis: Da fällt mir ein, dass ich ja den Interrupt Sachen an den Mega48 anpassen musste. Könnte da jemand draufsehen ob ich es richtig gemacht habe? Könnte es an den 8Mhz liegen? Chris
Ähhh noch was: Als du mich da jetzt drauf hingewiesen hast habe ich meinen Code nochmal durchforstet. Mir viel auf, dass ich vor der Endlosschleife gar nicht mit sei() die Interrupts aktiviert habe. Das heisst beim aufruf einer der beiden Funktionen war noch nie ein Interrupt aktiv. Kann dies das verhalten auslösen? Oh mann, jetzt wär ich gern zuhause um das zu testen Chris
Christoph Hoell schrieb: > Hmmm. > Kann natürlich schon sein, aber wie könnte ich das herausfinden? In einem "Catch-all interrupt vector" ein freies Beinchen setzen bzw. eine LD leuchten lassen? http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
Danke. Das werde ich auf jeden Fall testen. Und das oben geschriebene...
Ich denke ich habe es gefunden ohne es jetzt hier auf der Arbeit testen zu können. Ausgehend davon, dass es ein nicht behandelter Interrupt sein könnte habe ich das Programm noch mal ausgiebig gelesen. Vor allem die Sache mit meinem vergessenen sei(). Eigentlich heisst dies ja nur, dass ich die Interrupts global erst aktiviere wenn ich eine der beiden genannten Funktionen aufrufe. Das wäre dann auch auch der Zusammenhang warum diese Funktionen Fehler machen. Ich bin mir fast sicher wenn ich sei() vor die Main Loop schreibe funktioniert es auch nicht wenn ich die Funktionen wieder kommentiere. Das wäre dann zumindest logisch. Also habe ich geschaut wo ich überhaupt noch einen weiteren Interrupt im Programm habe. Und da kam ich auf die UART Schnittstelle die ich ja nur zu debuggen verwende. Und da ich immer auf meine vorhanden Codezeilen zurückgreife habe natürlich in uart_init() den Empfang via Interruppt aktiviert. Und im ganzen Programm finde ich den UART Reveive Handler nicht. Ich bin mir ziemlich sicher, dass wenn ich UCSR0B |= (1<<RXEN0)|(1<<RXCIE0); herauskommentiere das Programm laufen wird. Freue mich auf den Test. Chris
Es gab in mir einfach keine Ruhe. Ich hab kurz zuhause den Rechner einschalten lassen und bin mit der Fernwartung drauf. Es war in der Tat der UART Receive Interrupt. Danke für die tolle Hilfe. Ich hab das mit dem Catch All Interrupt mal in die Checkliste gepostet: http://www.mikrocontroller.net/articles/AVR_Checkliste#Alle_Interruptvektoren_definiert.3F Danke Chris
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.