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
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):
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.
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
voidinitLEDStripe(){
2
for(inti=0;i<sizeof(ledValues);i+=2){
3
structledStripeValuetest;
4
test.pin=STRIPE_H;
5
test.port=GPIOD;
6
test.action=Bit_RESET;
7
ledValues[i]=test;
8
structledStripeValuetest2;
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
voidsetOneLedRed(){
19
for(inti=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(inti=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(inti=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:
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.
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