Forum: Mikrocontroller und Digitale Elektronik STM32F103 - Timer2 Overflow Interrupt


von Max M. (maxmicr)


Lesenswert?

Hallo,

ich versuche aktuell, einen Timer2 Overflow Interrupt für den 
STM32F103C8 zu konfigurieren, leider klappt das nicht, mein aktueller 
Stand:
1
#include "stm32f10x_conf.h"
2
3
void TIM2_IRQHandler(void){
4
    GPIOA->ODR ^= 1;            //Toggle Pin A0
5
    NVIC->ICPR[0] &= ~(1<<28);  //Remove Interrupt pending bit
6
}
7
8
void initTimer2(){
9
    TIM2->SMCR &= ~0b111;       //Slave mode disabled, clocked by internal clock
10
    TIM2->PSC = 1;              //Prescaler
11
    NVIC->ISER[0] |= (1<<28);   //Enable Timer2 Global Interrupts
12
    NVIC->IP[7] |= 0xFF;        //lowest Priority, TIM2_Interrupt = 28, 28 / 4 = 7
13
    TIM2->CR1 |= 1;             //Counter enable
14
15
}
16
17
int main(void)
18
{
19
    RCC->APB1ENR = 0b01;        //Enable Timer2 Clock
20
    RCC->APB2ENR = 0b100;       //Enable Port A Clock
21
22
    GPIOA->CRL |= 0b0011;       //Pin A0 PushPull Output
23
24
    initTimer2();
25
26
    while(1)
27
    {
28
29
    }
30
}

Beim Debuggen wird nie in den Interrupt Handler gesprungen.

: Bearbeitet durch User
von Tippgeber (Gast)


Lesenswert?

Ich habe von STM32 keine Ahnung, aber fehlt da nicht noch ein 
interrupt_enable oder ä.?

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


Lesenswert?

Max M. schrieb:
> void initTimer2(){
>     TIM2->SMCR &= ~0b111;       //Slave mode disabled, clocked by
> internal clock
>     TIM2->PSC = 1;              //Prescaler
>     NVIC->ISER[0] |= (1<<28);   //Enable Timer2 Global Interrupts
>     NVIC->IP[7] |= 0xFF;        //lowest Priority, TIM2_Interrupt = 28,
> 28 / 4 = 7
>     TIM2->CR1 |= 1;             //Counter enable

Das ist sehr unübersichtlich und du solltest selber erstmal zusehen, das 
du da die richtigen Bits triffst - was z.B. erwartest du bei
1
TIM2->SMCR &= ~0b111;       //Slave mode disabled, clocked by internal clock
Und wo gibst du den NVIC Channel dann frei?

von Max M. (maxmicr)


Lesenswert?

Matthias S. schrieb:
> was z.B. erwartest du bei

Dass die ersten 3 bits im SMCR Register 0 sind (also Slave mode 
disabled).

Matthias S. schrieb:
> Und wo gibst du den NVIC Channel dann frei?

Wie mache ich das? Seh ich im Programming Manual nicht.

von Christopher J. (christopher_j23)


Lesenswert?

Max M. schrieb:
> NVIC->ICPR[0] &= ~(1<<28);  //Remove Interrupt pending bit

Das braucht man nicht, weil das der Cortex von alleine löscht, sobald 
die Interruptroutine ausgeführt wird. Stattdessen sollte man im Handler 
das UIF (Update Interrupt Flag) im Status Register des Timer 2 löschen:
1
TIM2->SR &= ~(TIM_SR_UIF);


Max M. schrieb:
> TIM2->SMCR &= ~0b111;       //Slave mode disabled, clocked by
> internal clock

Das braucht man streng genommen auch nicht, weil das dem Wert nach Reset 
entspricht. Außerdem würde ich wenn dann direkt auf die vorgegebenen 
Bitmasken zurückgreifen, also
1
TIM2->SMCR &= ~(TIM_SMCR_SMS);
Das finde ich persönlicher deutlich leserlicher als 0b111


Max M. schrieb:
> NVIC->ISER[0] |= (1<<28);   //Enable Timer2 Global Interrupts

Das geht einfacher über eine extra dafür vorgesehene CMSIS-Funktion (aus 
core_cm3.h)
1
NVIC_EnableIRQ(TIM2_IRQn);


Max M. schrieb:
> NVIC->IP[7] |= 0xFF;        //lowest Priority, TIM2_Interrupt = 28,
> 28 / 4 = 7

Das brauchst du streng genommen auch nicht, weil standardmäßig einfach 
alle Interrupts mit der gleichen Priorität laufen ,nämlich mit 0, also 
der höchsten vom Benutzer definierbaren Priorität. Man muss keine 
Priorität zuweisen, sofern man keine Preemption will. Will man eine 
Priorität, dann geht das deutlich komfortabler mit der 
NVIC_SetPriority-Funktion (ebenfalls aus core_cm3.h):
1
NVIC_SetPriority(TIM2_IRQn, 0xFF);


Eine ganz wichtige Sache fehlt dir noch in deiner timer_init() und zwar 
musst du für den Timer selbst den Update-Interrupt aktivieren:
1
TIM2->DIER |= TIM_DIER_UIE;


Wenn du willst, dass der Timer beim Debuggen angehalten wird, dann 
kannst du das mit
1
DBGMCU->CR |= DBGMCU_CR_DBG_TIM2_STOP;
erreichen, siehe Kapitel 31.16.3 im Reference Manual.

von Max M. (maxmicr)


Lesenswert?

Vielen, vielen Dank Christopher! Funktioniert alles wunderbar jetzt :)
1
#include "stm32f10x_conf.h"
2
3
void TIM2_IRQHandler(void){
4
    GPIOA->ODR ^= 1;            //Toggle Pin A0
5
    TIM2->SR &= ~TIM_SR_UIF;    //Clear Pending interrupt flag
6
}
7
8
void initTimer2(){
9
    TIM2->PSC = 0;              //Prescaler
10
    TIM2->ARR = 900;            //Reload value
11
    TIM2->DIER |= TIM_DIER_UIE; //Enable Update Interrupt
12
    NVIC_EnableIRQ(TIM2_IRQn);  //Enable Timer2 Global Interrupts
13
    TIM2->CR1 |= 1;             //Counter enable
14
}
15
16
int main(void)
17
{
18
19
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;     //Enable Timer2 Clock
20
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;      //Enable Port A Clock
21
    GPIOA->CRL |= GPIO_CRL_MODE0;           //Pin A0 PushPull Output
22
23
    initTimer2();
24
25
    while(1)
26
    {
27
28
    }
29
}

Edit: Nochmal eine Frage:

Im Reference Manual steht im Kapitel "DAC" (S. 253):

"This section applies to connectivity line, high-density and XL-density 
STM32F101xx and STM32F103xx devices only."

Ich benutze einen STM32F103C8T6, also müsste der doch einen DAC haben, 
oder?

Allerdings kann ich im Register APB1ENR das "DACEN" Bit nicht setzten, 
beim Debuggen wird das nicht '1'

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Max M. schrieb:
> Ich benutze einen STM32F103C8T6

Nö, der gehört zur 'Medium Density Performance Line' und nicht zu den 
von dir genannten Produktionslinien. Hat also keinen DAC, wird auch bei 
den 'Key Features' nicht erwähnt.
http://www.st.com/en/microcontrollers/stm32f103c8.html

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Mist :(

von Christian J. (Gast)


Lesenswert?

Max M. schrieb:

> void initTimer2(){
>     TIM2->SMCR &= ~0b111;       //Slave mode disabled, clocked by
> internal clock
>     TIM2->PSC = 1;              //Prescaler
>     NVIC->ISER[0] |= (1<<28);   //Enable Timer2 Global Interrupts
>     NVIC->IP[7] |= 0xFF;        //lowest Priority, TIM2_Interrupt = 28,
> 28 / 4 = 7
>     TIM2->CR1 |= 1;             //Counter enable

Wer soll solchen Code eigentlich verstehen ohne das Datenbuch direkt 
daneben zu haben und jedes Bit einzeln nachzugucken? Wofür gibt es 
StdPeriphLib und die HAL? Effizienter ist das auf keinen Fall.

von Mampf unterwegs (Gast)


Lesenswert?

Christian J. schrieb:
> Wer soll solchen Code eigentlich verstehen ohne das Datenbuch direkt
> daneben zu haben und jedes Bit einzeln nachzugucken? Wofür gibt es
> StdPeriphLib und die HAL? Effizienter ist das auf keinen Fall.

Seh ich auch so... HAL würde ich(!) vermeiden, aber es gibt imho keinen 
Grund die StdPeriphLib nicht zu verwenden... Die ist nur eine minimale 
Abstraktion und nicht so ein überladenes Monster wie HAL :)

Auch wenn man mal von Controller zu Controller portiert hat das 
Vorteile... Man kann sich auf die Lib verlassen und muss nicht jedes Bit 
in jedem Register überprüfen, ob das noch so passt.

von Mike J. (linuxmint_user)


Lesenswert?

Max M. schrieb:
> Mist :(

Nutze doch einfach einen PWM-Pin, dahinter einen Widerstand und 
Kondensator und du bist fertig. Meist macht man es ja eh so dass man 
einen OpAmp nutzt um das Signal zu verstärken oder den analogen Ausgang 
belastbar zu machen.

von Bernd K. (prof7bit)


Lesenswert?

Christian J. schrieb:
> Max M. schrieb:
>
>> void initTimer2(){
>>     TIM2->SMCR &= ~0b111;       //Slave mode disabled, clocked by
>> internal clock
>>     TIM2->PSC = 1;              //Prescaler
>>     NVIC->ISER[0] |= (1<<28);   //Enable Timer2 Global Interrupts
>>     NVIC->IP[7] |= 0xFF;        //lowest Priority, TIM2_Interrupt = 28,
>> 28 / 4 = 7
>>     TIM2->CR1 |= 1;             //Counter enable
>
> Wer soll solchen Code eigentlich verstehen ohne das Datenbuch direkt
> daneben zu haben

Die Unleserlkichkeit liegt nicht an der Nichtverwendung der HAL oder 
StdPeriphLib.

Den Code hätte er auch perfekt lesbar hinschreiben können indem er 
vollständig auf so magische unbenamte Konstanten wie  ~0b111 oder 
(1<<28) verzichtet und stattdessen die benannten Konstanten aus den 
Headern genommen hätte, die sind nahezu selbsterklärend und dann hätte 
er sich auch die unnötigen Kommentare auf der rechten Seite sparen 
können.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Bernd K. schrieb:
> Den Code hätte er auch perfekt lesbar hinschreiben können indem er
> vollständig auf so magische unbenamte Konstanten wie  ~0b111 oder
> (1<<28) verzichtet und stattdessen die benannten Konstanten aus den
> Headern genommen hätte, die sind nahezu selbsterklärend und dann hätte
> er sich auch die unnötigen Kommentare auf der rechten Seite sparen
> können.

Diese Kommentare schreibe ich allerdings auch dran

Aber das wie unten, grad aus meinem Code kopiert, kann jeder verstehen 
und durch die Autoergänzung ist das auch keine Tipparbeit mehr. Ob man 
jetzt durch die CMSIS Konstanten schlauer wird, weiss ich nicht. Allein 
der Zeitaufaufwand das alles rauszusuchen... ich gucke fast nie ins 
Datenbuch, aber oft in die StdPeriphLibs rein und bin zufrieden.

Ich bin er Ansicht, dass es bei heutigen Gschwindigkeiten der UC und den 
aufgeblasenen Flash Speichern wichtiger ist lesbaren Code zu erzeugen 
als da das letzte Byte heraus zu optimieren. Der Compiler modelt das 
sowieso runter bis auf die Registerebene und entfernt alles, was 
dazwischen ist, wie Calls usw. Den Cortex muss man nicht mehr innendrin 
verstehen, es reicht wenn man sein Programmiermodell und die API dazu 
verstanden hat.
Ich benutze auch ein ILI9341 SPI Display, überhaupt keine Ahnung wie das 
genau funktioniert, packe eine fertige Lib drüber mit ein paar 
Zeichensätzen, Linien, Kreise, Farben....und es spielt einwandfrei.


 /* Der Timer clock'ed mit 64Mhz */
   TIM_TimeBaseStructInit(&TimerBaseInitStructure);
   TimerBaseInitStructure.TIM_Prescaler         = tim_presc;
   TimerBaseInitStructure.TIM_Period            = freq;
   TimerBaseInitStructure.TIM_ClockDivision     = TIM_CKD_DIV1;
   TimerBaseInitStructure.TIM_RepetitionCounter = 1;
   TIM_TimeBaseInit(TIM3, &TimerBaseInitStructure);

   TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

   /* Interrupt Handler anbinden */
   nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
   nvicStructure.NVIC_IRQChannelPreemptionPriority = 15;        // 
Niedrigste Priorität
   nvicStructure.NVIC_IRQChannelSubPriority = 0;
   nvicStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&nvicStructure);

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.