Hallo Ich hantiere nun schon ein paar Mal damit herum, aber irgendwie schaffe ich es nicht, dass meine Interrupts richtig funktionieren. Ich habe vor die Digits per Multiplex anzusteuern. Dazu habe ich mir überlegt. Jede 500us wird der Systick Interrupt aufgerufen und der digitCounter inkrementiert. Die Periode kann ich ja noch anpassen. Und jede Sekunde wird der Interrupt Timer 6 aufgerufen, der den digitValue dekrementiert. Ich habe 5 Digits und der digitValue startet bei 99999. Die Daten werden bei jedem Aufruf der Systick Interrupts gesendet und die Digits geswitched. Jetzt habe ich 2 Probleme: 1. Mein 1s-Takt Interrupt kommt irgendwie öfters als der Systick Interrupt. Eigentlich sollte es ja genau umgekehrt sein. Im Debugger zählt der digitValue nämlich schneller ab als der andere digitCounter hoch. 2. Habe ich das Problem, dass immer im Anschluss an den Systick Interrupt den Counter hochzählen und gleich die die Daten senden und die Digits switchen muss. Das heisst also, dass ich die Funktion switchDigit(digitCounter, digitValue, digitCode); in der der Switch-Case enthalten ist, ausführen muss. Aber für den Interrupt ist diese Funktionsausführung zeitlich sicher zu lange. Wie kann ich das irgendwie besser machen? Mir fällt hierzu leider nichts Besseres ein. Ich bedanke mich jetzt schon für eure Hilfestellung. PS: Init Timer 6 ist im tim6.c, für den Systick im delay.c. Die Funktion die die Daten sendet und digits switchet im digitcommands und in definitions sind die Definitionen. Im it.c stecken die Interrupts.
Es handelt sich hier um den STM32F051R8T6 und KeilV5. Sorry, das hatte ich noch vergessen.
Ohne jetzt den Code angesehen zu haben... Die Inebtriebnahme von Interrups ist nicht trivial. Ich mach das ueblicherweise so. 1. Interruptprozedur deklarieren, und das Trivialste machen lassen, bedeutet 1. die Quelle befriedigen, zB Zuruecksetzen 2. einen Pin toggeln 3. interrupt verlassen 2. Interrupt vor dem loop() initialisieren 3. Interrupt vor der loop() enablen 4. main() schreiben, etwas wie main() { init(); while (1) { togglepin(); delay() } } 4. alle interrupt enablen mit dem ozilloskop die beiden toggel pins nachmessen.
:
Bearbeitet durch User
Sebastian Kaulitz schrieb: > Aber für den Interrupt ist diese Funktionsausführung zeitlich sicher zu > lange. Es gibt auch keinerlei Grund, im Interrupt mehr als nötig zu machen. Sobald ein neuer Wert vorliegt, berechnet das Main die neuen Digits und legt sie in einem Ausgabearray ab. Möglichst nicht schneller als 2..5/s, damit es ergonomisch ist (nicht flackert). Der MUX-Interupt nimmt dann nur das Digit aus dem Array und schaltet ein Digit weiter. Damit kein Ghosting auftritt, schaltet er erst alle Digittreiber aus, gibt das neue Digit aus und schaltet den dazu gehörenden Digittreiber ein.
Im Moment laufen bei mir alle Interrupts durchgehend. Das ist vielleicht in diesem Fall doch nicht ganz geschickt. Vielen Dank also für die Hinweise. Ich werde das mal versuchen so zu implementieren wie ihr es vorschlägt. Peter D. schrieb: > Sebastian Kaulitz schrieb: > Sobald ein neuer Wert vorliegt, berechnet das Main die neuen Digits und > legt sie in einem Ausgabearray ab. Möglichst nicht schneller als 2..5/s, > damit es ergonomisch ist (nicht flackert). Könntest du mir kurz noch erklären, was du mit 2...5/s meinst? 2..5mal pro Sekunde? Ich habe leider noch nicht verstanden, wann ich die Berechnung im Main durchführen soll. Die Berechnung sollte ja nur durchgeführt werden, wenn sich auch der Wert geändert hat, sprich der TIM6 Interrupt aktiv war und der Counter um 1 dekrementiert wurde. Wenn die Main das aber in fixen Zeitabständen immer berechnen soll, heisst das ja, dass ich ein Delay einbauen muss, was ja schlecht ist. Oder meinst du eher, dass ich in der while eine if-Anweisung einbauen und überprüfen muss ob sich das digitValue geändert hat?
Sebastian Kaulitz schrieb: > Könntest du mir kurz noch erklären, was du mit 2...5/s meinst? 2..5 Werte/s kann der Mensch bequem ablesen. Daher haben auch viele Meßgeräte diese Wandlerrate. Man sollte also nicht schneller neue Werte anzeigen.
Hallo zusammen Ich habe den Code nun fertig gebracht und funktioniert auch recht gut. Im Systick Handler behandle ich das Multiplexen und schalte alle 1ms weiter. Nun würde ich das gerne noch weiter auflösen können. Wenn ich bei der SystemClock_Config nun den SysTickDevider von 1000 auf 1000000 ändere, sprich 1us, dann funktionert mein Programm nicht mehr.. Das mit den Teilern ist von ST selbst irgendwie definiert. Warum funktioniert mit dem Wert 1000000 das Programm nicht mehr? Ist die Anweisung im Interrupt dann zu lange und der nächste steht bereits an? Mir geht es dabei nur um das Verständnis, warum das Eine funktioniert und das Andere nicht.
1 | void SysTick_Handler(void) |
2 | {
|
3 | TIM6->DIER &= ~TIM_DIER_UIE; |
4 | |
5 | /* USER CODE BEGIN SysTick_IRQn 0 */
|
6 | if(counter >= SYSTICK_MULTIPLICATOR) |
7 | {
|
8 | counter=0; |
9 | digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS); // increments first digitCounter and calculates modulo |
10 | // e.g digitCounter = 4, then digitCounter will be 0
|
11 | switchDigit(digitCounter); |
12 | |
13 | }
|
14 | /* USER CODE END SysTick_IRQn 0 */
|
15 | HAL_IncTick(); |
16 | HAL_SYSTICK_IRQHandler(); |
17 | /* USER CODE BEGIN SysTick_IRQn 1 */
|
18 | TIM6->DIER |= TIM_DIER_UIE; |
19 | counter++; |
20 | /* USER CODE END SysTick_IRQn 1 */
|
21 | }
|
1 | void SystemClock_Config(uint32_t sysTickDevider) |
2 | {
|
3 | |
4 | RCC_OscInitTypeDef RCC_OscInitStruct; |
5 | RCC_ClkInitTypeDef RCC_ClkInitStruct; |
6 | |
7 | __HAL_RCC_SYSCFG_CLK_ENABLE(); |
8 | |
9 | /**Initializes the CPU, AHB and APB busses clocks
|
10 | */
|
11 | RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14; |
12 | RCC_OscInitStruct.HSIState = RCC_HSI_ON; |
13 | RCC_OscInitStruct.HSI14State = RCC_HSI14_ON; |
14 | RCC_OscInitStruct.HSICalibrationValue = 16; |
15 | RCC_OscInitStruct.HSI14CalibrationValue = 16; |
16 | RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; |
17 | RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; |
18 | RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12; |
19 | RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1; |
20 | if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) |
21 | {
|
22 | _Error_Handler(__FILE__, __LINE__); |
23 | }
|
24 | |
25 | /**Initializes the CPU, AHB and APB busses clocks
|
26 | */
|
27 | RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |
28 | |RCC_CLOCKTYPE_PCLK1; |
29 | RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; |
30 | RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; |
31 | RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // no devider for Systick clock |
32 | |
33 | if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) |
34 | {
|
35 | _Error_Handler(__FILE__, __LINE__); |
36 | }
|
37 | |
38 | /**Configure the Systick interrupt time
|
39 | */
|
40 | HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / sysTickDevider); |
41 | |
42 | /**Configure the Systick
|
43 | */
|
44 | HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); |
45 | |
46 | /* System interrupt init*/
|
47 | /* SVC_IRQn interrupt configuration */
|
48 | HAL_NVIC_SetPriority(SVC_IRQn, 0, 0); |
49 | /* PendSV_IRQn interrupt configuration */
|
50 | HAL_NVIC_SetPriority(PendSV_IRQn, 0, 0); |
51 | /* SysTick_IRQn interrupt configuration */
|
52 | HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); |
53 | }
|
1 | void switchDigit(uint8_t digitCounter) |
2 | {
|
3 | |
4 | switch(digitCounter) |
5 | {
|
6 | case 0: |
7 | HAL_GPIO_WritePin(FLAMELED.port, FLAMELED.pin, FLAME_LEDS_OFF); |
8 | HAL_GPIO_WritePin(DIGIT1.port, DIGIT1.pin, DIGIT_ON); |
9 | break; |
10 | |
11 | case 1: |
12 | HAL_GPIO_WritePin(DIGIT1.port, DIGIT1.pin, DIGIT_OFF); |
13 | HAL_GPIO_WritePin(DIGIT2.port, DIGIT2.pin, DIGIT_ON); |
14 | break; |
15 | |
16 | case 2: |
17 | HAL_GPIO_WritePin(DIGIT2.port, DIGIT2.pin, DIGIT_OFF); |
18 | HAL_GPIO_WritePin(DIGIT3.port, DIGIT3.pin, DIGIT_ON); |
19 | break; |
20 | |
21 | case 3: |
22 | HAL_GPIO_WritePin(DIGIT3.port, DIGIT3.pin, DIGIT_OFF); |
23 | HAL_GPIO_WritePin(DIGIT4.port, DIGIT4.pin, DIGIT_ON); |
24 | break; |
25 | |
26 | case 4: |
27 | HAL_GPIO_WritePin(DIGIT4.port, DIGIT4.pin, DIGIT_OFF); |
28 | HAL_GPIO_WritePin(FLAMELED.port, FLAMELED.pin, FLAME_LEDS_ON); // here as DIGIT 5 in Testing phase |
29 | break; |
30 | }
|
31 | |
32 | }
|
Sebastian Kaulitz schrieb: > Ist die > Anweisung im Interrupt dann zu lange und der nächste steht bereits an? Sehr warscheinlich. Schau mal ins Assemblerlisting, wieviel Code erzeugt wird. Z.B.: Sebastian Kaulitz schrieb: > digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS); Hat Dein MC eine Hardwaredivision? Sparsamer ist ein Vergleich mit dem Endwert und auf 0 setzen. Sebastian Kaulitz schrieb: > digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS); Dieser Code ist außerdem unbestimmt. Er enthält 2 Zuweisungen auf die selbe Variable ohne Sequence Point. Stell mal den Warnlevel des Compilers so ein, daß er bei sowas warnt.
:
Bearbeitet durch User
Hier mal ein einfaches MUX-Beispiel für den AVR:
1 | #include <avr/io.h> |
2 | |
3 | #define DIGITPORT PORTB
|
4 | #define SEGMENTPORT PORTC
|
5 | #define DIGITNUM 8 // 2 .. 8
|
6 | |
7 | uint8_t muxram[DIGITNUM]; |
8 | |
9 | uint8_t *muxptr = muxram; |
10 | |
11 | void muxer( void ) |
12 | {
|
13 | SEGMENTPORT = 0xFF; // segments off (low active) |
14 | DIGITPORT >>= 1; // next digit on |
15 | if( DIGITPORT == 0 ){ |
16 | DIGITPORT = 1<<DIGITNUM-1; |
17 | muxptr = muxram; |
18 | }
|
19 | SEGMENTPORT = *muxptr++; // next segment pattern on |
20 | }
|
Peter D. schrieb: > Hier mal ein einfaches MUX-Beispiel für den AVR:#include > <avr/io.h> > > #define DIGITPORT PORTB > #define SEGMENTPORT PORTC > #define DIGITNUM 8 // 2 .. 8 > > uint8_t muxram[DIGITNUM]; > > uint8_t *muxptr = muxram; > > void muxer( void ) > { > SEGMENTPORT = 0xFF; // segments off (low active) > DIGITPORT >>= 1; // next digit on > if( DIGITPORT == 0 ){ > DIGITPORT = 1<<DIGITNUM-1; > muxptr = muxram; > } > SEGMENTPORT = *muxptr++; // next segment pattern on > } Ich muss denmach also noch ein wenig optimieren. Danke, das werde ich gleich mal umsetzen. > Sebastian Kaulitz schrieb: >> digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS); > > Dieser Code ist außerdem unbestimmt. Er enthält 2 Zuweisungen auf die > selbe Variable ohne Sequence Point. Stell mal den Warnlevel des > Compilers so ein, daß er bei sowas warnt. Ich verstehe nicht ganz was du meinst hiermit. Könntest du genauer erklären, was mein Fehler ist? Wieso 2 Zuweisungen?
Sebastian Kaulitz schrieb: > Wieso 2 Zuweisungen eine mit "digitCounter=" und eine mit "++digitCounter". Schreib halt
1 | digitCounter= ((digitCounter + 1) % NUMBEROFMUXMEMBERS); |
Dann ist's nur noch eine Zuweisung. oder z.B.
1 | if (++digitCounter==NUMBEROFMUXMEMBERS) |
2 | digitcounter=0; |
Εrnst B. schrieb: > Sebastian Kaulitz schrieb: >> Wieso 2 Zuweisungen > > eine mit "digitCounter=" und eine mit "++digitCounter". > > Schreib halt > digitCounter= ((digitCounter + 1) % NUMBEROFMUXMEMBERS); > > Dann ist's nur noch eine Zuweisung. > > oder z.B. if (++digitCounter==NUMBEROFMUXMEMBERS) > digitcounter=0; Danke Und warum genau darf man so etwas in C nicht machen?
Sebastian Kaulitz schrieb: > Und warum genau darf man so etwas in C nicht machen? Weil der C-Compiler die Freiheit hat, die Zuweisungen bis zum nächsten Sequence Point so umzusortieren, wie es ihm passt. Das muss nicht von links-nach-rechts oder von oben-nach-unten sein. d.H. der Compiler darf in der Theorie aus deiner Zeile Maschinencode erzeugen, bei der Werte > NUMBEROFMUXMEMBERS in digitcounter stehen. Ich denke zwar nicht, dass es einen Compiler gibt, der das machen würde, aber trotzdem ist es schlechter Stil sich da auf Implementierungsdetails zu verlassen anstatt gleich laut Standard eindeutig definierten und damit richtigen Code zu schreiben. Der GCC warnt, wenn mit "-Wall" oder "-Wsequence-point" aktiviert, vor solchen Konstrukten.
Εrnst B. schrieb: > Sebastian Kaulitz schrieb: > Das muss nicht von links-nach-rechts oder von oben-nach-unten sein. Entschuldige, dass ich so frage, aber irgendwie kann ich mir das nicht vorstellen. Der Compiler kann ja den Code bis zum sequence point ; nicht so umsortieren, dass da zB steht: digitCounter = (NUMBEROFMUXMEMBERS % (++digitCounter)); Wie digitCounter auf einmal grösser werden kann, kann ich mir auch irgendwie nicht vorstellen, wenn da steht digitCounter = ((++digitCounter)%NUMBEROFMUXMEMBERS); digitCounter mit 0 initialisiert NUMBEROFMUXMEMBERS = 5 Im Systick Handler beim 1. Mal wird er 1 zählt hinauf bis 4 beim letzten Mal wird ++digitCounter = 5 und 5%NUMBEROFMUXMEMBERS ergibt 0 Wie also kann da jemals der Wert > 5 werden? Vielen Dank für die Erklärung
Sebastian Kaulitz schrieb: > Entschuldige, dass ich so frage, aber irgendwie kann ich mir das nicht > vorstellen. Der Prozessor kann kein C. Daraus wird also ungefähr sowas: >> digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS); (1) lade register A mit digitalCounter (2) addiere 1 zum register A (3) lade register B mit NUMBEROFMUXMEMBERS (4) dividiere register A mit register B, schreibe ergebnisse nach register C (wert) und D (rest) (5) schreibe register A mit register D (6) schreibe register A nach digitalCounter (Ganz so blöd sind moderne Compiler natürlich nicht.) Es ist nicht definiert, in welcher Reihenfolge (2) und (4) passieren, weil "zwei Schreibzugriffe auf die gleiche Variable ohne Sequenzpunkt" laut Standard ungültiges Verhalten ist. Theoretisch darf der Compiler an der Stelle auch einen Absturz produzieren, deinen Controller auf Koreanisch umstellen, deinen Schreibtisch grün malen oder den gesamten Code wegschmeißen. Oder "-1" (alle Bits gesetzt) in digitCounter schreiben.
S. R. schrieb: >>> digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS); > > (1) lade register A mit digitalCounter > (2) addiere 1 zum register A > (3) lade register B mit NUMBEROFMUXMEMBERS > (4) dividiere register A mit register B, schreibe ergebnisse nach > register C (wert) und D (rest) > (5) schreibe register A mit register D > (6) schreibe register A nach digitalCounter > > (Ganz so blöd sind moderne Compiler natürlich nicht.) > > Es ist nicht definiert, in welcher Reihenfolge (2) und (4) passieren, > weil "zwei Schreibzugriffe auf die gleiche Variable ohne Sequenzpunkt" > laut Standard ungültiges Verhalten ist. Danke
Sebastian Kaulitz schrieb: > Ich muss denmach also noch ein wenig optimieren. Ich versuche, möglichst nicht einfach drauflos zu programmieren, sondern erstmal zu überlegen, worin sich die einzelnen Aktionen unterscheiden. Und beim Multiplexen bietet sich das Schieben für die Digitauswahl geradezu an, wenn die Digittreiber auf einem Port liegen. If/else-Ketten oder switch/case nehme ich nur, wenn sich keine gemeinsame Regel finden läßt. Eine gemeinsame Regel hat auch den Vorteil, daß Fehler nur an einer Stelle auftreten und nicht in vielen Copy&Paste Sequenzen. D.h. Fehler müssen nur an einer Stelle behoben werden. Auch kann sie einfach erweitert werden, ohne daß man Copy&Paste Fehler hinzufügt.
Ich habe die Anweisungen mal im Systick Handler gemessen. Mit dem Oszilloskop messe ich ca. 68us. Damit ist klar, warum die Division des Systick Interrupts mit dem Teiler 1.000.000 für ein 1us Intervall nicht funktioniert. Danke euch allen für die Unterstützung.
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.