Nach etlichen Mühen sollte es laufen. Sollte. Tuts nicht. Was sollte laufen und was läuft nicht?
Am mega16 auf der RNControl soll ein CTC-Interrupt eine Variable im 10 kHz Takt hochzählen (bis 50 000, danach 0...). Diese Zeitmarke wird in zwei externen ISR für EXT0 + EXT1 (von asynchronen Taktgebern - Drehgeber) ausgewertet.
Das komplette Programm wurde für Testzwecke auf die Timerroutinen zurückgestutzt und getestet. Testziel: LED auf PC4 toggeln.
Es geschieht - nein, nicht nix
- noch schlimmer.
1) PC4 auf high (LED ist aus), auch PC2, 3 und 5.
2) PC0,1 und PC6,7 sind low (LED leuchtet) - das hatte ich nirgendwo gesagt.
3) Timer läuft wohl nicht - weil LED statisch aus ist.
4) andere PC-Ports sind auch statisch (sagt Oskar).
Will bitte jemand helfen?
A) Was habe ich bei der Timerinitialisierung falsch gemacht? bzw. besser
B) Wie mache ich die Timerinitialisierung richtig?
C) Ist die ISR ok?
D) na ja, wenn sonst was Übles auffällt 
Die vollständige Quelle der Testvariante (ohne "andere" Routinen) mit der die hier beschriebene Fehlfunktion entsteht.
Code:
/* >> Diese ersten 2 Zeilen können zum Compilieren entfernt werden (muss nicht)
Sicherung 17dez07 hhmm nach Datei ..C1..\2_drehzahlen\2_drehzahlen-xxx.c
===================================================================================
========== Beachte: printout aus AVRStudio geht (nur) bis col 85 ==================
Target MCU : ATmega16
Target Hardware : RNControl
Target cpu-frequ. : 16 MHz, externer Quarzoszillator
===================================================================================
Enthaltene Routinen:
static inline void setportdon/~off
void XTI_01_init( void ) // ISR ext0+1 initialisieren
void TMR_0_init( void ) // Timer initialisieren 10 ms
... (testweise gestrichen)
SIGNAL (SIG_INTERRUPT0) // ISR Motor/Speed1
SIGNAL (SIG_INTERRUPT1) // ISR Motor/Speed2
SIGNAL(SIG_OVERFLOW0)
int main(void)
===================================================================================
*** Versionsgeschichte:
====================
x11 17dez07 17:mmff Test - seltsame Ergebnisse - Timer timt nicht
x10 17dez07 15:43 Erster Codeausbau fertig zum Test (sichern und LED-Test mit
dem Timer0
x01 16dez07 23:42 Sichern des ersten Standes - ohne Timer-Interrupt-Routine
x00 16dez07 14:30ff erster Aufbau
===================================================================================
*** Aufgabenstellung : Messen von 2 Motordrehzahlen
Es werden Impulse von zwei Gabellichtschranken am µC erfasst. Daraus werden
zwei Drehzahlen errechnet.
Drehzahlbereich von 0 (Stillstand) bis 1kHz (60 000 Upm)
Grundlage wird ein Timerinterrupt mit ...
Original: ...C1..\C-motst_x10\C_motst_x21_OK.c
===================================================================================
*/
/* ============================================================================== */
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
/* Beachte C:\Programme\WinAVR-20070525\avr\include\avr\iom16.h */
#define MCU = ATMega16
// Mit Quarz 16 Mhz-CPU
#define F_CPU 16000000
/* ============================================================================== */
/* Interrupt-Handler und -Adressen
Address Labels Code Comments
siehe C:\Programme\WinAVR-20070525\avr\include\avr\iom16.h sowie
Interruptvektoren/-vektortabelle in *doc2466, S 45+46 von AVR */
/* beachte: volatile! und Vorzeichenlos reicht für alle */
volatile uint16_t Iencdr1, Iencdr2; /* Counter für Encoder-ticks
Werden in der ISR hochgezählt und im main bei Gelegenheit
(welcher? - nach 5 sec?) - spätestens aber beim Fahrtrichtungswechsel
auf Null gesetzt. DAnn aber die Werte musv+musi entsprechend anpassen */
volatile uint16_t Iz_ysecv1, Iz_ysecv2; /* Zeitmarke "µsec"
des vorletzten Interrupts in der Einheit 100 Mikrosekunden. Ist der Wert des
hochlaufenden Timers zum Interruptzeitpunkt i-1 */
volatile uint16_t Iz_yseci1, Iz_yseci2; /* Zeitmarke "µsec"
des letzten Interrupts in der Einheit 100 Mikrosekunden. Ist der Wert des
hochlaufenden Timers zum Interruptzeitpunkt i (letzter Interrupt) */
volatile uint16_t Iz_diff1, Iz_diff2; /* Zeitdifferenz
Beim Abarbeiten des Interrupts wird yseci mit time1 belegt und diff aus der
Differenz yseci-ysecv errechnet. Danach wird yseci nach ysecv kopiert.
Im main wird aus diff die Drehzahl berechnet. */
volatile uint16_t Izeit_1; /* Timer läuft hoch
Die Interruptroutine läuft mit 10 kHz (Möglicherweise sind 100 kHz besser)
Beim Start des Timers läuft der Zähler time1 hoch und wird nach 5 sec -
also beim Wert 50 000 - wieder auf Null gesetzt. Dabei werden die Werte
ysecv und yseci angepasst */
/* ============================================================================== */
/* ============================================================================== */
/* =================================================================================
##### Hier ISR und ISR - Initialisierung(en)
================================================================================= */
/* === Initialisierung fuer EXT_INT0/1 auf Pin 16+17/mega16(32) ==================
$002 jmp SIG_INTERRUPT0 ; IRQ0 Handler und
$004 jmp SIG_INTERRUPT1 ; IRQ1 Handler */
void XTI_01_init( void )
{ //Initialisiere beide Interrupts auf rising edge
// d.h. MCUCR ISC00,01,10+11 auf 1 (doc,S68)
MCUCR |= (1<<ISC11)|(1<<ISC10)|(1<<ISC01)|(1<<ISC00);
GICR |= (1<<INT1)|(1<<INT0); // und erlaube diese I´s in GICR
}
/* ============================================================================== */
/* === Initialisierung fuer Timer0 8-bit mega16(32) ==============================
Aufruf als SIGNAL (SIG_OVERFLOW0) */
void TMR_0_init( void )
{ //Initialisiere 8-Bit-Timer auf 10 kHz
TCCR0 |= (1<<CS02 | 0<<CS01 | 1<<CS00); // Prescaler 1024 / Clock <- CPU
TCCR0 |= (1<<WGM01 | 0<<WGM00); // Timer im CTC-Mode
TCNT0 = 0x64; // Timer0 Counter 0x64 für 10ms bei 16Mhz
TIMSK |= (1<<OCIE0); // Compare Match IRQ
}
/* ============================================================================== */
/* === Nicht unterbrechbare ISR für EXT_INT0 auf Pin 16/PD2/mega16(32) ======== */
/* Routine setzt einfach einen Zähler hoch. Der Zähler wird im main
ausgelesen ##>> cli/sei setzen <<## und nach 2 (od. 5) hunderstel Sekunden
auf den Speicher WEG_L/_R vorzeichenrichtig aufaddiert und dann zurückgesetzt.
##>> Beim Richtungswechsel (cli/sei) wird der Zähler ausgelesen und genullt,
damit ist eine saubere Wegmessung möglich.
Der zugehörige Motor auf RNControl auf PB0/PB1 = li,re und PD5 Geschwind.
Der alternat. Motor auf RNControl auf PC7/PC6 = li,re und PB4 PWM/Geschw.
$002 jmp EXT_INT0 ; IRQ0 Handler */
SIGNAL(SIG_INTERRUPT0)
{
Iencdr1 ++; //zähle Counter/encoder 1 hoch
Iz_yseci1 = Izeit_1; //Weise musi den akt. Timerwert zu
Iz_diff1 = Iz_yseci1-Iz_ysecv1; //Neue Zeit-Differenz1 ausrechnen
Iz_ysecv1 = Iz_yseci1; //der aktuelle Zeitwert wird "Alter"
}
/* ============================================================================== */
/* ============================================================================== */
/* === Nicht unterbrechbare ISR für EXT_INT1 auf Pin 17/PD3/mega16(32) ======== */
/* Routine setzt einfach einen Zähler hoch.
Sonst wie ISR für EXT_INT0 für Motor auf PC7/PC6 = li,re und PB4 PWM/Geschw.
$004 jmp EXT_INT1 ; IRQ1 Handler */
SIGNAL(SIG_INTERRUPT1)
{
Iencdr2 ++; //zähle Counter/encoder 2 hoch
Iz_yseci2 = Izeit_1; //Weise Iz_yseci den akt. Timerwert zu
Iz_diff2 = Iz_yseci2-Iz_ysecv2; //Neue Zeit-Differenz2 ausrechnen
Iz_ysecv2 = Iz_yseci2; //der aktuelle Zeitwert wird "Alter"
}
/* ============================================================================== */
/* ============================================================================== */
/* === Nicht unterbrechbare ISR für timer ====================================== */
/* Diese Routine zählt hoch im Takt 10 kHz.setzen.
Dieser Wert wird von den beiden anderen ISR ausgelesen und den Werten
*/
// SIGNAL(SIG_OVERFLOW0)
// #define SIG_OUTPUT_COMPARE0 //Interuptvektor, siehe Tabelle
SIGNAL(SIG_OUTPUT_COMPARE0)
{
Izeit_1 ++; //Zeitstand des Interupt-Timers
PORTC ^= (1 << PC4); //LED toggeln
}
/* ============================================================================== */
// ------------------------------------------------------------------
/*### Hauptschleife ###*/
int main(void)
{
DDRC |= 0xFF; //11111111 -> PC6 und PC7 linker Mot, Rest LEDs/Lauflicht
TMR_0_init();
sei();
for(;;) {
}
}
oder in einer gekürzten Fassung, mit den "kritischen" Routinen:
Code:
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
/* Beachte C:\Programme\WinAVR-20070525\avr\include\avr\iom16.h */
#define MCU = ATMega16
// Mit Quarz 16 Mhz-CPU
#define F_CPU 16000000
/* ============================================================================== */
/* Interrupt-Handler und -Adressen
Address Labels Code Comments
siehe C:\Programme\WinAVR-20070525\avr\include\avr\iom16.h sowie
Interruptvektoren/-vektortabelle in *doc2466, S 45+46 von AVR */
/* beachte: volatile! und Vorzeichenlos reicht für alle */
volatile uint16_t Izeit_1; /* Timer läuft hoch
Die Interruptroutine läuft mit 10 kHz (Möglicherweise sind 100 kHz besser)
Beim Start des Timers läuft der Zähler time1 hoch und wird nach 5 sec -
also beim Wert 50 000 - wieder auf Null gesetzt. Dabei werden die Werte
ysecv und yseci angepasst */
/* ============================================================================== */
/* =================================================================================
##### Hier ISR und ISR - Initialisierung(en)
================================================================================= */
/* === Initialisierung fuer Timer0 8-bit mega16(32) ==============================
Aufruf als SIGNAL (SIG_OVERFLOW0) */
void TMR_0_init( void )
{ //Initialisiere 8-Bit-Timer auf 10 kHz
TCCR0 |= (1<<CS02 | 0<<CS01 | 1<<CS00); // Prescaler 1024 / Clock <- CPU
TCCR0 |= (1<<WGM01 | 0<<WGM00); // Timer im CTC-Mode
TCNT0 = 0x64; // Timer0 Counter 0x64 für 10ms bei 16Mhz
TIMSK |= (1<<OCIE0); // Compare Match IRQ
}
/* ============================================================================== */
/* ============================================================================== */
/* === Nicht unterbrechbare ISR für timer ====================================== */
/* Diese Routine zählt hoch im Takt 10 kHz.setzen.
Dieser Wert wird von den beiden anderen ISR ausgelesen und den Werten
*/
// SIGNAL(SIG_OVERFLOW0)
// #define SIG_OUTPUT_COMPARE0 //Interuptvektor, siehe Tabelle
SIGNAL(SIG_OUTPUT_COMPARE0)
{
Izeit_1 ++; //Zeitstand des Interupt-Timers
PORTC ^= (1 << PC4); //LED toggeln
}
/* ============================================================================== */
Danke im Voraus
Lesezeichen