PWM auslesen STM8

Gast #6160521
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
}
Gast #6160675
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
  }
#6161319
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?
Gast #6165163
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren