Forum: Compiler & IDEs RC-Switch in GCC - Problem


von Wolly2.0 (Gast)


Lesenswert?

Hi,
ich wollte mir einen RC-Switch bauen.
ganz simpel - dacht ich mir

TINY13:
Int0 an Impulsleitung
PB4 an Schalt-Mosfet

Fuses sind gesetzt auf 4,8Mhz ohne Prescaler

Mein Problem ist jetzt das der Mosfet (bzw die LED) ständig an ist, bzw. 
bei einer Sendeweg von ca 30% flackert.

Verstehen kann ich das nicht.
Bei nem Tackt von 4,8MHz und nem 64er Timer-Teiler sollte ca alle 
0,013ms der Timer inkremmentiert werden -> bei 1,5ms (mittelstellung) 
wären wir bei TCNT0 = 112,5

Und das liegt im bereich von 111 bis 114 wo er eigentlich dicht machen 
sollte (PIN 4).

Code:
1
#include <avr/io.h>
2
#include <stdint.h>
3
#include <avr/interrupt.h> 
4
5
#ifndef cbi
6
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
7
#endif
8
#ifndef sbi
9
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
10
#endif                
11
12
13
volatile int valu;
14
15
int main (void) {
16
17
  DDRB = 0b011000;  //PIN 3 u. 4 als Ausgang
18
19
  cbi(PORTB,PB3);    //PIN 3 auf low  
20
  cbi(PORTB,PB4);    //Pin 4 auf low
21
22
  sbi(GIMSK,INT0);  //Interrupt auf INT0 aktiv
23
  
24
  sbi(MCUCR,ISC01);  sbi(MCUCR,ISC00);  //Interrupt an INT0 bei steigender Flanke
25
26
  sei();      //Interrupts an
27
28
  while (1){
29
    if ((valu > 111) && (valu < 114)) {    //grober Bereich Mittelstellung
30
      cbi(PORTB,PB4);            //PIN 4 aus
31
      sbi(PORTB,PB3);            //PIN 3 an
32
    } else {
33
      sbi(PORTB,PB4);            //PIN 4 an
34
      cbi(PORTB,PB3);            //PIN 3 aus
35
    }
36
  }
37
38
  
39
   return 0;
40
}
41
42
43
44
ISR(_VECTOR(1)) {              //ISR Routine für INT0
45
  if (MCUCR & (1 << ISC00)) {            //Wenn auf steigende Flanke gesetzt
46
    sbi(TCCR0B,CS01);  sbi(TCCR0B,CS00);      //Timer starten mit 64er Teiler
47
    cbi(MCUCR,ISC00);                //Interrupt auf fallende Flanke setzen
48
  } else {                    //Wenn auf fallende Flanke gesetzt
49
    cbi(TCCR0B,CS00);  cbi(TCCR0B,CS01);      //Timer anhalten
50
    valu = TCNT0;                  //Timerwert übergeben
51
    TCNT0 = 0;                    //Timer reseten
52
    sbi(MCUCR,ISC00);                //auf steigende Flanke setzen
53
  }
54
}

Seht ihr was, was ich nicht seh?

MFG Wolly2.0

von Karl H. (kbuchegg)


Lesenswert?

Wolly2.0 schrieb:

> Fuses sind gesetzt auf 4,8Mhz ohne Prescaler

Wo kommen die 4.8Mhz her?
interner RC-Generator?

Dann würde ich nicht davon ausgehen, dass das exakt 4800000 Hz sind.
Ich kenn jetzt den Tiny 13 nicht konkret, aber andere Tinys haben ein 
Register OSCAL (Oszillator Calibration), mit dem man den Oszillator 
kallibrieren kann.

> bei einer Sendeweg von ca 30% flackert.

Wenn man die Flankenrichtung eines Eingangs umstellt, ist es meistens 
eine gute Idee, wenn man gleich danach sicherheitshalber das Interrupt 
Flag noch zusätzlich löscht. Nicht dass sich durch das Umstellen selbst 
wieder ein Interrupt ergibt.

Aber an deiner Stelle würde ich ehrlich gesagt, die Flankenrichtung gar 
nicht umstellen, wenn es sich vermeiden lässt. Wenn dein Tiny das kann, 
dann lass ihn bei jeder Flanke (egal ob steigend oder fallend) einen 
Interrupt auslösen. Welche Flanke es war kannst du ganz leicht 
feststellen, wenn du den Pin einliest und nachsiehst ob er 1 war (dann 
muss es ein Übergang 0->1 gewesen sein).

Da valu eine int Variable ist, müsstest du ausserdem atomaren Zugriff 
sicherstellen. Es erhebt sich allerdings die Frage, warum value 
überhaupt ein int sein muss. Bei einem Wertebereich bis maximal 256 
reicht ein uint8_t dicke aus.

Weiters solltest du dir das AVR-GCC-Tutorial reinziehen. In deinem Code 
ist so ziemlich alles enthalten, was man heutzutage nicht mehr macht, 
wenn man Programme für den WinAVR schreibt. Hol dir im Tutorial 
Anregungen, wie du die Dinge besser schreiben kannst.

von Rolf Magnus (Gast)


Lesenswert?

Wolly2.0 schrieb:
> if ((valu > 111) && (valu < 114)) {    //grober Bereich Mittelstellung

So grob finde ich das eigentlich nicht. Deine LED geht nur bei den 
Werten 112 und 113 aus. Das sind ca. 2,7% vom gesamten Weg.

Karl Heinz Buchegger schrieb:
> Dann würde ich nicht davon ausgehen, dass das exakt 4800000 Hz sind.
> Ich kenn jetzt den Tiny 13 nicht konkret, aber andere Tinys haben ein
> Register OSCAL (Oszillator Calibration), mit dem man den Oszillator
> kallibrieren kann.

Und bei 4,8 MHz auch muß:

(aus dem Datenblatt des Tiny13)
There is a separate calibration byte for the internal oscillator in 4.8 
MHz
mode of operation but this data is not loaded automatically. The 
hardware
always loads the 9.6 MHz calibraiton data during reset. To use separate
calibration data for the oscillator in 4.8 MHz mode the OSCCAL register
must be updated by firmware. The calibration data for 4.8 MHz operation 
is
located in the high byte at address 0x01 of the signature area.

> Aber an deiner Stelle würde ich ehrlich gesagt, die Flankenrichtung gar
> nicht umstellen, wenn es sich vermeiden lässt.

Ich würde auch den Timer nicht jedesmal stoppen und starten, sondern 
einfach bei steigender Flanke auf 0 setzen, oder besser noch sich vorher 
und nachher den Wert merken und die Differenz bilden. So läßt sich der 
Timer auch für andere Sachen noch nutzen.

von Wolly2.0 (Gast)


Lesenswert?

Also das mit dem OSCCAL Register wusste ich nicht, dafür schonmal ein 
dickes THX.

Und das mit dem Timer nicht mehr starten und stoppen hab ich auch mal 
übernommen.

Ändert aber leider nichts an dem Punkt das es nicht geht.

Interessant ist das das Flackern bei 30% jetzt weg ist, jetzt passiert 
einfach garnichts mehr.

An der Hardware liegts aber nicht, ich hab hier ein Bascom Programm das 
eine Ähnlich aufgabe erledigt und das geht.

Verbesserter Code
1
#include <avr/io.h>
2
#include <stdint.h>
3
#include <avr/interrupt.h> 
4
5
#ifndef cbi
6
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
7
#endif
8
#ifndef sbi
9
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
10
#endif                
11
12
uint8_t valu;
13
14
int main (void) {
15
16
  OSCCAL = 0x01;    //4,8MHz Einstellung
17
18
  DDRB = 0b011000;  //PIN 3 u. 4 als Ausgang
19
20
  cbi(PORTB,PB3);    //PIN 3 auf low  
21
  cbi(PORTB,PB4);    //Pin 4 auf low
22
23
  sbi(GIMSK,INT0);  //Interrupt auf INT0 aktiv
24
  
25
  sbi(MCUCR,ISC01);  sbi(MCUCR,ISC00);  //Interrupt an INT0 bei steigender Flanke
26
27
  sbi(TCCR0B,CS01);  sbi(TCCR0B,CS00);  //Timer mit 64er Teiler starten
28
29
  sei();      //Interrupts an
30
31
  while (1){
32
    if ((valu > 110) && (valu < 115)) {    //grober Bereich Mittelstellung
33
      cbi(PORTB,PB4);            //PIN 4 aus
34
      sbi(PORTB,PB3);            //PIN 3 an
35
    } else {
36
      sbi(PORTB,PB4);            //PIN 4 an
37
      cbi(PORTB,PB3);            //PIN 3 aus
38
    }
39
  }
40
41
  
42
   return 0;
43
}
44
45
46
47
ISR(_VECTOR(1)) {              //ISR Routine für INT0
48
  if (MCUCR & (1 << ISC00)) {            //Wenn auf steigende Flanke gesetz
49
    TCNT0 = 0;                    //Timer reseten
50
    cbi(MCUCR,ISC00);                //Interrupt auf fallende Flanke setzen
51
  } else {                    //Wenn auf fallende Flanke gesetzt
52
    valu = TCNT0;                  //Timerwert übergeben
53
    sbi(MCUCR,ISC00);                //auf steigende Flanke setzen
54
  }
55
}

Und bitte nicht böse sein wegen dem sbi und cbi, ich kann damit einfach 
besser als mit den ganzen &= ~ / |= usw. - mach ich warsch dann mehr 
Fehler als so, und das sollte ja das Prog auch nich ausbremsen.

von Stefan E. (sternst)


Lesenswert?

1
uint8_t valu;
Wo ist das volatile geblieben?

1
OSCCAL = 0x01;    //4,8MHz Einstellung
Ich kann nicht so recht glauben, dass das Calibration-Byte in der 
Signatur tatsächlich 1 ist (wäre ziemlich extrem). Ich denke, du hast 
wohl das Datenblatt-Zitat falsch verstanden.

von Wolly2.0 (Gast)


Lesenswert?

Oja stimmt das volatile hab ich da glatt unterschlagen.

"The calibration data for 4.8 MHz operation
is located in the high byte at address 0x01 of the signature area."

So ganz freie Übersetzung:
Die Kalibrierungseinstellung für 4,8MHz liegt im hohen Byte in mit der 
Adresse 0x01
(ich weiß mein Englisch ist Mist)

Ich dachte das das mit
1
OSCCAL = 0x01;
zu verstehen ist

von Wolly2.0 (Gast)


Lesenswert?

Ok ich hab grad gesehen bei PonyProg gibts ja die Option 'Osc. 
Calibration Options'

Da kann man das warsch. setzen
wenn ich dort den wert auslesen lasse kommt da 0x6B (107) raus
Sollte ich da jetzt dann 01 setzen?
bzw im setzen Feld sind sogar 4Bits verfügbar (Maske 0x0000)
Soll ich da jetzt  einfach mal 0x0010 reintexten oder mach ich da dann 
iwas falsch/kapput?

von Karl H. (kbuchegg)


Lesenswert?

Wolly2.0 schrieb:

> "The calibration data for 4.8 MHz operation
> is located in the high byte at address 0x01 of the signature area."
>
> So ganz freie Übersetzung:
> Die Kalibrierungseinstellung für 4,8MHz liegt im hohen Byte in mit der
> Adresse 0x01
> (ich weiß mein Englisch ist Mist)
>
> Ich dachte das das mit
>
1
> OSCCAL = 0x01;
2
>
> zu verstehen ist

Nö.
In der Beschreibung steht "Address" also Adresse. Unter dieser Adresse 
(die daher das Byte 0x01 im Bereich der Signatur-Daten kennzeichnet), da 
ist der Wert zu finden, der ins OSCCAL Register muss.

Address ist immer die Angabe, wo man einen Wert findet, aber sie ist 
nicht selber dieser Wert.

von Karl H. (kbuchegg)


Lesenswert?

Wolly2.0 schrieb:
> Ok ich hab grad gesehen bei PonyProg gibts ja die Option 'Osc.
> Calibration Options'
>
> Da kann man das warsch. setzen
> wenn ich dort den wert auslesen lasse kommt da 0x6B (107) raus

107 schaut vernünftig aus. Könnte stimmen. Zumindest ist die Zahl im 
erwarteten Wertebereich.
Kann man aber auch einfach ausprobieren.

> Sollte ich da jetzt dann 01 setzen?

No.
Der kommt ins OSCCAL Register.

> iwas falsch/kapput?

kaputt machst du erst mal gar nichts. Das ist nur eine Kalibrierung, mit 
der der RC-Schwingkreis "gestimmt" wird.


Schreib dir doch ein Testprogramm:
1
#define F_CPU 4800000UL
2
3
#include "avr/io.h"
4
#include "utils/delay.h"
5
6
int main()
7
{
8
  OSCCAL = 107;
9
10
  DDRB = ( 1 << PB4 );
11
12
  while( 1 ) {
13
    PORTB |= ( 1 << PB4 );
14
    _delay_ms( 1000 );
15
    PORTB &= ~( 1 << PB4 );
16
    _delay_ms( 1000 );
17
  }
18
}

an PB4 hängst du eine LED
(Optimizer auf -Os stellen nicht vergessen)

Wenn der µC tatsächlich mit 4.8Mhz läuft, dann stimmen die Wartezeiten, 
die der Compiler anhand der F_CPU Vorgabe errechnet hat. Die LED wird 
ruhig und konstant 1 Sekunde ein / 1 Sekunde aus sein. Größere 
Abweichungen sieht man normalerweise mit freiem Auge sofort, zur Not 
einfach eine Uhr mit Sekundenzeiger daneben legen. Kleinere Abweichungen 
kannst du leicht feststellen, in dem du zb 60 derartige Zyklen abwartest 
und mit einer Uhr stoppst. Wenn die Taktfrequenz stimmt, dann dauern 60 
Blinker ziemlich genau 2 Minuten. Noch genauer wirst du es für deine 
Zwecke nicht brauchen.


Du kannst auch mal den Gegentest machen und ins OSCCAL Register einen 
kleinen Wert (10) oder einen großen Wert (250) laden und dir ansehen, 
wie sich das auf das Geblinke auswirkt.

Und wenn die 107 nicht stimmen, dann weißt du dann auch ob du den Wert 
vergrößern oder verkleinern musst, um die 60 Blinker auf 2 Minuten zu 
bringen.

von Wolly2.0 (Gast)


Lesenswert?

der OSCCAL wert 107 ist aber der standard wert - also für 9,6Mhz
sollte dann also für 4,8MHz 53,5 respektive 53/54 sein oder irre ich 
schon wieder

von Karl H. (kbuchegg)


Lesenswert?

Wolly2.0 schrieb:
> der OSCCAL wert 107 ist aber der standard wert - also für 9,6Mhz
> sollte dann also für 4,8MHz 53,5 respektive 53/54 sein oder irre ich
> schon wieder

Du irrst.

Beim Umstellen von 9.6Mhz auf 4.8Mhz werden ganze Baugruppen 
umgeschaltet. OSCCAL ist nur die Feineinstellung auf die Zielfrequenz. 
Die kann (und wird) bei 9.6Mhz ein wenig anders als bei 4.8Mhz sein.

Stell dir das ganze wie bei einem (analogen) Oszilloskop vor.
Mit dem großen Knopf stellst du die Ablenkfrequenz grob ein. Und daneben 
ist noch ein kleiner Einstellknopf mit dem man noch ein wenig Feintuning 
machen kann. Das ist der OSCCAL.

von Wolly2.0 (Gast)


Lesenswert?

hmm ok dann tast ich mich mal n bischen an den wert ran mit der 
blink-uhr methode
müsste man ja sogar mit ++ un -- otf einstellen können

von Karl H. (kbuchegg)


Lesenswert?

Wolly2.0 schrieb:
> hmm ok dann tast ich mich mal n bischen an den wert ran mit der
> blink-uhr methode
> müsste man ja sogar mit ++ un -- otf einstellen können

Machs um Gottes Willen nicht zu kompliziert.
Mit Halbieren des Suchbereichs hast du das ratz fatz ausgemittelt.

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.