Forum: Compiler & IDEs Bitfelder in WINAVR


von Thomas B. (detritus)


Lesenswert?

Ich bin grad dabei, mich in die AVRs einzuarbeiten. Das Programm soll 
jetzt erstmal eine Phasenanschnittsteuerung sein.
Nach der AN von Atmel hab ich die Netzspannung über 100k an nem Portpin 
und die Masse über 100k an der Netzerde. Das liefert saubere Interrupts 
ohne Aussetzer(getestet).

Nach dem Pin-Change-Int warte ich eine bestimmte Zeit, schalte dann ein 
SSR ein und nach einer kleinen Wartezeit wieder aus.
Der Controller(Mega32) läuft mit 16Mhz.

Ich setze im Pin-Change-Int ein Flag(Periodenanfang). Dieses Flag wollte 
ich in ein Bitfeld ("bits") setzen, damit für 1/0 kein ganzes Byte 
verschwendet wird. Mache ich das, kommt es beim Phasenanschnitt 
reproduzierbar eine gewisse Zeit nach nem Reset zu kurzen Aussetzern. Es 
scheint ab und zu mindestens eine Halbwelle nicht durchgeschaltet zu 
werden. Die Zeitabstände der Aussetzer zueinander und zum Resetzeitpunkt 
bleibt nach jedem Reset gleich.

Nach langer Fehlersuche hab ich das Flag dann mal probeweise als ganzes 
Byte realisiert. Und dann geht es völlig problemlos.

Hier jetzt mal der funktionierende Code:
1
//***********************************************************************************
2
#include <avr/io.h> 
3
#include <avr/interrupt.h>     
4
//***********************************************************************************
5
#define SSR_PORT PORTB
6
#define SSR_DDR DDRB
7
#define SSR0 0
8
9
#define DG_MASKE ((1<<DG_A)|(1<<DG_B))
10
#define LED_PORT PORTD
11
#define LED_DDR DDRD
12
#define LED0 5
13
#define LED1 6
14
//***********************************************************************************
15
typedef unsigned char  u8;
16
typedef   signed char  s8;
17
typedef unsigned short u16;
18
typedef   signed short s16;
19
typedef unsigned long  u32;
20
typedef   signed long  s32;
21
//***********************************************************************************
22
volatile struct{            
23
  unsigned flag_1khz:1;        //Flag für HP->UP
24
}  bits;
25
26
volatile u16 zuendschwelle_zaehl=440;
27
volatile u16 zuendschwelle_nachlad=440;  
28
volatile u8 periodenanfang=0;      //Geht nicht als Teil des Bitstructes...
29
//***********************************************************************************
30
void config(void)
31
{
32
  
33
  TCCR0 |= (1<<WGM01)|(1<<CS00);    //CTC Modus an, Prescaler auf 1
34
  OCR0   =  160;            //Timer0Ints mit 100khz
35
  TIMSK |= (1<<OCIE0);        //Timer 0 CTC-int
36
  MCUCR |= (1<<ISC00);        //Externen Pin-Change-Int am PD2(ex.int0)
37
  GICR  |= (1<<INT0);          //Externen Int0 aktivieren
38
  DDRD  |= (1<<3);          //EXT-INT-Pin auf Eingang schalten 
39
  LED_DDR   =  (1<<LED0)|(1<<LED1);  //Ausgaenge definieren
40
  LED_PORT  =  0x00;          //alle LED aus
41
  SSR_DDR   =  (1<<SSR0);        //Ausgaenge definieren
42
  SSR_PORT  =  0x00;          //alle LED aus
43
  sei();                //globale Ints ein
44
}
45
//***********************************************************************************
46
ISR(TIMER0_COMP_vect)           //Timer0int mit 100khz
47
{  
48
  static u8 khz_50=2;
49
  static u8 khz_1=50;
50
  static u16 hz_1=1000;
51
  static u8 impuls=3;
52
    
53
  if((--khz_50)==0){
54
    khz_50=2;
55
    if(periodenanfang){
56
      if((--zuendschwelle_zaehl)==0){
57
        periodenanfang=0;  
58
        impuls=3;      
59
        SSR_PORT|=(1<<SSR0);
60
      }
61
    }
62
    else{
63
      if((--impuls)==255){ 
64
      impuls=0;
65
      SSR_PORT&=~(1<<SSR0);
66
      }
67
    }
68
    if((--khz_1)==0){
69
      bits.flag_1khz=1;
70
      khz_1=50;
71
      if((--hz_1)==0){
72
        hz_1=1000;
73
      }
74
    }
75
  }  
76
}
77
//*********************************************************************************** 
78
ISR(INT0_vect)               //Externer Int0
79
{
80
  zuendschwelle_zaehl=zuendschwelle_nachlad;
81
  periodenanfang=1;
82
}
83
84
//***********************************************************************************
85
//***********************************************************************************
86
int main (void)
87
{
88
  config();
89
  for(;;){
90
    }
91
  }
92
}
93
//***********************************************************************************

Der Struct mit den Aussetzern:
1
volatile struct{            
2
  unsigned flag_1khz:1;        //Flag für HP->UP
3
  unsigned periodenanfang:1;
4
}  bits;

So wird das Flag dann überall im Programm angesprochen:
1
bits.periodenanfang

Die Bitfelder hab ich aus dem Tutorium übernommen.

Und nun meine Frage: WARUM??

Braucht das Rotieren und Maskieren der Bits wirklich soo viel Zeit, dass 
der Timer-Interrupt manchmal nicht mehr nachkommt?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Thomas B. wrote:

> Ich setze im Pin-Change-Int ein Flag(Periodenanfang). Dieses Flag wollte
> ich in ein Bitfeld ("bits") setzen, damit für 1/0 kein ganzes Byte
> verschwendet wird.

Das lohnt sich eigentlich nur, wenn deine RAM-Auslastung schon knapp
an der Grenze ist.  Du erkaufst nämlich damit RAM-Platz durch ROM
und Laufzeit.

> Mache ich das, kommt es beim Phasenanschnitt
> reproduzierbar eine gewisse Zeit nach nem Reset zu kurzen Aussetzern.

Ohne bis zu Ende durch deinen Code durchsteigen zu wollen, würde ich
einfach mal vermuten, dass du eine race condition drin hast.  Das
Testen eines Bytes ist praktisch atomar, das Testen eines Bits (bis
auf Ausnahmen in niedrigen IO-Registern) ist es nicht.  Das Bit
könnte also mitten in deinem Test verändert werden.  (Ähnliches trifft
auf 16-bit-Variable zu.)

von Thomas B. (detritus)


Lesenswert?

Jörg Wunsch wrote:
> Thomas B. wrote:
>
>> Ich setze im Pin-Change-Int ein Flag(Periodenanfang). Dieses Flag wollte
>> ich in ein Bitfeld ("bits") setzen, damit für 1/0 kein ganzes Byte
>> verschwendet wird.
>
> Das lohnt sich eigentlich nur, wenn deine RAM-Auslastung schon knapp
> an der Grenze ist.  Du erkaufst nämlich damit RAM-Platz durch ROM
> und Laufzeit.

RAM hab ich eigentlich schon noch genug, ist wohl eher die schwäbische 
Sparsamkeit ;)
Hab mittlerweile auch eingesehen, dass es an dieser Stelle keinen Sinn 
macht.

>
>> Mache ich das, kommt es beim Phasenanschnitt
>> reproduzierbar eine gewisse Zeit nach nem Reset zu kurzen Aussetzern.
>
> Ohne bis zu Ende durch deinen Code durchsteigen zu wollen, würde ich
> einfach mal vermuten, dass du eine race condition drin hast.  Das
> Testen eines Bytes ist praktisch atomar, das Testen eines Bits (bis
> auf Ausnahmen in niedrigen IO-Registern) ist es nicht.  Das Bit
> könnte also mitten in deinem Test verändert werden.  (Ähnliches trifft
> auf 16-bit-Variable zu.)

Klingt erstmal logisch, aber gelesen und beschrieben wird ja 
ausschliesslich in den beiden Interrupts. Die können sich gegenseitig 
nicht unterbrechen, da keine Prioritäten beim AVR, das Lesen bzw. 
Schreiben sollte also gekapselt sein...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Thomas B. wrote:

> RAM hab ich eigentlich schon noch genug, ist wohl eher die schwäbische
> Sparsamkeit ;)

Atmel gibt dir kein Geld zurück, wenn du den RAM nicht komplett
benutzt. ;-)

> Klingt erstmal logisch, aber gelesen und beschrieben wird ja
> ausschliesslich in den beiden Interrupts. Die können sich gegenseitig
> nicht unterbrechen, da keine Prioritäten beim AVR, das Lesen bzw.
> Schreiben sollte also gekapselt sein...

Dann wundert es mich auch, stimmt.

von Klaus W. (Firma: privat) (texmex)


Lesenswert?

> Ohne bis zu Ende durch deinen Code durchsteigen zu wollen, würde ich
> einfach mal vermuten, dass du eine race condition drin hast.  Das
> Testen eines Bytes ist praktisch atomar, das Testen eines Bits (bis
> auf Ausnahmen in niedrigen IO-Registern) ist es nicht.  Das Bit
> könnte also mitten in deinem Test verändert werden.  (Ähnliches trifft
> auf 16-bit-Variable zu.)

Könnte man daraus folgern, dass zur Kommunikation zwischen ISR und 
Hauptprogramm eigentlich nur uint8_t oder int8_t geeignet ist?

(vorausgesetzt man ergreift nicht andere Maßnahmen wie Sperren des 
Interrupts während der Abfrage im Hauptprogramm oder sowas)

von Klaus W. (Firma: privat) (texmex)


Lesenswert?

Klaus W. wrote:
>> Ohne bis zu Ende durch deinen Code durchsteigen zu wollen, würde ich
>> einfach mal vermuten, dass du eine race condition drin hast.  Das
>> Testen eines Bytes ist praktisch atomar, das Testen eines Bits (bis
>> auf Ausnahmen in niedrigen IO-Registern) ist es nicht.  Das Bit
>> könnte also mitten in deinem Test verändert werden.  (Ähnliches trifft
>> auf 16-bit-Variable zu.)
>
> Könnte man daraus folgern, dass zur Kommunikation zwischen ISR und
> Hauptprogramm eigentlich nur uint8_t oder int8_t geeignet ist?
>
> (vorausgesetzt man ergreift nicht andere Maßnahmen wie Sperren des
> Interrupts während der Abfrage im Hauptprogramm oder sowas)

Hm, mal ausprobiert:

Aus dem C-Code macht der Compiler folgende Assembler Befehle:

Hier in der ISR:
   pending_msg.condition=1;
     130:       80 91 a1 00     lds     r24, 0x00A1
     134:       82 60           ori     r24, 0x02       ; 2
     136:       80 93 a1 00     sts     0x00A1, r24


Hier im Hauptprogramm:
   if (pending_msg.condition == 1)
     470:       80 91 a1 00     lds     r24, 0x00A1
     474:       81 ff           sbrs    r24, 1
     476:       05 c0           rjmp    .+10            ; 0x482 
<main+0x46>

Selbst wenn ich im Hauptprogramm schreibend zugreifen würde und die ISR 
unterbricht, wären doch die lds/sts Befehle als solche atomar. Es kann 
natürlich sein, dass das Hauptprogramm noch den alten Zustand des 
Bitfelds liest und die ISR vor dem Vergleich einen neuen schreibt, aber 
das merke ich ja dann bei der nächsten Abfrage.

Allerdings... grübel
Ich muss das Flag im Hauptprogramm irgendwann wieder zurücksetzen. Dann 
könnte es natürlich passieren, dass ich ein anderes Flag mit 
zurücksetze, welches die ISR zwischen lds und sts des Hauptprogramms 
gesetzt hat.

Ich sehe schon, man muss sich wohl recht genau überlegen was man macht 
:-(.

viele Grüße,
Klaus

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.