Forum: Mikrocontroller und Digitale Elektronik PWM auslesen STM8


von B_S (Gast)


Lesenswert?

Hallo zusammen,

Ich möchte bei einem Projekt ein eingehendes PWM-Signal auslesen, dazu 
stehen mir aber leider wegen unüberlegter PIN-Belegung die PWMI-Timer 
Register des STM8 nicht zur Verfügung. Dementsprechend wollte ich 
einfach die Zeit, die zwischen zwei Taktflanken vergeht auslesen und 
"berechnen". Zum Test habe ich einen Pulsgenerator mit einem bestimmten 
PWM-Signal angelegt und mir im Debug-Modus die Variablen (natürlich 
nachdem die Zeit gemessen war) angeschaut.
Nun ist folgendes Problem aufgetaucht und zwar wird bei einem duty-cycle 
>50% die Berechnung wohl eher ein Random-Number-Generator, dieses 
Problem tritt bei einem duty-cycle von ca 30% auch jeden 3.Aufruf auf. 
Woran liegt das, dass der Interrupt einfach mal zu früh triggert? Bricht 
die Spannung am  Pulsgenerator vielleicht alle paar Flanken mal ein 
(Kann ich das ein einem Oszi überprüfen?)? Oder liegt es am Programm? 
GIbt es hierfür vielleicht ein "bekanntes" workaround, sodass wenigstens 
die Werte annähernd genau sind?
Hier noch das Programm:
main:
1
main()
2
{
3
  _asm("sim");
4
  CLK_CONF();
5
  GPIO_CONF();
6
  TIM2_CONF();
7
  TIM4_CONF();
8
  _asm ("rim");    
9
while(1);
Tim4 ISR & Conf:
1
void TIM4_CONF(void){ 
2
  TIM4->PSCR |= 0x02; 
3
  TIM4->CNTR =  0xFA; 
4
  TIM4->IER  |= 0x01;  
5
  TIM4->CR1  =  0x01;  
6
}
7
@far @interrupt void TIM4_UPD_OVF_IRQHandler(void)
8
{
9
  /*GPIOD->ODR ^= ((1<<3)|(1<<4));
10
  GPIOA->ODR ^= 1<<3;*/
11
  if(FbFlag){
12
    ovFlag++;
13
  }else{
14
    ovFlag = 0;
15
  }
16
  TIM4->SR1 = ~0x01; //Clear Flag
17
}
PortC-Interrupt (so konfiguriert, dass EXTI auf rising und falling 
triggert):
1
@far @interrupt void EXTI_PORTC_IRQHandler(void)
2
{
3
  if((GPIOC->IDR & 1<<6) == (1<<6)){
4
    if(FlankFlag == 0){
5
      FbFlag = TRUE;
6
      T1_H = TIM4->CNTR;
7
      FlankFlag++;
8
    }else if(FlankFlag == 2){
9
      T2_H = ((TIM4->CNTR)+250*ovFlag);
10
      Period = T2_H-T1_H;
11
      T1_L = T1_L/100;
12
      T2_H = T2_H/100;
13
      FbFlag = FALSE;
14
      FlankFlag = 0;
15
      DutyCycle = (float)((T1_L*100)/T2_H);
16
      ovFlag = 0;
17
    }
18
  }else if((GPIOC->IDR & 1<<6) == 0){
19
    if(FlankFlag == 1){
20
      T1_L = ((TIM4->CNTR)+250*ovFlag);
21
      T1_L_OVF = ovFlag;
22
      FlankFlag++;
23
    }
24
  }
25
  
26
}

von B_S (Gast)


Lesenswert?

Also meine bisherige Lösung für das Problem ist das Auslesen von zwei 
Perioden, da komischerweise die zweite Periode immer sehr genau 
ausglesen wird. Für andere Lösungsvorschläge bin ich natürlich immer 
gerne offen!

Hier der Code für den Interrupt:
1
@far @interrupt void EXTI_PORTC_IRQHandler(void)
2
{
3
  if((GPIOC->IDR & 1<<6) == (1<<6)){
4
    switch(FlankFlag){
5
      case 0:
6
        FbFlag = TRUE;
7
        T1_H = TIM4->CNTR;
8
        FlankFlag++;
9
        break;
10
      case 2:
11
        T2_H = TIM4->CNTR + 255*ovFlag;
12
        FlankFlag++;
13
        break;
14
      case 4:
15
        T3_H = TIM4->CNTR + 255*ovFlag;
16
        FbFlag = FALSE;
17
        FlankFlag = 0;
18
        High1 = T1_L - T1_H;
19
        Low1 = T2_H - T1_L;
20
        High2 = T2_L - T2_H;
21
        Low2 = T3_H - T2_L;
22
        Period1 = High1+Low1; // = Zeit in us
23
        Period2 = High2+Low2;
24
        DutyCycle = (High2/10)/(Period2/1000);
25
        break;
26
      default: 
27
        FlankFlag = 0;
28
        ovFlag = 0;
29
        FbFlag = FALSE;
30
      break;
31
    
32
    }
33
  }else if((GPIOC->IDR & 1<<6) == 0){
34
    switch(FlankFlag){
35
      case 1:
36
        T1_L = TIM4->CNTR + 255*ovFlag;
37
        FlankFlag++;
38
        break;
39
      case 3:
40
        T2_L = TIM4->CNTR + 255*ovFlag;
41
        FlankFlag++;
42
        break;
43
      default: 
44
        FlankFlag = 0;
45
        ovFlag = 0;
46
        FbFlag = FALSE;
47
        break;
48
    }
49
  }

von Viktor B. (coldlogic)


Lesenswert?

Hi B_S,
Kannst du nachschauen, ob das Problem gelöst wird, wenn du bei der 
Zuweisung von T1_H dasselbe mit dem ovFlag machst wie bei den anderen 
Zuweisungen und dafür den ovFlag gleich bei der Berechnung löschst? Also 
in etwa so:
1
 
2
@far @interrupt void TIM4_UPD_OVF_IRQHandler(void)
3
{
4
  ovFlag = 1;
5
  TIM4->SR1 = ~0x01; //Clear Flag
6
}
1
@far @interrupt void EXTI_PORTC_IRQHandler(void)
2
{
3
  if(GPIOC->IDR & 1<<6){
4
    if(FlankFlag == 0){
5
      FbFlag = TRUE;
6
      T1_H = ((TIM4->CNTR)+255*ovFlag);
7
      FlankFlag++;
8
      ovFlag = 0;
9
    }else if(FlankFlag == 2){
10
      T2_H = ((TIM4->CNTR)+255*ovFlag);
11
      Period = T2_H-T1_H;
12
      T1_L = T1_L/100;
13
      T2_H = T2_H/100;
14
      FbFlag = FALSE;
15
      FlankFlag = 0;
16
      ovFlag = 0;
17
      DutyCycle = (float)((T1_L*100)/T2_H);
18
      
19
    }
20
  }else{
21
    if(FlankFlag == 1){
22
      T1_L = ((TIM4->CNTR)+255*ovFlag);
23
      FlankFlag++;
24
      ovFlag = 0;
25
    }
26
  }
27
}
Und noch was: sind die Tx_X Variablen 8 bit oder 16 bit?

von B_S (Gast)


Lesenswert?

Hallo Viktor,

Auch das ändert leider nichts daran. Komischerweise ist jeder 
Auslesevorgang nach der ersten Periode auf 0,2µs genau, daher werde ich 
wohl in meiner switch-case einfach die ersten zwei Flanken überspringen.

Die Variablen Tx_H/Tx_L sind 16-Bit Variablen.

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.