Forum: Mikrocontroller und Digitale Elektronik Silicon Labs C8051F310 LED Blinken lassen


von Silicon (Gast)


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

von Silicon (Gast)


Lesenswert?

Ok ich habe es gerade selber gefunden:

if( (ms-wait) > 1000 ) {

muss es heißen. Sind die Datentypen ansonsten ausreichend groß?

von Jim M. (turboj)


Lesenswert?

1
if( (ms-wait) < 1000 ) {

Da würde ich ein
1
if( (ms-wait) >= 1000 ) {

draus machen.

von Wilhelm F. (Gast)


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.

von Jim M. (turboj)


Lesenswert?

Der Zugriff auf ms ist nicht atomar. Das kann dann gar lustige Effekte 
im Zusammenspiel mit dem Interrupt geben.

von Wilhelm F. (Gast)


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.

von Silicon (Gast)


Lesenswert?

Jim Meba schrieb:
> Der Zugriff auf ms ist nicht atomar. Das kann dann gar lustige Effekte
> im Zusammenspiel mit dem Interrupt geben.

Was meinst du damit?

von Wilhelm F. (Gast)


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?

von Silicon (Gast)


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.

von Wilhelm F. (Gast)


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.

von troll (Gast)


Lesenswert?

atomar <-- klick

von Silicon (Gast)


Lesenswert?

Ich bearbeite ms doch aber nicht im Hauptprogramm, sondern werte sie nur 
aus.

von Wilhelm F. (Gast)


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.

von Silicon (Gast)


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
    }

von Wilhelm F. (Gast)


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.

von Silicon (Gast)


Lesenswert?

Silicon schrieb:
> 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.

Ok danke! War das jetzt eigentlich auch richtig gedacht?

von Wilhelm F. (Gast)


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.

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.