Silicon Labs C8051F310 LED Blinken lassen

Gast #2871392
Lesenswert?

Hallo,

ich möchte mit dem oben genannten Controller eine LED unter Nutzung 
eines Timers im Sekundentakt blinken lassen:
1
//-----------------------------------------------------------------------------
2
// Includes
3
//-----------------------------------------------------------------------------
4
#include <c8051f310.h>                    // SFR declarations
5

6
unsigned long ms;
7

8
//-----------------------------------------------------------------------------
9
// 16-bit SFR Definitions for 'F33x
10
//-----------------------------------------------------------------------------
11

12
sfr16 TMR2RL   = 0xca;                    // Timer2 reload value
13
sfr16 TMR2     = 0xcc;                    // Timer2 counter
14
//-----------------------------------------------------------------------------
15
// Global CONSTANTS
16
//-----------------------------------------------------------------------------
17

18
#define SYSCLK       24500000         // SYSCLK frequency in Hz
19

20
sbit LED = P0^3;                          // LED='1' means ON
21

22
//-----------------------------------------------------------------------------
23
// Function PROTOTYPES
24
//-----------------------------------------------------------------------------
25
void SYSCLK_Init (void);
26
void PORT_Init (void);
27
void Timer2_Init (void);
28
void Timer2_ISR (void);
29
void ADC0_Init (void);
30
void ADC0_ISR (void);
31

32
//-----------------------------------------------------------------------------
33
// MAIN Routine
34
//-----------------------------------------------------------------------------
35
void main (void) {
36

37
  unsigned long wait;
38

39
   ms=0;
40
   wait=0;
41

42
   // disable watchdog timer
43
   PCA0MD &= ~0x40;                       // WDTE = 0 (clear watchdog timer
44
                                          // enable)
45

46
   SYSCLK_Init ();                        // Initialize system clock to
47
                                          // 24.5MHz
48
   PORT_Init ();                          // Initialize crossbar and GPIO
49
   Timer2_Init ();                 // Init Timer2 
50

51
   EA = 1;                                // enable global interrupts
52
   while (1) {                            // spin forever
53
    if( (ms-wait) < 1000 ) {
54
      LED=~LED;
55
      wait=ms;
56
    }
57
    
58
   }
59
}
60

61
//-----------------------------------------------------------------------------
62
// SYSCLK_Init
63
//-----------------------------------------------------------------------------
64
//
65
// This routine initializes the system clock to use the internal 24.5MHz
66
// oscillator as its clock source.  Also enables missing clock detector reset.
67
//
68
void SYSCLK_Init (void)
69
{
70

71
   OSCICN = 0xC3;                         // configure internal oscillator for
72
                                         
73
   RSTSRC = 0x04;                         // enable missing clock detector
74
}
75

76
//-----------------------------------------------------------------------------
77
// PORT_Init
78
//-----------------------------------------------------------------------------
79
//
80
// Configure the Crossbar and GPIO ports.
81
// P0.3 - LED (push-pull)
82
//
83
void PORT_Init (void)
84
{
85

86
   XBR0     = 0x00;                       // no digital peripherals selected
87
   XBR1     = 0x40;                       // Enable crossbar and weak pull-ups
88
   P0MDOUT |= 0x08;                       // enable LED as a push-pull output
89
}
90

91
//-----------------------------------------------------------------------------
92
// Timer2_Init
93
//-----------------------------------------------------------------------------
94
//
95
// Configure Timer2 to 16-bit auto-reload and generate an interrupt
96
//
97
void Timer2_Init (void)
98
{
99
   TMR2CN  = 0x00;                        // Stop Timer2; Clear TF2;
100
                                          // use SYSCLK/12 as timebase
101
   CKCON  &= ~0x20;                       // Timer2 clocked based on T2XCLK;
102

103
   TMR2RL  = -SYSCLK/12/1000;              // Init reload values
104
   TMR2    = 0xffff;                      // set to reload immediately
105
   ET2     = 1;                           // enable Timer2 interrupts
106
   TR2     = 1;                           // start Timer2
107
}
108

109

110
//-----------------------------------------------------------------------------
111
// Interrupt Service Routines
112
//-----------------------------------------------------------------------------
113

114
//-----------------------------------------------------------------------------
115
// Timer2_ISR
116
//-----------------------------------------------------------------------------
117
// This routine changes the state of the LED whenever Timer2 overflows.
118
//
119
void Timer2_ISR (void) interrupt 5
120
{
121
   TF2H = 0;                              // clear Timer2 interrupt flag
122
   ms++;
123
}

Die LED verhält sich ganz komisch. Sie ist die meiste Zeit an und geht 
ab und zu aus. Sieht jemand den Fehler?

Viele Grüße
Gast #2871401
Lesenswert?

Silicon schrieb:

> Sind die Datentypen ansonsten ausreichend groß?

Das sieht doch soweit gut aus.

Größere Datentypen auf einem 8-Bitter, wie z.B. ein 
32-bit-Millisekundenzähler im Timer-Interrupt, erzeugen natürlich auch 
viel mehr Code bzw. Laufzeit.

Schau dir das Assembler-Listing an, dann weißt du, was ich meine.
Gast #2871408
Lesenswert?

Jim Meba schrieb:

> Das kann dann gar lustige Effekte
> im Zusammenspiel mit dem Interrupt geben.

Na ja, wenn in verschiedenen Programmteilen die selbe Variable verwendet 
wird. Dann muß man gegebenenfalls über die Bearbeitungsdauer einen 
Interrupt sperren.

Sorry, ich sah das jetzt bei näherem Hinsehen auch erst.
Gast #2871413
Lesenswert?

Silicon schrieb:

> Was meinst du damit?

Deine Variable wird im Hauptprogramm verwendet. Jetzt stell dir vor, 
genau mitten drin schlägt der Timer-Interrupt zu, und verändert 
seinerseits die Variable. Meinst du, da käme noch was manierliches bei 
raus, wenn die Variablenbearbeitung im Hauptprogramm dann weiter geführt 
wird?
Gast #2871419
Lesenswert?

Ok und volatile sollte ich die Variable wahrscheinlich auch noch 
deklarieren ist mir gerade eingefallen.

Also wenn ich drei Dateien habe:

funcctions.h
functions.c
main.c


Dann habe ich in:

functions.h:
extern volatile unsigned long ms;

functions.c:
volatile unsigned long ms;

und in der main.c greife ich dann darauf zu.

Nur wie genau gestalte ich den atomaren Zugriff? Ich kann ja nicht für 
die komplette Verzweigung den Interrupt deaktivieren.
Gast #2871424
Lesenswert?

Silicon schrieb:

> Nur wie genau gestalte ich den atomaren Zugriff?

Im Hauptprogramm hast du eine Anweisung in C-Code stehen, die die 
Millisekunden-Variable auswertet.

Jetzt könnte mitten in der Auswertung der Timer-Interrupt dazwischen 
schlagen, und diese Variable ändern.

Denn im Assemblercode hast du für die Bearbeitung der 
Millisekunden-Variable eine ganze Menge Assemblercode stehen, anders als 
die eine Zeile in C.

Also muß man folgendes machen: Im Main-Programm unmittelbar vor 
Bearbeitung der Variablen den Timer-Interrupt sperren, und ihn 
unmittelbar nach dem Befehl wieder frei geben.
Gast #2871433
Lesenswert?

Silicon schrieb:
> Ich bearbeite ms doch aber nicht im Hauptprogramm, sondern werte sie nur
> aus.

Schau dir genau den Assemblercode an, den der Befehl

wait=ms;

macht.

Schlägt dort mitten in der Befehlsbearbeitung ein Timer-Interrupt 
herein, wird deine Variable ms geändert, und in wait steht nur Mist.
Gast #2871436
Lesenswert?

Ja aber beim Vergleichen wird ms ja auch ausgewertet. Muss ich das dann 
so schreiben:
1
  ET2 = 0;
2
  tmp=ms-wait;
3
  ET2 = 1;
4
    if( tmp >= 1000 ) {
5
      LED=~LED;
6
    //...
7
    // noch mehr code
8
    //...
9

10
    ET2 = 0;
11
      wait=ms;
12
    ET2 = 1;
13
    }
Gast #2871443
Lesenswert?

Silicon schrieb:
> Ja aber beim Vergleichen wird ms ja auch ausgewertet. Muss ich das dann
> so schreiben:
>
>
1
>   ET2 = 0;
2
>   tmp=ms-wait;
3
>   ET2 = 1;
4

5
>     ET2 = 0;
6
>       wait=ms;
7
>     ET2 = 1;
8
>     }

Das sieht schon besser aus. Interruptsperrung vor der 
Variablenauswertung, und Interruptfreigabe nach der Variablenauswertung.
Gast #2871451
Lesenswert?

Silicon schrieb:

> Ok danke! War das jetzt eigentlich auch richtig gedacht?

Also, du mußt grundsätzlich Variablen davor schützen, falls sie auch an 
einer anderen Stelle, Interrupts, noch mal geändert werden.

Du hast dir immer noch nicht den Assemblercode angeschaut, oder? An 
jeder Stelle des Assemblercodes kann ein Interrupt statt finden, und das 
ist oft mitten in einem einzigen C-Befehl, der aus mehreren 
Assemblerbefehlen besteht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren