Forum: Mikrocontroller und Digitale Elektronik Tastverhältnis 0/4,1/4,3/4 auf STM32 programmieren


von J. -. (Gast)


Lesenswert?

Grüß euch,

gerade habe ich auf dem AVR mein erstes C-Programm geschrieben, das in 
der main-Schleife nichts als luftleeren Raum enthält ;)
Es geht um Funksteckdosen, und mit OCRA und OCRB kann man das auf dem 
AVR recht effizient erledigen.
Was ich brauchte, war ein Signal mit ~3900Hz, und eben obige Bitzeiten; 
was auch gut klappt.
Falls jemand das brauchen kann oder sogar sucht (obwohl der Hype um die 
Protokolle ja längst gegessen ist und genügend Dokumentation der Sache 
da ist), hier ein Snip:
1
//ATMega32, F_CPU 14,7456 MHz
2
#define FAM 4600
3
volatile uint8_t cnt=0,bit=0;
4
volatile uint8_t code[32]={1,3,1,1,1,3,1,3,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,3,1,0,0,0,0,0,0,0}; //Code A, code[23] = on/off
5
//volatile uint8_t code[32]={1,3,1,1,1,3,1,3,1,3,1,1,1,1,1,1,1,1,1,3,1,3,1,3,1,0,0,0,0,0,0,0}; //Code B, code[23] = on/off
6
//volatile uint8_t code[32]={1,3,1,1,1,3,1,3,1,1,1,3,1,1,1,1,1,1,1,3,1,3,1,3,1,0,0,0,0,0,0,0}; //Code C, code[23] = on/off
7
//volatile uint8_t code[32]={1,3,1,1,1,3,1,3,1,3,1,3,1,1,1,1,1,1,1,3,1,3,1,3,1,0,0,0,0,0,0,0}; //Code D, code[23] = on/off
8
9
int main (void) 
10
{
11
//AM433
12
DDRD |= (1<<PD0);
13
PORTD &= ~(1<<PD0); //Ausgangsport -> zum Eingang Amplitudenmodulation 433MHz-Sender
14
15
//Setze Timer 1 auf ca. 300µs CTC
16
TCCR1A = 0;
17
TCCR1B = (1<<WGM12)|(1<<CS10); //
18
OCR1A = 4*FAM;//3; //50 Hz ^ 20ms
19
OCR1B = FAM;//3; //50 Hz ^ 20ms
20
TIMSK = (1<<OCIE1A)|(1<<OCIE1B);
21
TCNT1 = 0;
22
while(1)
23
{
24
    switch (cnt)
25
    {//test ein- und ausschalten Kanal B/C
26
      case 64:
27
        code[23] = 1;
28
        code[9] = 3;
29
        code[11] = 1;
30
      break;
31
      
32
      case 128:
33
        code[23] = 1;
34
        code[9] = 1;
35
        code[11] = 3;
36
      break;
37
38
      case 192:
39
        code[23] = 3;
40
        code[9] = 3;
41
        code[11] = 1;
42
      break;
43
44
      case 0:
45
        code[23] = 3;
46
        code[9] = 1;
47
        code[11] = 3;
48
      break;
49
    }
50
51
}
52
}
53
//-------------------------------------------------------------------------
54
ISR (TIMER1_COMPA_vect)
55
{
56
  OCR1B = code[bit]*FAM;
57
  if (code[bit]>0) PORTD |= (1<<PD0);
58
  if (bit++>31)
59
  {
60
    bit=0;
61
    OCR1B = code[bit]*FAM;
62
    cnt++;
63
  }
64
}
65
//-------------------------------------------------------------------------
66
ISR (TIMER1_COMPB_vect)
67
{
68
  PORTD &= ~(1<<PD0);
69
}


Das ist natürlich noch nicht optimal, sondern code[] gehört noch in eine 
32-bit-variable, aber es tut und ich will es auf dem AVR auch nicht 
nutzen.


Bei dem STM32 würde ich es so machen, daß ich einen Timer auf die 
entsprechende Periodendauer einstelle (1 / 3900Hz), und zuvor wie oben 
vorerst mal 32 Variablen setze mit Werten von 0,1 oder 3. So würde man 
das auf jeden Fall geknackt kriegen. Aber ich weiß nicht, ob es schlau 
ist.

Schlauer wäre es, wenn es beim STM32-Timer auch diese Art OC-Interrupts 
wie beim AVR gäbe. Noch schlauer wär's wahrscheinlich, wenn man das 
entsprechende Manual liest g

Wie würdet ihr das machen? Einfach einen Timer aufsetzten, und die 
Bitzeiten per Software abnudeln wie o.a., oder eher mit (evtl. 
vorhandenen) Timerfunktionen?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Jürgen S. schrieb:
> Schlauer wäre es, wenn es beim STM32-Timer auch diese Art OC-Interrupts
> wie beim AVR gäbe. Noch schlauer wär's wahrscheinlich, wenn man das
> entsprechende Manual liest g

Zumindest der 'Advanced Timer' (einen davon sollte dein STM32 mindestens 
haben) bietet 3 (oder warens 4?) CC Register mit je einem Interrupt und 
den entsprechenden PWM Ausgängen.

Jürgen S. schrieb:
> Wie würdet ihr das machen? Einfach einen Timer aufsetzten, und die
> Bitzeiten per Software abnudeln wie o.a., oder eher mit (evtl.
> vorhandenen) Timerfunktionen?

Faul, wie ich bin, lasse ich das den Interrupt machen, damit ich in der 
Hauptschleife anderen Krams machen kann. Allerdings nehme ich im 
Motorkontroll Projekt dafür den Overflow Interrupt und nicht die CC's.

: Bearbeitet durch User
von Jan B. (berge)


Lesenswert?

Moin,
ich habe nicht genau verstanden, was du machen willst und verstehe den 
AVR Code auch nicht auf Anhieb. Wenn ich richtig interpoliere, brauchst 
du 1x eine zyklische Abtastung eines Eingangs und 1x ein zyklisches 
Setzen eines Ausgangs.

Wenn ein OC IRQ ein "Overcount" ist, dann ja, so etwas gibt es auf den 
STM32s. Hier http://mikrocontroller.bplaced.net/wordpress/?page_id=2251 
gibt es eine Library für Timer, die genau diesen IRQ Typ nutzt. Du 
kannst in der Callback Funktion dann den Code platzieren, den du 
ausgeführt haben magst. Für die Berechnung des Prescalers und der 
Periode habe ich den Libraries eine Funktion hinzugefuegt:
1
void UB_TIMER2_Init_WithFrequency( float desiredFreqInHz )
2
{
3
    // Hole Eingangstakt des Timers
4
    RCC_ClocksTypeDef RCC_Clocks;
5
    RCC_GetClocksFreq(&RCC_Clocks);
6
    int inputClkFreq = RCC_Clocks.PCLK1_Frequency;
7
8
    // Berechne prescaler und periode
9
    int frequencyDivNecessary = (float) inputClkFreq / desiredFreqInHz;
10
    frequencyDivNecessary >> 16;  // Schiebe 16 Bit nach links, da 16 Bit breite periode moeglich
11
    int timerMaxCountValue = ( inputClkFreq / (frequencyDivNecessary + 1) ) / desiredFreqInHz;
12
13
  // Setze Timer auf
14
  UB_TIMER2_Init(frequencyDivNecessary, timerMaxCountValue);
15
}

Hoffe das hilft dir.
Liebe Grüße, Jan

von J. -. (Gast)


Lesenswert?

Matthias Sch. schrieb:

> Faul, wie ich bin, lasse ich das den Interrupt machen, damit ich in der
> Hauptschleife anderen Krams machen kann. Allerdings nehme ich im
> Motorkontroll Projekt dafür den Overflow Interrupt und nicht die CC's.
Ja klar, das ist das Beste so ;)
Ich habe mich inzwischen ein bißchen durch das Manual durchgelesen und 
brauche die Komplexität von Timer 1 oder 8 nicht. Jetzt habe ich den 
TIM4 als normale PWM aufgesetzt, und es geht.
Am meisten Kopfzerbrechen hat mir der Zugriff auf Register 0x40000834 
(CCR1 von TIM4) bereitet (wie, wieso, was ist mit dem Offset etc.), bis 
ich dann in den Systembibliotheken nach TIM_pulse gesucht habe und dann 
TIM4->CCR1 = x fand; damit ist alles erledigt.
Anfangs neigt man ja dazu, immer die gesamte InitStruktur nochmal 
durchzunudeln, weil man es nicht besser weiß. Das war jetzt also die 
erste, erfolgreiche Registerprogrammierung g


Jan Berg schrieb:
> Moin,
> ich habe nicht genau verstanden, was du machen willst und verstehe den
> AVR Code auch nicht auf Anhieb. Wenn ich richtig interpoliere, brauchst
> du 1x eine zyklische Abtastung eines Eingangs und 1x ein zyklisches
> Setzen eines Ausgangs.
Hallo Jan, danke für Deinen Code. Es ist viel trivialer als es 
angekommen ist, es braucht keinen Inputpin.
Der AVR-Code oben macht einfach eine konstante Zeitdauer mit OCA 
(sozusagen ein verkürzter Overflow-Interrupt mit Periode 4*FAM), und 
unterteilt diese Zeitdauer mittels OCB nochmal, also macht nichts anders 
als ein Tastverhältnis; und das 32mal hintereinander.
Die Library von Uwe Becker habe ich schon oft einsetzen können, ist 
wirklich ein tolles, hilfreiches Stück Arbeit, was da dahintersteckt.
Prinzipiell schalte ich jetzt so ähnlich wie von Dir vorgeschlagen im 
Interrupt des Timers die "Bits" um und weiter. Es gefällt mir allerdings 
noch nicht so richtig (es funktioniert sehr gut und blockiert auch 
nicht, aber wenn ich die Macht der Timer abschätze, steckt in meinem 
Progrämmchen noch eine Menge Verbesserungspotential ;) )

Danke euch beiden und Gruß

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.