Forum: Mikrocontroller und Digitale Elektronik TIMER für IRMP


von Heinz B. (Gast)


Lesenswert?

Nabend,

ich habe ein kleines Problem mit meinem IR-Empfänger.
Ich nutze die IRMP-Bibliothek aus dem Tutorial.

Als Empfänger nutze ich den TSOP31256.

Und zwar reagiert meine Software anscheinend nicht auf den Empfänger.
Dazu gesagt habe ich für die ISR-Routine den Timer2 meines Atmega32 
benutzt.

Diesen habe ich folgendermaßen initialisert:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
#include <stdlib.h>
5
#include "lcd.h"
6
#include "irmp.h"
7
8
9
10
ISR(TIMER2_COMP_vect)                        
11
{
12
  (void) irmp_ISR();                           
13
  
14
}
15
int main (void)
16
{
17
OCR2   =  (F_CPU / F_INTERRUPTS) - 1;   // Compare Register OCR2 eingestellt 
18
                                        //F_CPU ist im Makefile auf 8000000      
19
20
TCCR2  = (1 << WGM21) | (1 << CS20);   //CTC Mode an, prescaler = 1
21
TIMSK  = 1 << OCIE2;                    //Timer2 Interrupt eingeschaltet
22
23
  
24
  lcd_init();
25
  irmp_init();
26
  IRMP_DATA irmp_data;
27
  sei();
28
  while(1){
29
  if (irmp_get_data (&irmp_data)){
30
    lcd_string_xy(0,0,"gelesen");
31
    _delay_ms(500);
32
    _delay_ms(500);
33
    
34
  }else{
35
    lcd_clear();
36
  }
37
  
38
  
39
  }
40
  
41
return 0;
42
}

Wie zu erkennen ist soll der Atmega mir ein kleines Feedback auf mein 
LCD schreiben wenn es etwas erkannt hat.
Die ISR wird aufgerufen. Das habe ich mit einer Ausgabe auf dem LCD 
getestet.
Der IR-Empfänger erkennt ebenfalls die Signale wie ich per Oszi 
überprüft habe.
Nun vermute ich meinen Fehler in der Software. Habe ich einen Fehler bei 
der initialisierung des Timers oder des Interrupts gemacht oser was könt 
ihr erkennen.

Vielen Dank schonmal.

von roehrenvorheizer (Gast)


Lesenswert?

Hallo,

wichtig für das Funktionieren des IRMP ist es, genügend oft einen 
Interrupt auszulösen, so wie es das favorisierte Protokell erfordert, 
also im Bespiel 10000 ... 15000 IRQs pro Sekunde. Desweiteren müssen per 
defines diejenigen Protokolle eingeschaltet sein, die es empfangen 
können soll. Es müßte also zumindest das eine Protokoll aktiviert sein, 
das Deine Fernbendienung liefert. Falls nur die falschen Protokolle 
aktiviert sind, wird auch nichts empfangen.

ebenso muß der Richtige Eingangs-Pin eingestellt sein und in IRMP.c die 
passenden Protokolle aktiviert sein.

hier mal ein kleiner Auszug aus IRMP.h:



"/*--------------------------------------------------------------------- 
------------------------------------------------------------------------ 
------
 * Change F_INTERRUPTS if you change the number of interrupts per 
second, F_INTERRUPTS should be in the range from 10000 to 15000
 *----------------------------------------------------------------------- 
------------------------------------------------------------------------ 
----
 */
#define F_INTERRUPTS                             10000 
// interrupts per second

#define IRMP_SIRCS_PROTOCOL                          1 
// Sony
#define IRMP_NEC_PROTOCOL                            2 
// NEC, Pioneer, JVC, Toshiba, NoName etc.
#define IRMP_SAMSUNG_PROTOCOL                        3 
// Samsung
#define IRMP_MATSUSHITA_PROTOCOL                     4 
// Matsushita
#define IRMP_KASEIKYO_PROTOCOL                       5 
// Kaseikyo (Panasonic etc)
#define IRMP_RECS80_PROTOCOL                         6 
// Philips, Thomson, Nordmende, Telefunken, Saba
#define IRMP_RC5_PROTOCOL                            7 
// Philips etc
#define IRMP_DENON_PROTOCOL                          8 
// Denon
#define IRMP_RC6_PROTOCOL                            9 
// Philips etc
#define IRMP_SAMSUNG32_PROTOCOL                     10 
// Samsung32: no sync pulse at bit 16, length 32 instead of 37
#define IRMP_APPLE_PROTOCOL                         11 
// Apple, very similar to NEC
#define IRMP_RECS80EXT_PROTOCOL                     12 
// Philips, Technisat, Thomson, Nordmende, Telefunken, Saba
#define IRMP_NUBERT_PROTOCOL                        13 
// Nubert"

IRMP.c:

"#include "irmp.h"

/*---------------------------------------------------------------------- 
------------------------------------------------------------------------ 
-----
 * Change settings from 1 to 0 if you want to disable one or more 
decoders.
 * This saves program space.
 * 1 enable  decoder
 * 0 disable decoder
 *----------------------------------------------------------------------- 
------------------------------------------------------------------------ 
----
 */
#define IRMP_SUPPORT_SIRCS_PROTOCOL             1       // flag: support 
SIRCS                      uses ~100 bytes
#define IRMP_SUPPORT_NEC_PROTOCOL               1       // flag: support 
NEC + APPLE                uses ~250 bytes
#define IRMP_SUPPORT_SAMSUNG_PROTOCOL           1       // flag: support 
Samsung + Samsung32        uses ~250 bytes
#define IRMP_SUPPORT_MATSUSHITA_PROTOCOL        1       // flag: support 
Matsushita                 uses  ~50 bytes
#define IRMP_SUPPORT_KASEIKYO_PROTOCOL          1       // flag: support 
Kaseikyo                   uses  ~50 bytes
#define IRMP_SUPPORT_RECS80_PROTOCOL            1       // flag: support 
RECS80                     uses  ~50 bytes
#define IRMP_SUPPORT_RC5_PROTOCOL               1       // flag: support 
RC5                        uses ~250 bytes
#define IRMP_SUPPORT_DENON_PROTOCOL             1       // flag: support 
DENON                      uses ~250 bytes
#define IRMP_SUPPORT_RC6_PROTOCOL               1       // flag: support 
RC6                        uses ~200 bytes
#define IRMP_SUPPORT_RECS80EXT_PROTOCOL         1       // flag: support 
RECS80EXT                  uses  ~50 bytes
#define IRMP_SUPPORT_NUBERT_PROTOCOL            1       // flag: support 
NUBERT                     uses  ~50 bytes

/*---------------------------------------------------------------------- 
------------------------------------------------------------------------ 
-----
 * Change hardware pin here:
 *----------------------------------------------------------------------- 
------------------------------------------------------------------------ 
----
 */
#ifdef PIC_CCS_COMPILER                                 // PIC CCS 
Compiler:

#define IRMP_PIN                                PIN_D2  // use PD2 as IR 
input on PIC

#else                                                   // AVR:

#define IRMP_PORT                               PORTD
#define IRMP_DDR                                DDRD
#define IRMP_PIN                                PIND
#define IRMP_BIT                                2       // use PB6 as IR 
input on AVR

#define input(x)                                ((x) & (1 << IRMP_BIT))
#endif
"
______________________________________________________

Du scheinst nirgends die gelesenen Daten aufs Display zu schreiben.
funktionierende Lösung im mir vorliegenden Beispiel:
....main...

  for (;;)
  {
    if (irmp_get_data (&irmp_data))
    {
        // ir signal decoded, do something here...
        // irmp_data.protocol is the protocol, see irmp.h
        // irmp_data.address is the address/manufacturer code of ir 
sender
        // irmp_data.command is the command code

#if IRMP_LOGGING != 1

        lcd_clrscr();
        lcd_puts("R: Code: ");
    lcd_puts(Proto[irmp_data.protocol-1]);
    lcd_gotoxy(0,1);
        lcd_puts("A: ");
    lcd_puts(itoa(irmp_data.address,s,16));
        lcd_puts(" C: ");
    lcd_puts(itoa(irmp_data.command,s,16));

        uart_putc(0x0A);
        uart_puts("R: Code: ");
    uart_puts(Proto[irmp_data.protocol-1]);
        uart_puts(" A: ");
    uart_puts(itoa(irmp_data.address,s,16));
        uart_puts(" C: ");
    uart_puts(itoa(irmp_data.command,s,16));
    }"
____________________________
Die konfiguration ist gar nicht so einfach.

mit freundlichem Gruß

von Hans B. (Gast)


Lesenswert?

Vielen Dank für die ausführliche Antwort.
Habe vergessen zu sagen, dass die irmpconfig.h bereits eingestellt ist.
Ich habe zum testen erstmal nahezu alle protokolle aktiviert und der 
Eingangspin ist eingestellt.

Die Interrupts habe ich auf 15000 belassen.

Gehe ich recht in der Annahme, dass es daran liegen könnte dass der 
Timer2 des Atmegas ein 8Bit-Timer ist und die 15000 Interrupts mit 
meinen Einstellungen nicht liefern kann?

Wenn ja worauf muss ich achten? In welchem Modus bekomme ich den Timer2 
auf die nötige Gesvhwindigkeit?

von Hans B. (Gast)


Lesenswert?

Achso.. nein ich schreibe die gelesenen Daten momentan nicht aufs 
Display.
Vielmehr schreibe ich das Wort "gelesen" aufs LCD wenn überhaupt 
irgendein IR-Signal erkannt wurde. Halt zum Testen obs erstmal 
grundsätzlich läuft.

Gruß

von roehrenvorheizer (Gast)


Lesenswert?

Hqllo,

Auszug aus dem Beispiel von KLaus Leidinger (dort mit Timer1 gemacht):
"void
timer_init (void)
{
#ifdef CODEVISION
  OCR1AH  = ((F_CPU / F_INTERRUPTS) >> 8) & 0xFF; 
// compare value: 1/10000 of CPU frequency (upper byte)
  OCR1AL  = ((F_CPU / F_INTERRUPTS) - 1)  & 0xFF; 
// compare value: 1/10000 of CPU frequency (lower byte)
#else  // gcc
  OCR1A   =  (F_CPU / F_INTERRUPTS) - 1; 
// compare value: 1/10000 of CPU frequency
#endif  // CODEVISION
  TCCR1B  = (1 << WGM12) | (1 << CS10); 
// switch CTC Mode on, set prescaler to 1

#if defined (_AVR_ATmega8_) || defined (_AVR_ATmega16_) || defined 
(_AVR_ATmega32_) || defined (_AVR_ATmega64_) || defined 
(_AVR_ATmega162_)
  TIMSK  = 1 << OCIE1A; 
// OCIE1A: Interrupt by timer compare
#else
  TIMSK1  = 1 << OCIE1A; 
// OCIE1A: Interrupt by timer compare
#endif  // __AVR...
}"

Link zur Version von Klaus Leidinger:

http://www.klaus-leidinger.de/mp/Mikrocontroller/IR-LCD/IR-LCD.html

Lief bei mir schon mal.
Device: atmega8

Program:    4156 bytes (50.7% Full)
(.text + .data + .bootloader)

Data:        953 bytes (93.1% Full)
(.data + .bss + .noinit)

mit freundlichem Gruß

von roehrenvorheizer (Gast)


Lesenswert?


von Hans B. (Gast)


Lesenswert?

roehrenvorheizer schrieb:
> Lief bei mir schon mal.

Ja bei mir lief es auch schonmal. Aber halt mit Timer1(wie in deinem 
Beispiel) Timer1 ist im Gegensatz zu Timer2 allerdings ein 16bit Timer.

Ich möchte den Interrupt aber von dem 8Bit-Timer auslösen.

Ich werde nochmal schauen müssen wie ich den CompareWert für OCR2 für 
den 8Bit-Timer ausrechnen muss.

Kann mir da vielleicht wer einen Tipp geben?

von roehrenvorheizer (Gast)


Lesenswert?

Hallo,
Timer2 kann beliebig "schnell" eingestellt werden, siehe Mega48 88 168 
Datenblatt:

• Bit 2:0 – CS22:0: Clock select
The three Clock Select bits select the clock source to be used by the 
Timer/Counter, see Table
18-9.
If external pin modes are used for the Timer/Counter0, transitions on 
the T0 pin will clock the
counter even if the pin is configured as an output. This feature allows 
software control of the
counting.
Table 18-9. Clock select bit description.
CS22 CS21 CS20 Description
0 0 0 No clock source (timer/counter stopped)
001clkT2S/(no prescaling)
010clkT2S/8 (from prescaler)
011clkT2S/32 (from prescaler)
100clkT2S/64 (from prescaler)
101clkT2S/128 (from prescaler)
110clkT2S/256 (from prescaler)
111clkT2S/1024 (from prescaler)

Um auf den gewünschten Wert zu kommen, braucht es noch einen 
"reload"-Wert, mit dem man das Timer-Register bei jedem Interrupt 
beschreibt.

mfG

von Hans B. (Gast)


Lesenswert?

Sorry aber ich hab das Gefühl du kopierst nur Auszüge. Das Hilft mir 
nicht und war auch nicht meine Frage.

Nochmal die Frage:

Wir errechne ich den Wert für einen 8bitTimer(Prescaler1, 
Ctc-Modus,interner Takt 8Mhz) den ich in OCR2 schreiben muss um auf 
15000 Interrupts zu kommen?

Eine kleine Formel reicht mir ;-D ansonsten werde ich es heute 
Nachmittag mal damit versuchen den Wert vom timer1 über 
verhältnisrechnung an timer2 anzupassen. Oder begehe ich damit einen 
völlig falschen weg?

von c-hater (Gast)


Lesenswert?

Hans B. schrieb:

> Nochmal die Frage:
>
> Wir errechne ich den Wert für einen 8bitTimer(Prescaler1,
> Ctc-Modus,interner Takt 8Mhz) den ich in OCR2 schreiben muss um auf
> 15000 Interrupts zu kommen?
>
> Eine kleine Formel reicht mir

Warum entnimmst du sie dann nicht einfach dem Datenblatt, da steht die 
nämlich drinne?

Aber natürlich zeigt schon eine überschlägige Rechnung, daß es so nicht 
funktionieren kann. 8.000.000/256=31.250.
256 ist der maximale Zählumfang eines 8Bit-Timers, d.h.: die 
Interruptfrequenz kann bei 8MHz Timertakt niemals geringer als 31kHz 
werden, jedenfalls nicht im CTC-Modus.

Du wirst also entweder einen anderen Prescaler oder einen anderen 
Basistakt oder einen anderen Timermodus verwenden müssen, um deine 15kHz 
Interruptfolgefrequenz zu erreichen.

von Karl H. (kbuchegg)


Lesenswert?

Hans B. schrieb:

> Eine kleine Formel reicht mir ;-D ansonsten werde ich es heute
> Nachmittag mal damit versuchen den Wert vom timer1 über
> verhältnisrechnung an timer2 anzupassen. Oder begehe ich damit einen
> völlig falschen weg?

Besser wäre es zu verstehen, wie die einzelnen Werte zusammen hängen.
Das ist tatsächlich wirklich nicht schwer.

FAQ: Timer

: Bearbeitet durch User
von roehrenvorheizer (Gast)


Lesenswert?

Hallo,


und nun? Geht's jetzt?

"Ich möchte den Interrupt aber von dem 8Bit-Timer auslösen.

Ich werde nochmal schauen müssen wie ich den CompareWert für OCR2 für
den 8Bit-Timer ausrechnen muss.
"

Man könnte ja per Oszilloskop schauen, wieviele Interrupts pro Sekunde 
an einem extra dafür angesteuerten Pin ausgelöst werden und einfach 
mehrere Einstellungen ausprobieren. Interessant ist auch der entstehende 
Jitter zu beobachten.

mfG

von Hans B. (Gast)


Lesenswert?

Ja es funktioniert jetzt.. war dann doch sehr einfach.. ich werde heute 
Abend die Lösung posten wenn ich wieder vorm lappie sitze.

von Heinz B. (Gast)


Lesenswert?

Soo.. letzendlich wirklich simpel.
Ich stand nur extrem auf dem Schlauch scheinbar.

Also es lag an dieser Zeile:
1
OCR2   =  (F_CPU / F_INTERRUPTS) - 1;

Und zwar kommt bei der Rechnung mit den Werten 8Mhz und 15000 Interrupts 
ein Wert von 532 heraus.
Da es sich bei dem Timer2 um ein 8-bit-Timer handelt lässt sich dieser 
Wert nicht in OCR2 ablegen da man mit 8 bit nur bis 256 kommt.

Meine Lösung war folgende:
1
OCR2   =  ((F_CPU / F_INTERRUPTS)/8) - 1;         
2
TCCR2  = (1 << WGM21) | (1 << CS21);

Und zwar setzte ich en Prescaler des Timer2 auf 8 und dividierte dann 
den Wert für OCR2 ebenfalls durch acht. Damit kam ich auf einen Wert von 
65, welcher sich nun bequem in das 8Bit-Register OCR2 schreiben lässt.

Durch C-haters kleinen Tipp kam die erleuchtung und die Hand an die 
Stirn ;-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Heinz B. schrieb:
> Meine Lösung war folgende:
>
1
> OCR2   =  ((F_CPU / F_INTERRUPTS)/8) - 1;
2
> TCCR2  = (1 << WGM21) | (1 << CS21);
3
>

Mal nachrechnen:
1
8000000 / 15000 / 8 - 1 = 65,666  --> OCR2 = 65

Jetzt zurückrechnen:
1
F_INTERRUPTS = F_CPU / (8 * (OCR2 + 1)) = 15151,51

Der tatsächliche Wert für F_INTERRUPTS wäre dann also 15152.

Setze ihn also auf 15152, dann arbeitet IRMP genauer.

Dann musst Du aber noch folgende Zuweisung korrigieren, sonst koppelt 
die Korrektur von F_INTERRUPTS zurück in den Wert von OCR2 wegen 
(falscher) konsequenter Abrundung:
1
OCR2   =  (uint8_t) ((F_CPU / F_INTERRUPTS) / 8) - 1 + 0.5);

Diese Zuweisung wird dann noch auf den besten Integer gerundet - in 
diesem Fall von 64,99 auf 65 aufgerundet. Keine Angst, Du fängst Dir 
damit keine Floating-Point-Rechnung ein. Das macht der Preprocessor at 
Compile-Time.

> Und zwar setzte ich en Prescaler des Timer2 auf 8 und dividierte dann
> den Wert für OCR2 ebenfalls durch acht. Damit kam ich auf einen Wert von
> 65, welcher sich nun bequem in das 8Bit-Register OCR2 schreiben lässt.

Ich werde die obige Methode im IRMP-Artikel als Alternative zum 
Timer1 dokumentieren.

: Bearbeitet durch Moderator
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
Noch kein Account? Hier anmelden.