www.mikrocontroller.net

Forum: Compiler & IDEs Bitfelder in WINAVR


Autor: Thomas B. (detritus)
Datum:

Bewertung
0 lesenswert
nicht 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:
//***********************************************************************************
#include <avr/io.h> 
#include <avr/interrupt.h>     
//***********************************************************************************
#define SSR_PORT PORTB
#define SSR_DDR DDRB
#define SSR0 0

#define DG_MASKE ((1<<DG_A)|(1<<DG_B))
#define LED_PORT PORTD
#define LED_DDR DDRD
#define LED0 5
#define LED1 6
//***********************************************************************************
typedef unsigned char  u8;
typedef   signed char  s8;
typedef unsigned short u16;
typedef   signed short s16;
typedef unsigned long  u32;
typedef   signed long  s32;
//***********************************************************************************
volatile struct{            
  unsigned flag_1khz:1;        //Flag für HP->UP
}  bits;

volatile u16 zuendschwelle_zaehl=440;
volatile u16 zuendschwelle_nachlad=440;  
volatile u8 periodenanfang=0;      //Geht nicht als Teil des Bitstructes...
//***********************************************************************************
void config(void)
{
  
  TCCR0 |= (1<<WGM01)|(1<<CS00);    //CTC Modus an, Prescaler auf 1
  OCR0   =  160;            //Timer0Ints mit 100khz
  TIMSK |= (1<<OCIE0);        //Timer 0 CTC-int
  MCUCR |= (1<<ISC00);        //Externen Pin-Change-Int am PD2(ex.int0)
  GICR  |= (1<<INT0);          //Externen Int0 aktivieren
  DDRD  |= (1<<3);          //EXT-INT-Pin auf Eingang schalten 
  LED_DDR   =  (1<<LED0)|(1<<LED1);  //Ausgaenge definieren
  LED_PORT  =  0x00;          //alle LED aus
  SSR_DDR   =  (1<<SSR0);        //Ausgaenge definieren
  SSR_PORT  =  0x00;          //alle LED aus
  sei();                //globale Ints ein
}
//***********************************************************************************
ISR(TIMER0_COMP_vect)           //Timer0int mit 100khz
{  
  static u8 khz_50=2;
  static u8 khz_1=50;
  static u16 hz_1=1000;
  static u8 impuls=3;
    
  if((--khz_50)==0){
    khz_50=2;
    if(periodenanfang){
      if((--zuendschwelle_zaehl)==0){
        periodenanfang=0;  
        impuls=3;      
        SSR_PORT|=(1<<SSR0);
      }
    }
    else{
      if((--impuls)==255){ 
      impuls=0;
      SSR_PORT&=~(1<<SSR0);
      }
    }
    if((--khz_1)==0){
      bits.flag_1khz=1;
      khz_1=50;
      if((--hz_1)==0){
        hz_1=1000;
      }
    }
  }  
}
//*********************************************************************************** 
ISR(INT0_vect)               //Externer Int0
{
  zuendschwelle_zaehl=zuendschwelle_nachlad;
  periodenanfang=1;
}

//***********************************************************************************
//***********************************************************************************
int main (void)
{
  config();
  for(;;){
    }
  }
}
//***********************************************************************************

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

So wird das Flag dann überall im Programm angesprochen:
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?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.)

Autor: Thomas B. (detritus)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus W. (Firma: privat) (texmex)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Klaus W. (Firma: privat) (texmex)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.