Forum: Mikrocontroller und Digitale Elektronik STM32 TIM4 Interrupt zur Ansteuerung LEDs ähnlich PL9834


von Joerg (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich arbeite mich in den STM32 mit dem STM32F4Discovery (407) ein (Coocox 
IDE 1.7.8).
Nun habe ich ein paar von den LEDs (siehe Anhang, voltage step down 
programmeable).
Da die mit 5V laufen, hab ich noch einen 74HCT125 zwischen geschaltet 
(siehe Anhang).

Da mein Interrupt-Ansatz zuerst nicht funktionierte, habe ich das ganze 
sehr banal angegangen.
Bitte nicht schlagen:

// STRIPE_PORT=GPIOD
// STRIPE_H = GPIO_Pin_0, H da im Ruhezustand H
// STRIPE_L = GPIO_PIN_1, L da im Ruhezustand L
// SHORT_DELAY = 35
void writeHalf(uint8_t intensity) {
  int i;
  uint32_t j;
  for(i=0;i<(8-intensity);i++) {
    GPIO_WriteBit(STRIPE_PORT, STRIPE_H, Bit_RESET);
    for(j=0;j<SHORT_DELAY;j++) {} // 4µs... als Short delay noch 288 
war...
    GPIO_WriteBit(STRIPE_PORT, STRIPE_H, Bit_SET);
    for(j=0;j<SHORT_DELAY;j++) {} // 4µs...
  }
  for(i=0;i<intensity;i++) {
    GPIO_WriteBit(STRIPE_PORT, STRIPE_L, Bit_SET);
    for(j=0;j<SHORT_DELAY;j++) {} // 4µs...
    GPIO_WriteBit(STRIPE_PORT, STRIPE_L, Bit_RESET);
    for(j=0;j<SHORT_DELAY;j++) {} // 4µs...
  }
}

Das ganze für 5 Leds im Hauptprogramm je LED 3 mal aufgerufen, Werte 
zwischen 0 und 8.
Alles super soweit, bekomme die Farben die ich möchte.

Also wieder ran an die Interrupt-Routine:
// HSE_VALUE = (uint32_T)8000000; PLL_M = 8; SystemInit() wird als 
erstes in main ausgeführt.
void initTimerLEDStripe() {
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
  TIM_TimeBaseInitTypeDef tim4Def;
  NVIC_InitTypeDef nvicDef4;
  tim4Def.TIM_ClockDivision = TIM_CKD_DIV1;
  tim4Def.TIM_CounterMode = TIM_CounterMode_Up;
  // approx 0.6µs, da APB1 mit Teiler 4 und Multiplikator 2 = 84MHz
  tim4Def.TIM_Period = 4;
  tim4Def.TIM_Prescaler = 9;
  TIM_TimeBaseInit(TIM4, &tim4Def);

  TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
  nvicDef4.NVIC_IRQChannel = TIM4_IRQn;
  nvicDef4.NVIC_IRQChannelCmd = ENABLE;
  nvicDef4.NVIC_IRQChannelPreemptionPriority = 0x0F;
  nvicDef4.NVIC_IRQChannelSubPriority = 0x0F;
  NVIC_Init(&nvicDef4);
  // Do nothing for the time beeing...

  TIM_Cmd(TIM4, DISABLE);
}

Hier die Interrupt-Routine und variablen. Ich habe den Ansatz gewählt, 
jede Zustandsänderung in einem Array zu hinterlegen.

struct ledStripeValue {
  BitAction action;
  uint16_t pin;
  GPIO_TypeDef* port;
};

volatile struct ledStripeValue ledValues[5*3*8*2];
volatile uint32_t counter = 0;

void TIM4_IRQHandler(void) {
  TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
  if(counter < sizeof(ledValues)) {
    GPIO_WriteBit(ledValues[counter].port, ledValues[counter].pin, 
ledValues[counter].action);
    counter++;
  } else {
    counter++;
    if(counter > 26000) {
                       // Latch time 3ms done...
      TIM_Cmd(TIM4, DISABLE);
      counter = 0;
    }
  }
}

Hauptprogramm:

int main(void)
{
  SystemInit();
  init(); // TIM2 and TIM3 with 1ms interrupt, TIM2 counts uint32_t 
millisCounter up
  initTimer();

  GPIO_WriteBit(STRIPE_PORT, STRIPE_H, Bit_SET);
  GPIO_WriteBit(STRIPE_PORT, STRIPE_L, Bit_RESET);

  initTimerLEDStripe();
  initLEDStripe();

  while(1) {
    setOneLedRed(); // like initLEDStripe, only setting appripriate bits 
to H
    simpleDelay(1000); // wait a second
                initLEDStripe(); // Clear all LEDs
    simpleDelay(1000);
  }
}

Es passiert nix, rein gar nix. Mit dem Debugger - dem ich hierbei nicht 
wirklich traue - sieht es so aus, als wenn das Hauptprogramm priorität 
gegenüber der Interrupt-Routine hat.
Anscheinend zählt er nicht hoch. Er steht beim debuggen in der 
simpleDelay, der Brechpunkt in der Interrupt wird nur selten aufgerufen.

Ach ja, simpleDelay:
void simpleDelay(uint32_t ms) {
  uint32_t current = millisCounter;
  while( (millisCounter-current) <= ms) {
  }
}

Wenn ich so nachrechne, dann sollte es mit der Einfachmethode nicht 
gehen, For-Schleife wird 35 mal durchlaufen. Bei 168MHz wären das 0,2µs. 
Die LEDs brauchen aber angeblich 1µs.
Was mache ich falsch, wo ist mein Denkfehler? Werde daraus nicht schlau.

Besten Dank im voraus für jede Hilfe.

Jörg

von Joerg (Gast)


Lesenswert?

Oh, sehe gerade in der Schaltung ist der Abgriff zwischen den 
Widerständen zum D_in des ersten LEDs nicht zu sehen.

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


Lesenswert?

Wo initialisierst du denn die GPIOs?
Un dann:
Joerg schrieb:
> TIM_Cmd(TIM4, DISABLE);

Damit endet sowohl die Initialisierung als auch die IRQ Routine. So wird 
dein Timer aber nie gestartet, denn irgendwo sollte dann ja mal
1
TIM_Cmd(TIM4, ENABLE);
stehen.
Und wenn du schon Timer 4 nimmst, warum nicht gleich mit den PWM 
Registern? Da sind die drei Kanäle schon fix und fertig und die ganze 
Sache läuft prima im Hintergrund.
So gehts mit Kanal 2 (war die orange LED, wimre):
1
TIM_OCInitTypeDef TIM_OCInitStructure;
2
  TIM_OCStructInit(&TIM_OCInitStructure);
3
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
4
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
5
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
6
  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
7
  TIM_OCInitStructure.TIM_Pulse = 1050;
8
  TIM_OC2Init(TIM4, &TIM_OCInitStructure);

Wenn der SysTick läuft, ist

Joerg schrieb:
> nvicDef4.NVIC_IRQChannelPreemptionPriority = 0x0F;
>   nvicDef4.NVIC_IRQChannelSubPriority = 0x0F;

Möglicherweise noch hinter SysTick. Gib ihm mal etwas höhere Priorität.
Sollte aber trotzdem klappen.

: Bearbeitet durch User
von Joerg (Gast)


Lesenswert?

Hallo,

erstmal vielen Dank, PWM werde ich mir mal anschauen, wenn ich den 
normalen Timermodus ans laufen bekomme. Bin mir noch nicht so ganz im 
klaren darüber, wie ich damit den Tri-State (H-M-L) abbilde.

TIM4 disable ich in der initialisierung, da ich den Timer nur laufen 
lassen wollte, wenn ich etwas neues Anzeigen möchte. Den interrupt zu 
disablen würde auch reichen, das hab ich noch nicht geprüft, wie das 
geht.

Hier die Routinen zum Füllen des Arrays, hier wird dann auch der Timer 
enabled.
1
void initLEDStripe() {
2
  for(int i=0; i<sizeof(ledValues);i+=2) {
3
    struct ledStripeValue test;
4
    test.pin = STRIPE_H;
5
    test.port = GPIOD;
6
    test.action = Bit_RESET;
7
    ledValues[i] = test;
8
    struct ledStripeValue test2;
9
    test2.pin = STRIPE_H;
10
    test2.port = GPIOD;
11
    test2.action = Bit_SET;
12
    ledValues[i+1] = test2;
13
  }
14
  counter = 0;
15
  TIM_Cmd(TIM4, ENABLE);
16
}
17
18
void setOneLedRed() {
19
  for(int i=0;i<40;i+=2) {
20
    ledValues[i].pin = STRIPE_H;
21
    ledValues[i].action = Bit_RESET;
22
    ledValues[i+1].pin = STRIPE_H;
23
    ledValues[i+1].action = Bit_SET;
24
  }
25
        // Rot nur 50%...
26
  for(int i=40;i<48;i+=2) {
27
    ledValues[i].pin = STRIPE_L;
28
    ledValues[i].action = Bit_SET;
29
    ledValues[i+1].pin = STRIPE_L;
30
    ledValues[i+1].action = Bit_RESET;
31
  }
32
  for(int i=48;i<(5*8*3*2);i+=2) {
33
    ledValues[i].pin = STRIPE_H;
34
    ledValues[i].action = Bit_RESET;
35
    ledValues[i+1].pin = STRIPE_H;
36
    ledValues[i+1].action = Bit_SET;
37
  }
38
  counter = 0;
39
  TIM_Cmd(TIM4, ENABLE);
40
}

GPIOs werden in meiner init-Methode initialisiert:
1
void init(void) {
2
  GPIO_InitTypeDef GPIO_InitStructure;
3
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
4
  GPIO_InitStructure.GPIO_Pin = LED_GN | LED_OR | LED_RD | LED_BL | STRIPE_H | STRIPE_L;
5
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
6
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
7
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
8
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
9
  GPIO_Init(GPIOD, &GPIO_InitStructure);
10
}

Ich werde heute Abend mal die Prioriäten ändern.
Für TIM2,3,4
NVIC_IRQChannelPreemptionPriority = 0x00;
Für TIM2
NVIC_IRQChannelSubPriority = 0x00;
Für TIM3
NVIC_IRQChannelSubPriority = 0x08;
Für TIM4
NVIC_IRQChannelSubPriority = 0x0F;

Das würde dann meiner vollständigen Anwendung gerecht, in der ich TIM2 
als millisekunden-Timer nutze, der recht präzise sein sollte, TIM3 wird 
als Überwachungs/Ausschalt-Timer genutzt und TIM4 für die Anzeige.

Werde berichten, wenn es daran liegt, aber ich meine die Prioritäten 
auch mal angepasst zu haben, aber heute morgen im Zug habe ich dann 
nochmal genauer nachgelesen und bin mir nicht sicher, ob ich das gestern 
Abend richtig gemacht habe.

von Joerg (Gast)


Lesenswert?

OH MANN!!!

Manchmal sollte ich das zwischen meinen Ohren prüfen lassen...
1
for(int i=0; i<sizeof(ledValues);i+=2) {

dabei sollte das heißen:
1
#define array_length(x) (sizeof(x)/sizeof((x)[0]))
2
3
for(int i=0; i<array_length(ledValues);i+=2) {

Und schon klappt es, wenn ich alle sizeof durch array_length ersetze. 
Ich will da die Anzahl der Array-Elemente und nicht die bytes haben...

Geh jetzt erstmal in eine Ecke und schäm mich.

Danke für die Hilfe!
Jörg

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.