Guten Tag, ich bin Student und sollte mich für eine Hilfstätigkeit bei einem Professor mit Mikrocontrollern beschäftigen. Als Testobjekt wurde mir ein STM32F100RB in die Hand gegeben. Meine jetzige Aufgabe ist es, mich mit Timern, speziell dem Aufrufen von ISR's durch Timer, auseinanderzusetzen. Als Aufgabenstellung setzte ich mir, die LED zu togglen, wann immer eine gewisse Zeitvorbei ist. Dieses Togglen wollte ich in eine ISR packen, welche jedoch in meinem Programm nie aufgerufen wird. Habe ich beim Initialisiern des Interruptes irgendetwas vergessen/übersehen? Der Programmcode besteht teilweise aus Programmcodes, die hier hochgeladen wurden. Mein Programmcode, bzw. ein Screenshot davon,ist angehängt. Die IDE ist µVision5. Würde mich sehr über Hilfe freuen Daniel
Code als Bild posten ist böse. Dieses Forum kann *.c Dateien ordentlich darstellen. Dein Code hat vielzuviele magische Konstanten. Dafür gibt es sicher Definitionen im Header vom µC Hersteller. Dein Code setzt zwar die NVIC_InitSt member aber er eigentliche Aufruf von NVIC_Init() fehlt. Damit bleibt der Interrupt im NVIC disabled. TIM_ITConfig() vor der Initialisierung der entsprechenden Clock kann interessante Nebeneffekte haben - nur nicht die die man erwartet.
Hier könnten einige Dinge schiefgehn. Interrupt-Routinen werden bei Keil üblicherweise mit dem Qualifier __irq versehen - und es ist nirgendwo ersichtlich, wo der Vektor für den Interrupt-Handler eingetragen wird. Aber es könnte erstmal ein viel schwerwiegenderes Problem vorliegen - laut dem Datenblatt (http://www.st.com/resource/en/datasheet/stm32f100v8.pdf) besitzen die STM32F100Rx den TIM4-Timer nicht (S. 11, siehe Screenshot), den du verwendest. Hast du mal im Debugger geschaut, ob der Code in der Endlos-while-Schleife landet oder nicht doch im HardFault-Handler? Vielleicht hilft das ja schonmal ein wenig weiter... -- Michael
Ah, genau lesen hilft. Wenn das Board das STM32VL-Discovery ist (die Angabe fehlt leider), hat es einen STM32F100RBT6B mit 128 KB Flash und 8 KB RAM. Der hat dann laut Datenblatt den Timer TIM4. Warum auch immer man den einspart, bei den Teilen wäre ich davon ausgegangen, dass das Silizium bei allen gleich ist. Also ist's vermutlich eine Kombination der anderen im Thread genannten Probleme... -- Michael
Daniel L. schrieb: > Meine jetzige Aufgabe ist es, mich > mit Timern, speziell dem Aufrufen von ISR's durch Timer, > auseinanderzusetzen. Na, dann tu es doch einfach! Lies die grundlegenden Papiere über die Cortexe (deiner ist m.W. ein M3) und lies das Referenzmaual zum Chip - wenigstens auszugsweise. Ebenso guck dir mal einen Startupcode an - aber keinen solchen, der in C zusammengebraten ist, sondern einen in Assembler. Aber mit so einem Code, wie du gepostet hast? Damit wird das nichts. Laß diese ST-Lib weg und befasse dich DIREKT mit der Hardware, denn genau DAS war wohl das eigentliche Anliegen des Prof's. Und nochwas zur Struktur deines mageren main's: Sowas macht man im Allgemeinen so:
1 | int main (void) |
2 | { SetzeMeineClocksAuf(); |
3 | SetzeMeinePinsAuf(); |
4 | SetzeMeinenTaktAuf(); |
5 | SetzeMeinSonstigesZeugsAuf(); |
6 | |
7 | immerzu:
|
8 | MacheHierWasVernünftiges(); |
9 | MacheHierNochwasvernünftiges(); |
10 | goto immerzu; |
11 | }
|
Und wenn du einen Horror vor dem Goto hast, dann eben das alberne while(1). In jedem Falle solltest du deine main-Quelle nicht zumüllen, sondern du solltest deine Quelle in vernünftige Teile aufteilen. Also eine separate Quelle für das Aufsetzen des Chips (Clocks, also Taktversorgung der gewünschten Peripherie-Cores), Pins (also welche Pinfunktionen), Takt (also PLL, Oszillator(en) und Frequenzen) Ebenso eine separate Quelle für die Systemuhr (sowas braucht man eigentlich fast immer), dann eine für eventuelle UART's, dann eine für Kommandoauswertungen (sofern kommuniziert werden soll), eine für diverse I/O-Konvertierungen und so weiter. Also, bringe mal ne Systematik rein. Das hilft ungemein. W.S.
Michael Engel schrieb: > und es ist nirgendwo > ersichtlich, wo der Vektor für den Interrupt-Handler eingetragen wird. Der steht im Startupcode - und zwar an genau DER Stelle, die der chipabhängigen Interruptnummer entspricht. W.S.
H.Joachim S. schrieb: > NVIC_SetVector(..); > fehlt auch noch. Bei den Cortex-M µC normalerweise unnötig, da die eine feste Vektortabelle im Flash haben. Dafür müssen die Handler Funktionen einen korrekten Namen inklusive Groß-u. Kleinschreibung haben. Dekorationen wie __irq sind AFAIK nicht erforderlich, da im ABI die Interrupt Handler wie normale C Funktionen aussehen.
Um einen Interrupt eines Timers nutzen zu können musst du das entsprechende Bit im CR des Timers setzen, z.B. so:
1 | /* enable update interrupt */
|
2 | TIM4->DIER |= TIM_DIER_UIE; |
Die ganze Interruptinitialisierung kannst du ebenfalls der Einfachheit halber auf eine einzige Funktion herunterbrechen, die dann für alle Cortex-M herstellerübergreifend immer gleich lautet, z.B.:
1 | /* enable interrupt for timer 4*/
|
2 | NVIC_EnableIRQ(TIM4_IRQn); |
Damit läuft der Interrupt mit Standardpriorität, was gleichzeitig die höchste Priorität darstellt. Legst du keine Prioritäten fest ist alles so simpel wie beim AVR. In deinem Startup-Code findest du den passenden Namen für deine Interrupt-Routine, wobei TIM4_IRQHandler eigentlich passen sollte. Außerdem musst du im RCC nicht nur den Takt des Timers einschalten sondern auch noch den Takt des GPIO und du musst diesen noch als Ausgang konfigurieren bevor da was blinken kann. Hier hast du ein Bare-Metal-Beispiel für einen STM32F103C8 mit LED an PC13: https://github.com/ChristianRinn/bare_metal_stm32f103c8/blob/master/src/example02_gpio-timer-interrupt/main.c Sieh das als Leitfaden, wo du siehst was du grundsätzlich brauchst um einen Pin per Timer zu togglen. Ich empfehle dir grundsätzlich dir das passende Reference Manual zu deinem Chip zu schnappen und das von Null auf selber zu schreiben. Nur so lernst die Register kennen und verstehst wie der Timer funktioniert, was der alles kann, usw.
Ich benutze die System Workbench for STM32. Wenn ich damit ein Projekt vom Typ "No Firmware" (also ohne die alte SPL und ohne HAL) beginne, erzeugt mir die IDE eine unvollständig befüllte Interrupt-Vektor Tabelle. Sie enthält nur eine Hand voll Einträge, die man allerdings leicht "manuell" erhänzen kann. Du hast zwar eine andere IDE, aber möglicherweise das selbe Fehlerursache.
Stefan U. schrieb: > Du hast zwar eine andere IDE, aber möglicherweise das selbe > Fehlerursache. Auch wenn ich es abgrundtief Verabscheue wenn irgendeine IDE im Hintergrund ohne mein Wissen oder Zutun irgendetwas macht, ist das Problem in diesem Fall in erster Linie das weder ein Takt am GPIO anliegt, noch der Pin als Ausgang konfiguriert ist und wenn man das DIER des Timers nicht richtig konfiguriert wird er auch niemals einen Interrupt auslösen.
Vielen Dank an jeden hier, nach dem Implementieren von den Vorschlägen hier funktioniert es einwandfrei.
1 | void TIM4_IRQHandler(void){ |
2 | TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //Säubern des EventFlags |
3 | if(GPIO_PinRead(GPIOC,8)){ //Wenn Nulldurchlauf und Pin-Nr 8 gesetzt |
4 | GPIO_PinWrite(GPIOC,8,0); //Rücksetzen des Pins (LED rechts unten, Blau?) |
5 | }
|
6 | else{ //Wenn Nulldurchlauf und Pin-Nr 8 nicht gesetzt |
7 | GPIO_PinWrite(GPIOC,8,1); //Setzen des Pins |
8 | }
|
9 | }
|
10 | |
11 | void Timer_Init(void){ |
12 | TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); |
13 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); |
14 | TIM4->ARR=0x9895;//Hexadezimal für 39061=40MHz/1024 |
15 | TIM4->PSC=0x0400; //Hexadezimal für 1024 |
16 | TIM4->SR = 0; |
17 | TIM4->DIER |= TIM_DIER_UIE; |
18 | NVIC_EnableIRQ(TIM4_IRQn); |
19 | TIM4->CR1 |= TIM_CR1_CEN;//T4 -> Run |
20 | }
|
21 | |
22 | void GPIO_Init(void){ |
23 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); |
24 | GPIO_PortClock(GPIOC,1); |
25 | GPIOC->BSRR = (1UL << 8); // set |
26 | GPIO_PinConfigure(GPIOC, 8, GPIO_OUT_PUSH_PULL, GPIO_MODE_OUT10MHZ); //Config des Pins Nr8, LED rechts unten |
27 | }
|
28 | |
29 | int main (void){ |
30 | |
31 | Timer_Init(); |
32 | |
33 | GPIO_Init(); |
34 | |
35 | while(1); |
36 | }
|
Der wahrscheinlich ausschlaggebene Punkt war das Einfügen von
1 | TIM4->DIER |= TIM_DIER_UIE; |
2 | NVIC_EnableIRQ(TIM4_IRQn); |
,also dem richtigen Initialisieren des Interrupts. Vielen Dank
Jim M. schrieb: > Dekorationen wie __irq sind AFAIK nicht > erforderlich, da.. .. ja? Was? Mag sein, daß der GCC da keine Unterschiede macht, aber mein dringendster Rat in dieser Sache ist: Sei nicht so faul und schreib das __irq einfach dazu. Das wird schon deine Finger nicht gar zu sehr abnützen. W.S.
Gerade solche Sachen lassen sich wunderbar am Keil-Simulator nachvollziehen. man sieht wie der Zähler eingestellt ist, wie er zählt, welche Flags gesetzt werden und, und... Einfach mal ausprobieren. Grüsse
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.