Forum: Mikrocontroller und Digitale Elektronik Timer Interrupt


von Christian (Gast)


Lesenswert?

Hallo. Ich bin noch totaler Anfänger und habe noch nie wirklich was mit
Interrupts gemacht. Für mein nächstes Projekt brauche ich einen Timer
(für eine Uhr). Die Frequenz des Timers ist erstmal egal. Ich möchte
nur zB. das auf meiner Anzeige von 0 - 9 regelmäßig durchgezählt wird
(bestens natürlich in 1Hz).
Wie muss ich meinen Code ändern dass das irgendwie was werden kann? Ich
nutze den NC30 für den m16c von Renesas.


#include <stdio.h>
#include "sfr62.h"

// Taktfrequenz MainClock
#define CLOCK_FREQ   16000000L
#define OS_TICKS_PER_SEC        4000    /* Set the number of ticks in
one second                        */

// Timer B5 Interrupt Level: Timer Counter
#define TB5_INTLVL    1

void TimerInit(void);
void Delay(int k);
void Anzeige(int Ziffer, int Zahl);

// -------------------------------------------------------------------
void main()
{
TimerInit(); // Timer initialisieren.

// Hier möchte ich dann das zB. die Funktion Anzeige von 0 bis 9
hochzählt


}
// ------------------------------------------------------------------
void TimerInit(void)
{
  tb5ic = 0;    // IR reset und disable Ints

  tb5s = 0;     // Timer stop
  tb5mr = 0x80;   // f/32, Timer Mode
  tb5 = CLOCK_FREQ  32  OS_TICKS_PER_SEC - 1;

  tb5ic = TB5_INTLVL;    // Int freigeben

  tb5s = 1;     // Timer start
}

von Christian (Gast)


Lesenswert?

Hat niemand einen Tipp am frühen morgen? :-)

Also ich brauch eigentlich nur irgendeine Variable die dann hochzählt

Gruß Christian

von Andy (Gast)


Lesenswert?

Hallo Christian,

ich arbeite beruflich mit dem 16C80.
Also, du willst erstmal eine Variable hochzählen: es funktioniert
grundsätzlich mit einem Interrupt. Lade das tb5-Register mit einem
Wert, starte den Timer und er zählt dann runter, bei 0x00 wird ein
Interrupt ausgelöst, danach wird die Interrupt Service Routine
abgearbeitet (hier steht deine Variable).
Bei diesem Ausdruck tb5 = CLOCK_FREQ  32  OS_TICKS_PER_SEC - 1 würde
ich vorschlagen, dass du Klammern setzt(sehr unübersichtlich).

Also, ich würde folgendes Vorschlagen:

void TimerInit(void)
{

  tb5mr = 0x80;   // f/32, Timer Mode
  tb5 = CLOCK_FREQ  32  OS_TICKS_PER_SEC - 1;

  tb5ic = 0x09;    // Interrupt-Priorität festlegen, IR aktivieren

  tb5s = 1;     // Timer start
}


tb5_isr()  //Deine Interrupt Service Routine
{
  Deine_Variable++; // fürs Erste

}


void main ()
{

TimerInit()


}

P.S. Meiner Meinung nach ist dieser Controller eine schlechte Wahl für
einen Anfänger.

von Christian (Gast)


Lesenswert?

mh also ich habe das soweit geändert und getestet.

Folgender Code Funktioniert immer noch nicht, warum weiß ich auch
nicht.

double TimerVar = 0;

void TimerInit(void);
void Delay(int k);
void Anzeige(int Ziffer, int Zahl);

//
------------------------------------------------------------------------ 
------------
//
------------------------------------------------------------------------ 
------------
void main()
{
pd2 = 0xFF; // p2.0 - p2.7 output bzw. A0 - bis A7
pd9 = 0xff;

pd5 = 0x00;     /* p50 output    */

p2_0 = 0x00; // enable
p2_1 = 0x00;


p5_0=0xff;

TimerInit();
while(1)
  {
    if(TimerVar >= 1)Anzeige(2,8);
  }

}
//
------------------------------------------------------------------------ 
------------
//
------------------------------------------------------------------------ 
------------
void TimerInit(void)
{
  tb5mr = 0x80;   // f/32, Timer Mode
  tb5 = (CLOCK_FREQ  32  OS_TICKS_PER_SEC);

  tb5ic = 0x09;    // Interrupt-Priorität festlegen, IR aktivieren

  tb5s = 1;     // Timer start
}
//
------------------------------------------------------------------------ 
------------
tb5_isr()  //Deine Interrupt Service Routine
{
  TimerVar++; // fürs Erste
}
//
------------------------------------------------------------------------ 
------------

von Andy (Gast)


Lesenswert?

Hallo Christian,

es wäre hilfreich, wenn du den ganzen Quellcode eingefügt hättest und
schreib mal,
welche Werkzeuge(Compiler IDE, Emulator...) du einsetzt.
Ich arbeite mit: C-Compiler nc308 v.5.10 Release 1, Assembler as308 für
m16c80,
Linker ln308  für m16c80.

Also ich gehe von den selben Voraussetzungen aus:

Wichtig für die Interrupt Service Routine(ISR) ist, dass du sie dem
Compiler bekannt machst und zwar in der Datei sect308.inc.
Diese sieht dann folgendermaßen aus:

;---------------------------------------------------------------
; variable vector section
;---------------------------------------------------------------
.section  vector    ; variable vector table
.org  VECTOR_ADR
.lword  dummy_int    ;
.org  (VECTOR_ADR +32)
.lword  dummy_int    ; DMA0 (software int 8)
.lword  dummy_int     ; DMA1 (software int 9)
.lword  dummy_int     ; DMA2 (software int 10)
.lword  dummy_int     ; DMA3 (software int 11)
.lword  dummy_int    ; TIMER A0 (software int 12)
.lword  dummy_int    ; TIMER A1 (software int 13)
.lword  dummy_int    ; TIMER A2 (software int 14)
.lword  dummy_int    ; TIMER A3 (software int 15)
.lword  dummy_int    ; TIMER A4 (software int 16)
.lword  dummy_int    ; uart0 trance (software int 17)
.lword  dummy_int    ; uart0 receive (software int 18)
.lword  dummy_int    ; uart1 trance (software int 19)
.lword  dummy_int    ; uart1 receive (software int 20)
.lword  dummy_int    ; TIMER B0 (software int 21)
.lword  dummy_int    ; TIMER B1 (software int 22)
.lword  dummy_int    ; TIMER B2 (software int 23)
.lword  dummy_int    ; TIMER B3 (software int 24)
.lword  dummy_int    ; TIMER B4 (software int 25)
.lword  dummy_int    ; INT5 (software int 26)
.lword  dummy_int    ; INT4 (software int 27)
.lword  dummy_int    ; INT3 (software int 28)
.lword  dummy_int    ; INT2 (software int 29)
.lword  dummy_int    ; INT1 (software int 30)
.lword  dummy_int    ; INT0 (software int 31)
.lword  dummy_int    ; TIMER B5 (software int 32)
.glb _uart2_isr
.lword  _uart2_isr    ; uart2 trance/NACK (software int 33)
.lword  dummy_int    ; uart2 receive/ACK (software int 34)
.lword  dummy_int    ; uart3 trance/NACK (software int 35)
.lword  dummy_int    ; uart3 receive/ACK (software int 36)
.lword  dummy_int    ; uart4 trance/NACK (software int 37)
.lword  dummy_int    ; uart4 receive/ACK (software int 38)
.lword  dummy_int    ; uart2 bus collision (software int 9)
.lword  dummy_int    ; uart3 bus collision (software int 40)
.lword  dummy_int    ; uart4 bus collision (software int 41)
.lword  dummy_int    ; A-D Convert (software int 42)
.lword  dummy_int    ; input key (software int 43)
; from vector XX to vector YY is used MR30

hier habe ich eine ISR für den UART2.

In deine ISR kommt das, was du, nach dem dein Timer ein Interrupt
ausgelöst hat, ausgeführt haben willst.
Also, wenn ich das richtig verstanden habe, führt die Funktion
"Anzeige(2,8)" irgendetwas, was angezeigt wird.

------------------------------------------------------------------------ 
------------

unsigned long TimerVar = 0;

void TimerInit(void);
void Delay(int );
void Anzeige(int , int );

//
------------------------------------------------------------------------ 
------------
//
------------------------------------------------------------------------ 
------------
void main()
{
pd2 = 0xFF; // p2.0 - p2.7 output bzw. A0 - bis A7
pd9 = 0xff;
pd5 = 0x00;     /* p50 output    */

p2_0 = 0; // enable
p2_1 = 0;


p5_0=1;

TimerInit();

tb5s = 1;     // Timer start

while(1)
{
  }


}//main zu Ende


//
------------------------------------------------------------------------ 
------------
//
------------------------------------------------------------------------ 
------------
void TimerInit(void)
{
  tb5mr = 0x80;   // f/32, Timer Mode
  tb5 = (CLOCK_FREQ  32  OS_TICKS_PER_SEC);

  tb5ic = 0x09;    // Interrupt-Priorität festlegen, IR aktivieren


}

//---------------------------------------------------------------------- 
--------------

tb5_isr()  //Deine Interrupt Service Routine
{
    TimerVar++;
  if(TimerVar >= 1)
  {
    Anzeige(2,8);
  }
}

//---------------------------------------------------------------------- 
--------------

Zweitens sollte deine Zählvariable TimerVar je nach Größe der Schleife
"unsigned char, unsigned int oder unsigned long" sein und
nicht double.

Mit dem Klammern habe ich folgendes gemeint: ((CLOCK_FREQ / 32) /
OS_TICKS_PER_SEC) oder (CLOCK_FREQ / (32 / OS_TICKS_PER_SEC))
-> Es ist ein großer Unterschied.

Grüße

'Andy

von Christian (Gast)


Lesenswert?

Hallo Andy,

vielen Dank für deine Hilfe. Das mit der *.inc Datei wusste ich noch
gar nicht.
Ich nutze auch den NC30 5.x für den m16c62P.

Meine sect30.inc habe ich nun so editiert:

;---------------------------------------------------------------
; variable vector section
;---------------------------------------------------------------
  .section  vector    ; variable vector table
  .org  VECTOR_ADR

  .lword  dummy_int    ; vector 0 (BRK)
  .org  (VECTOR_ADR +44)
  .lword  dummy_int    ; DMA0 (for user)
  .lword  dummy_int     ; DMA1 2 (for user)
          ;.glb _keyint
  .lword  dummy_int    ; input key (for user)
  .lword  dummy_int    ; AD Convert (for user)
  .org  (VECTOR_ADR +68)
  .lword  dummy_int    ; uart0 trance (for user)
  .lword  dummy_int    ; uart0 receive (for user)
  .lword  0FF900h      ; uart1 trance (for user)
  .lword  0FF900h      ; uart1 receive (for use
  .lword  dummy_int    ; TIMER A0 (for user)
  .lword  dummy_int    ; TIMER A1 (for user)
  .lword  dummy_int    ; TIMER A2 (for user)
  .lword  dummy_int    ; TIMER A3 (for user)
  .lword  dummy_int    ; TIMER A4 (for user) (vector 25)
  .glb _tb0_isr
  .lword  _tb0_isr      ; TIMER B0 (for user) (vector 26)
  .lword  dummy_int    ; TIMER B1 (for user) (vector 27)
  .lword  dummy_int    ; TIMER B2 (for user) (vector 28)
  .lword  dummy_int    ; INT0 (for user) (vector 29)
  .lword  dummy_int    ; INT1 (for user) (vector 30)
  .lword  dummy_int    ; INT2 (for user) (vector 31)
  .lword  dummy_int    ; vector 32 (for user or MR30)
  .lword  dummy_int    ; vector 33 (for user or MR30)
  .lword  dummy_int    ; vector 34 (for user or MR30)
  .lword  dummy_int    ; vector 35 (for user or MR30)
  .lword  dummy_int    ; vector 36 (for user or MR30)
  .lword  dummy_int    ; vector 37 (for user or MR30)
  .lword  dummy_int    ; vector 38 (for user or MR30)
  .lword  dummy_int    ; vector 39 (for user or MR30)
  .lword  dummy_int    ; vector 40 (for user or MR30)
  .lword  dummy_int    ; vector 41 (for user or MR30)
  .lword  dummy_int    ; vector 42 (for user or MR30)
  .lword  dummy_int    ; vector 43 (for user or MR30)
  .lword  dummy_int    ; vector 44 (for user or MR30)
  .lword  dummy_int    ; vector 45 (for user or MR30)
  .lword  dummy_int    ; vector 46 (for user or MR30)
  .lword  dummy_int     ; vector 47 (for user or MR30)
  ; to vector 63 from vector 32 is used MR30

- Ist es egal ob ich _funktion_isr schreibe oder funktion_isr ?

Mein C Code sieht nun wie folgt aus. Ich habe quasi deinen Vorschlag
übernommen. Und ja Anzeige(int,int) ist eine Funktion die eine Zahl auf
einer von vier Ziffern auf einer Siebensegmentanzeige ausgibt
(funktioniert).

#include <stdio.h>
#include "sfr62.h"

// Taktfrequenz MainClock
#define CLOCK_FREQ   16000000L
#define OS_TICKS_PER_SEC        4000    /* Set the number of ticks in
one second                        */

unsigned long TimerVar = 0;

void TimerInit(void);
void Delay(int k);
void Anzeige(int Ziffer, int Zahl);

// -------------------------------------------------------------------
void main()
{
pd2 = 0xff; // p2.0 - p2.7 output bzw. A0 - bis A7
pd9 = 0xff;

pd5 = 0x00;     /* p50 output    */

p2_0 = 0x00; // enable
p2_1 = 0x00;


p5_0=0xff;

TimerInit();

tb0s = 1;     // Timer start

while(1)
  {
    //if(TimerVar >= 0)Anzeige(2,8);
  }

}
// -------------------------------------------------------------------
void TimerInit(void)
{
 tb0mr = 0x80;   // f/32, Timer Mode
  tb0 = (CLOCK_FREQ  32  OS_TICKS_PER_SEC);

  tb0ic = 0x09;    // Interrupt-Priorität festlegen, IR aktivieren

}
// -------------------------------------------------------------------
tb0_isr()  //Deine Interrupt Service Routine
{
   TimerVar++;
  if(TimerVar >= 1)
  {
    Anzeige(2,8);
  }


}
// -------------------------------------------------------------------


Ist es eigentlich egal ob ich für diesen Zweck Timer A0 nehme oder zB.
B0 ?
Leider ist auch dieser Code ohne jeglichen Effekt. Ich hoffe du/ihr
kannst/könnt mir noch weiter helfen :-)

    Gruß Christian

von Andy (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Christian,

also, erstmal zu deinen Fragen:

- Der Eintrag in der sect308.inc muß für jeden Interrupt drin sein und
muß mit einem "_"(Unterstrich) beginnen, weil die
ganze Datei in Assembler geschrieben wurde. In deiner C-Routine
schreibst du den Namen ohne "_". Alle Namen sind frei wählbar.
Z.B. in sect308.inc: _interrupt_dma0, in C-Routine: interrupt_dma0
usw.
Lies mal dazu das Manual für C-Language.

-In deiner C-Routine mußt du noch folgendes einfügen: " #pragma
INTERRUPT dein_interrupt_name ".

-Und was am aller wichtigsten ist, mußt du bevor du einen Interrupt
ausführen kannst, das Interrupt enable flag (I flag)
in deiner C-Routine setzen, mit:
  asm( "fset I");    // alle Interrupts aktivieren

sonst sind alle Interrupts deaktiviert.

-Ob du für diesen Zweck Timer A oder B nimmst, ist egal, Timer A hat
halt mehr Funktionen.


Ich habe in der Datei meinen Quellcode eingefügt, der hat bei mir
funktioniert.

Grüße

'Andy

von Danyo (Gast)


Lesenswert?

hallo christian!

ich habe schon ne weile auf einem m16c62 programmiert. hier ist mein
timer code für einen 16mhz-quarz:

//********************************************************************** 
*
// Routine für eine Pause von time µs m.H. des Hardware-Timers A0
//********************************************************************** 
*

void delay_us(int time)
{
  ta0ic = 0x00;        // Timer A0 erst einmal deaktivieren

  ta0mr = 0x40;           // Timer A0 in Timer-Mode und F_CPU wird durch 
8
geteilt -> 2Mhz

    ta0 = 2*time;          // Set up Timer A0 Reload Register for 1 usec
interval interrupt
                // -> zählt bis 2*time -> 2*time / 2Mhz = 1µs*time

  ta0s = 1;          // Timer A0 count start flag aktiviert

  while (ir_ta0ic != 1);    // wait for timer to run out -> solange noch
nicht fertig gezählt,
                // noch kein interrupt-request, also tu nichts
                // -> ir_ta0ic ist das interrupt request bit vom ta0ic 
s.o.

  ta0s = 0;          // Timer stoppen
}

gruß danyo

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.