This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430 newbie on clocks and interrupts

Other Parts Discussed in Thread: MSP430FR5739

First of all, I'm new to MSP430 but have experiences with several other µC.

I have a MSP430FR5739 and tried to experience with some interrupts etc. Because I didn't notice any occurance of timer interrupts, I tried to route the clock signals to pins PJ.0, PJ.1 and PJ.2.

int main(void) {
	// Watchdog Timer anhalten
	WDT_A_hold(WDT_A_BASE);			// Stop watchdog timer

	initTimings();

	while (1) {
		// LPM0 mit Interrupts
		__bis_SR_register(LPM0_bits | GIE);
		__no_operation();
	}
}


/*
 * Initialisierung von Clocks und Timern
 */
void initTimings() {
    // DCO-Frequenz auf 8 MHz
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_3);

    //  MCLK: nutzt DCOCLK ( 8 MHz, Teiler 1)
    // SMCLK: nutzt DCOCLK ( 1 MHz, Teiler 8)
    //  ACLK = SMCLK
    CS_clockSignalInit(CS_ACLK,  CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_8);
    CS_clockSignalInit(CS_MCLK,  CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1);
    CS_clockSignalInit(CS_SMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_8);

    GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_PJ, GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN2, GPIO_PRIMARY_MODULE_FUNCTION);
}

On the oscilloscope the clock signals appeared fine on the corresponding pins. But what happened after a minute or so? They disappeared! Why???

Then I tried to toggle an LED by a Timer A interrupt.

int main(void) {
//	uint8_t cmd;

	// Watchdog Timer anhalten
	WDT_A_hold(WDT_A_BASE);			// Stop watchdog timer

	// Initialisierungen durchführen
	initTimings();

	GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN2);
	GPIO_setOutputHighOnPin(GPIO_PORT_P2, GPIO_PIN2);

	// Endlos-Schleife
	while (1) {
		// LPM0 mit Interrupts
		__bis_SR_register(LPM0_bits | GIE);
		__no_operation();
	}
}


/*
 * Initialisierung von Clocks und Timern
 */
void initTimings() {
    // DCO-Frequenz auf 8 MHz
    CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_3);

    //  MCLK: nutzt DCOCLK ( 8 MHz, Teiler 1)
    // SMCLK: nutzt DCOCLK ( 1 MHz, Teiler 8)
    //  ACLK = SMCLK
    CS_clockSignalInit(CS_ACLK,  CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_8);
    CS_clockSignalInit(CS_MCLK,  CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1);
    CS_clockSignalInit(CS_SMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_8);

    // Timer A1 arbeitet im Continuous Mode bei 25 kHz
    TIMER_A_clearTimerInterruptFlag(TIMER_A1_BASE);
    TIMER_A_configureContinuousMode(TIMER_A1_BASE,
                                    TIMER_A_CLOCKSOURCE_ACLK,
                                    TIMER_A_CLOCKSOURCE_DIVIDER_40,
                                    TIMER_A_TAIE_INTERRUPT_ENABLE,
                                    TIMER_A_DO_CLEAR
                                    );
    TIMER_A_enableInterrupt(TIMER_A1_BASE);
    TIMER_A_startCounter(TIMER_A1_BASE,
                         TIMER_A_CONTINUOUS_MODE
                         );
}

#define TA_CNT	25000
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=TIMER1_A1_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(TIMER1_A1_VECTOR)))
#endif
void TIMER1_A1_ISR(void)
{
	static volatile uint16_t usTimerACntr = TA_CNT;

	switch ( __even_in_range(TA1IV, 14) ) {
		case  0: break;		// No interrupt
		case  2: break;		// CCR1 not used
		case  4: break;		// CCR2 not used
		case  6: break;		// CCR3 not used
		case  8: break;		// CCR4 not used
		case 10: break;		// CCR5 not used
		case 12: break;		// CCR6 not used
		case 14:			// Overflow
			usTimerACntr--;
			if (usTimerACntr == 0) {
				swUptime++;
				usTimerACntr = TA_CNT;

				GPIO_toggleOutputOnPin(GPIO_PORT_P2, GPIO_PIN2);
			}
			break;
		default: break;
	}

	// with and without the following command
	TIMER_A_clearTimerInterruptFlag(TIMER_A1_BASE);
}
#undef TA_CNT

On startup the LED got lit and after some 25 seconds it turned off and never turned on again! I assumed and expected the following:

DCOCLK = 8 MHz, MCLK = 8 MHz, SMCLK = 1 MHz, ACLK = 1MHz

Timerinterrupt-Frequency: 1 MHz / 40 = 25 kHz, so a counter decrementing from 25000 should reach 0 every second. But it took that long long time. And no matter what value I changed in any way, the time doesn't get shorter...

Please help me! Any hint is appreciated.

Regards Helmut

  • No answer?


    Okay, after some further investigations and readings I found out that I have to use an UpMode-Counter instead of a ContinuousMode-Counter to get the required timing results of the interrupts. So my LED toggles once per second now.


    But the more important question remains: what happens with the clocks? Right after hand-stopped 55 seconds LED-toggling stops even when in active mode I think:

    I replaced the last four lines

    while (1) {
    	// LPM0 mit Interrupts
    	__bis_SR_register(LPM0_bits | GIE);
    	__no_operation();
    }
    

    by

    for ( ; 1 != 2; ) { cmd = 1 - cmd; }
    

    with

    unsigned char cmd = 1;

    Btw. I'm using Code Composer Studio v6.

    Another "interesting" thing is that the clocks do not seem to disappear when application is run inside the debugger.

    I'm looking for your (helpful) responses...

    Kind regards
    Helmut

  • In your first program, you made a conceptual mistake. You configured the timer to run with 25kHz. Which means that it increments with 25kHz. Then you used the overflow interrupt, to trigger an interrupt. This isn't wrong. However, the overflow appeared every 65536 ticks, thus every 2.6 seconds. Then you counted to 25000. which means, your action took place every 65536 seconds = 18.2hrs, not every second.

    I don't know what changes you did to switch to up mode. Up mode requires CCR0 to be set properly.

    If you use up mode and set CCR0 to 25000, then the interrupt will already happen every second, no need to count for 25000 interrupts.

    It's possible that you have a racing condition in the changed code, that causes a lock-up.

    Btw: it makes not much sense declaring a local variable as volatile (whether static or not). Especially not if it is local to an ISR.
    Volatile tells the compiler that the content may change unexpectedly (outside the program flow) which can't happen unless you pass a reference to it to outside the ISR and also let the ISR being interrupted by another ISR that changes this variable through the reference. Definitely not an often used situation :)

    Besides this, well, I personally believe that programming a timer by using these function calls is like shooting with cannons on sparrows. Yes, it may be modular, but nobody knows what the code actually does (unless you post it), so it adds another layer of obfuscation and possible bugs.

  • Hello Jens-Michael,


    thank you for the detailled explanation of my conceptual error. What you wrote was exactly the same what I found out.
    And yes, after switching to up mode and setting up the CCR0 value properly I got what I wanted...

    Belonging to that volatile thing, I was desperate and so I even tried really senseless things too :-(

    But one question to your notice "using these function calls is like shooting with cannons on sparrows": Do you refer to the TIMER_...() functions? They are part of TIs MSP430-API and not of my own. So I would expect a correct implementation and for a newbie their usage is much more comfortable than juggling directly with register flags.

    #include <msp430.h>
    /*
    #include <driverlib.h>
    #include <stdio.h>
    #include <string.h>
    */
    
    // #define SWITCH_ON_INIT
    
    
    #define SUPPORT_LV_INT			0x01
    #define SUPPORT_COMPARATOR_D	0x02
    #define SUPPORT_FEEDBACK		0x80
    
    
    #define MAX_MSEC	60000
    
    
    /*
     * Die Liste der definierten Statuscodes
     */
    enum {
    	ST_OK = 0,
    	ST_INFO_COUNTER_FIXED = 0,
    	ST_INFO_UNUSED_BITS_NONZERO,
    	ST_WARN_DOUBLE_LOW_VOLTAGE_DETECTION,
    	ST_ERR_COUNTER_CORRUPT,
    	ST_ERR_INVALID_POSITION_NUMBER,
    };
    #define MK_STATUS(x) (1 << (x))
    
    
    // ============================================================================
    // === Beginn der Werte im FRAM
    // ============================================================================
    
    #pragma SET_DATA_SECTION(".myFramVars")
    
    /*
     * Die Konstante 'ucNumPositions' enthält verfügbare Anzahl von Schalterstellungen.
     * Default: 4         Offset 80, Länge 1
     */
    const unsigned char ucNumPositions = 4;
    
    /*
     * Die Konstante 'ucFeatureSet' zeigt an, welche Möglichkeiten das Relais bietet.
     *  - SUPPORT_FEEDBACK
     *  	das Relais kann die aktuelle Schalterstellung an eigenen Pins signalisieren
     *  - SUPPORT_COMPARATOR_D
     *  	am Eingang CD13 des Comparators wird erkannt, ob die Versorgungsspannung
     *  	in Ordnung ist oder unter einen zulässigen Wert abgesunken ist
     *  - SUPPORT_LV_INT
     *  	am Port 4.1 signalisiert ein externer Comparator, dass die Versorgungsspannung
     *  	zu niedrig ist
     */
    // const unsigned char ucFeatureSet = SUPPORT_FEEDBACK | SUPPORT_COMPARATOR_D;
    const unsigned char ucFeatureSet = SUPPORT_FEEDBACK | SUPPORT_LV_INT;
    // const unsigned char ucFeatureSet = SUPPORT_FEEDBACK | SUPPORT_COMPARATOR_D | SUPPORT_LV_INT;
    
    // Offset 82, Länge 1
    const unsigned char ucDummy3 = 0;
    // Offset 83, Länge 1
    const unsigned char ucDummy4 = 0;
    
    /*
     * Die Konstante 'ulMaxCounter' enthält die maximal zulässige Anzahl von Schaltzyklen.
     * Default: 5.000.000 Offset 84, Länge 4
     */
    const unsigned long ulMaxCounter = 5000000;
    
    /*
     * Die Variablen 'swCount1' bis 'swCount4' enthalten die aktuelle Anzahl aller (bisherigen) Schaltzyklen.
     * Der Wert ist direkt im FRAM abgelegt.
     * <p>Die Werte werden bei jedem erfolgreichen Schaltzyklus inkrementiert und sollten im Idealfall stets
     * gleich sein. Durch hartes Ausschalten bzw. Stromausfall könnte es sein, dass es Unterschiede bei den
     * Zählerständen gibt. Diese dürfen jedoch nicht größer als eins sein.
     */
    volatile unsigned long swCount1;
    volatile unsigned long swCount2;
    volatile unsigned long swCount3;
    volatile unsigned long swCount4;
    
    /*
     * Die Variable 'swPosition' enthält die aktuelle Schalterstellung.
     */
    volatile unsigned char swPosition;
    
    /*
     * Die Variable 'swUptime' enthält die gesamte Betriebsdauer in Sekunden.
     */
    volatile unsigned long swUptime;
    
    #pragma SET_DATA_SECTION()
    
    // ============================================================================
    // === Ende des FRAM-Bereichs
    // ============================================================================
    
    
    
    /*
     * Diese Variable enthält den Systemstatus beim Einschalten.
     */
    unsigned long ulStatus = 0;
    
    unsigned char ucOutputPins = 0;
    unsigned char ucFeedbackPins = 0;
    
    // ============================================================================
    // === "echte" Konstanten
    // ============================================================================
    
    /*
     * Arrays für das Positions-Mapping je nach Switch-Kapazitäten
     */
    const unsigned char SP2T[7] = { 0, 1, 2, 0, 0, 0, 0 };
    const unsigned char SP4T[7] = { 0, 2, 3, 5, 6, 0, 0 };
    const unsigned char SP6T[7] = { 0, 1, 2, 3, 4, 5, 6 };
    
    const unsigned char ucPositionMapper[7] = { 0, 0, 0, 0, 0, 0, 0 };
    
    
    // ============================================================================
    // === Forward-Deklarationen der Unter-Routinen
    // ============================================================================
    
    
    void	initBasics();
    void	initTimings();
    void	initGPIO();
    
    unsigned char doSwitch(unsigned char ucContact, unsigned char ucForce);
    unsigned char doHwSwitch(unsigned char ucContact);
    unsigned char checkSuccess(unsigned char ucPinMask);
    
    
    
    // ============================================================================
    // === Beginn aller Funktions-Definitionen
    // ============================================================================
    
    /*
     * Main-Routine
     */
    int main(void) {
    	unsigned char cmd = 0, rc = 1;
    	int i;
    
    	// Watchdog Timer anhalten
    	WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
    	// WDT_A_hold(WDT_A_BASE);			// Stop watchdog timer
    
    	// Initialisierungen durchführen
    	initBasics();
    	initGPIO();
    	initTimings();
    
    	P2DIR |= BIT2;
    	P2OUT &= ~BIT2;
    	// GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN2);
    	// GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN2);
    
    	// Endlos-Schleife
    	while (rc != 0) {
    		for (cmd = ucNumPositions; cmd > 0; cmd--) {
    			rc = doSwitch(cmd, 0);
    			if (rc == 0)
    				break;
    
    			__delay_cycles(480000);
    
    //			sSleep(2);
    		}
    	}
    
    	// signalisiere Error
    	for (i = 0; i < 3; i++) {
    		for (cmd = 1; cmd <= ucNumPositions; cmd++) {
    			rc = doSwitch(cmd, 0);
    			__delay_cycles(230000);
    		}
    	}
    	// LPM0 mit Interrupts
    	__bis_SR_register(LPM0_bits | GIE);
    
    }
    
    
    /*
     * Initialisierung von Clocks und Timern
     */
    void initTimings() {
    
    	CSCTL0_H = 0xA5;
    
    	// DCO-Frequenz auf 8 MHz
    	CSCTL1 = DCOFSEL0 + DCOFSEL1;             // Set max. DCO setting
        // CS_setDCOFreq(CS_DCORSEL_0, CS_DCOFSEL_3);
    
        //  MCLK: nutzt DCOCLK (8 MHz, Teiler 1)
    	CSCTL2 = SELA_1 + SELS_3 + SELM_3;        // set ACLK = VLO, SMCLK = MCLK = DCO
    	CSCTL3 = DIVA_0 + DIVS_3 + DIVM_3;        // set all dividers: ACLK = 1, SMCLK = MCLK = 8
        // CS_clockSignalInit(CS_MCLK,  CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_8);
    
    	CSCTL0_H = 0;
    }
    
    
    /*
     * Basis-Initialisierung
     */
    void initBasics() {
    	ulStatus = ST_OK;
    
    	// Prüfe eingestelltes FeatureSet
    	if (ucFeatureSet & ~(SUPPORT_FEEDBACK | SUPPORT_COMPARATOR_D | SUPPORT_LV_INT)) {
    		ulStatus |= MK_STATUS(ST_INFO_UNUSED_BITS_NONZERO);
    	}
    	if ((ucFeatureSet & SUPPORT_COMPARATOR_D) && (ucFeatureSet & SUPPORT_LV_INT)) {
    		ulStatus |= MK_STATUS(ST_WARN_DOUBLE_LOW_VOLTAGE_DETECTION);
    	}
    
    	// Prüfe die Stellungsanzahl und richte das passende Umschalt-Mapping ein
    	switch(ucNumPositions) {
    		case 2:
    			memcpy((void *) ucPositionMapper, SP2T, sizeof(ucPositionMapper));
    			ucOutputPins = BIT0 + BIT1;
    			break;
    		case 4:
    			memcpy((void *) ucPositionMapper, SP4T, sizeof(ucPositionMapper));
    			ucOutputPins = BIT1 + BIT2 + BIT4 + BIT5;
    			break;
    		case 6:
    			memcpy((void *) ucPositionMapper, SP6T, sizeof(ucPositionMapper));
    			ucOutputPins = BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5;
    			break;
    		default:
    			ulStatus |= MK_STATUS(ST_ERR_INVALID_POSITION_NUMBER);
    			break;
    	}
    	ucFeedbackPins = ucOutputPins << 2;
    
    	// Prüfe Zählerstände
    	if (swCount1 == swCount2) {
    		if (swCount1 == swCount3) {
    			if (swCount1 == swCount4) {
    				// alles okay!
    			}
    			else {
    				if ((swCount1 - swCount4) == 1) {
    					swCount4 = swCount1;
    					ulStatus |= MK_STATUS(ST_INFO_COUNTER_FIXED);
    				}
    				else {
    					ulStatus |= MK_STATUS(ST_ERR_COUNTER_CORRUPT);
    				}
    			}
    		}
    		else {
    			if ((swCount1 - swCount3) == 1) {
    				swCount3 = swCount4 = swCount1;
    				ulStatus |= MK_STATUS(ST_INFO_COUNTER_FIXED);
    			}
    			else {
    				ulStatus |= MK_STATUS(ST_ERR_COUNTER_CORRUPT);
    			}
    
    		}
    	}
    	else {
    		if ((swCount1 - swCount2) == 1) {
    			swCount2 = swCount3 = swCount4 = swCount1;
    			ulStatus |= MK_STATUS(ST_INFO_COUNTER_FIXED);
    		}
    		else {
    			ulStatus |= MK_STATUS(ST_ERR_COUNTER_CORRUPT);
    		}
    	}
    }
    
    
    /*
     * GPIO-Initialisierung für I/O zum Relais
     */
    void initGPIO() {
    	// Die Pins 0-5 von Port 1 treiben die Relais-Schalter.
    	P1DIR |= ucOutputPins;
    	P1OUT &= ~ucOutputPins;
    	// GPIO_setAsOutputPin(GPIO_PORT_P1, ucOutputPins);
    	// GPIO_setOutputLowOnPin(GPIO_PORT_P1, ucOutputPins);
    
    
    	// Wenn das Relais Indikatoren für die Schalterstellungen besitzt,
    	// werden die Pins 2-7 von Port 2 als digitale Eingänge geschaltet.
    	if (ucFeatureSet & SUPPORT_FEEDBACK) {
    		P2DIR &= ~ucFeedbackPins;
    		P2REN |= ucFeedbackPins;
    		P2OUT &= ~ucFeedbackPins;
    		// GPIO_setAsInputPinWithPullDownresistor(GPIO_PORT_P2, ucFeedbackPins);
    	}
    }
    
    
    /*
     * Diese Funktion schaltet das Relais in die gewünschte Stellung sofern möglich,
     * aktualisiert die Laufzeitvariablen und gibt einen entsprechenden Wert zurück.
     */
    unsigned char doSwitch(unsigned char ucContact, unsigned char ucForce) {
    	unsigned char ucRC = 0;
    
    	if ((ucContact != swPosition) || (ucForce == 1)) {
    		// Hardware umschalten
    		if (doHwSwitch(ucContact)) {
    			// Werte aktualisieren
    			swPosition = ucContact;
    			swCount1++;
    			swCount2++;
    			swCount3++;
    			swCount4++;
    
    			// Erfolg signalisieren
    			ucRC = 1;
    		}
    	}
    	else if (ucContact == swPosition) {
    		ucRC = 1;
    	}
    
    	return ucRC;
    }
    
    
    /*
     * Diese Funktion übernimmt die physikalische Ansteuerung und Umschaltung des Relais.
     *
     * 'ucContact' ist die "logische" Schalterstellung, welche intern in Abhängigkeit des
     * verwendeten physikalischen Relais in die entsprechende reale Schalterstellung
     * umgewandelt wird und dann für das Schalten und Kontrollieren weiter verwendet wird.
     */
    unsigned char doHwSwitch(unsigned char ucContact) {
    	unsigned char ucSuccess = 1;
    
    	if ((ucContact > 0) && (ucContact <= ucNumPositions)) {
    		unsigned char myContact = ucPositionMapper[ucContact];
    
    		if (myContact == 0) {
    			// ungültige Anschlußnummer angegeben!
    			ucSuccess = 0;
    		}
    		else {
    			unsigned char pinMask = 1 << (myContact - 1);
    
    			// Schalter aktivieren
    			P2OUT |= BIT2;
    			// GPIO_setOutputHighOnPin(GPIO_PORT_P2, GPIO_PIN2);
    
    			P1OUT &= ~pinMask;
    			P1OUT |= pinMask;
    			// GPIO_setOutputLowOnPin(GPIO_PORT_P1, pinMask ^ ucOutputPins);
    			// GPIO_setOutputHighOnPin(GPIO_PORT_P1, pinMask);
    
    			// kurz warten (> 15ms)
    			__delay_cycles(20000);
    			// Schalter wieder deaktivieren
    			P1OUT &= ~pinMask;
    			// GPIO_setOutputLowOnPin(GPIO_PORT_P1, pinMask);
    
    			// Erfolg prüfen
    			ucSuccess = checkSuccess(pinMask);
    
    			P2OUT &= ~BIT2;
    			// GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN2);
    		}
    	}
    	else {
    		// ungültige Anschlußnummer angegeben!
    		ucSuccess = 0;
    	}
    
    	return ucSuccess;
    }
    
    
    /*
     * Diese Funktion liest die Monitor-Pins des Relais aus (falls das Relais diese Funktion anbietet)
     * und vergleicht sie mit dem gewünschten Schaltzustand. Bei Erfolg wird eine 1 und bei Unstimmigkeit
     * eine 0 zurückgeliefert. Für Relais ohne Monitor-Pins wird stets Erfolg (1) signalisiert.
     */
    unsigned char checkSuccess(unsigned char ucPinMask) {
    	unsigned char ucSuccess = 1;
    
    	if (ucFeatureSet & SUPPORT_FEEDBACK) {
    		// Wert einlesen und überprüfen
    		unsigned char ucInValue = 0, ucFinalValue = 0;
    
    		ucInValue = P2IN;
    		ucFinalValue = ucInValue & ucFeedbackPins;
    
    		ucSuccess = (ucFinalValue == (ucPinMask << 2)) ? 1 : 0;
    		// ucSuccess = GPIO_getInputPinValue(GPIO_PORT_P2, ucPinMask << 2);
    	}
    
    	return ucSuccess;
    }
    

    I stripped the application down as shown above to eliminate any race condition due to interrupts or something else. After power-up the application stopps after 27/28 seconds somewhere in the first for(;;) loop by no further relais switching and LED toggling. The second for(;;) loop is not reached.


    Could you give me any hint?

    Regards Helmut

    PS: Your name sounds very german. So you probably can understand the comments very well.

  • Helmut Friederici said:
    But one question to your notice "using these function calls is like shooting with cannons on sparrows": Do you refer to the TIMER_...() functions? They are part of TIs MSP430-API and not of my own. So I would expect a correct implementation and for a newbie their usage is much more comfortable than juggling directly with register flags.

    I didn’t say it isn’t convenient. Shooting with cannons on sparrows is convenient too. Just not very efficient.
    Assigning a value to a register is one assembly instruction. Calling a library function to do the very same requires setting up the parameters to pass, call the function, perform the action, return to the calling function  and then considering all the CPU registers used for parameter passing as clobbered. Just to do exactly the same.

    If one directly writes to a register, using the predefined symbols, this is almost as readable and definitely smaller and faster.

    Yes, I’m German, and therefore the comments are perfectly understandable. (however, I usually write English comments in my code, as they are usually shorter, especially if no native speaker has to understand them J )

    By “for(;;)” I assume your while loop that is marked as endless loop? There is no such for in your code.

     If I interpret the code right, it is intended to go from CMD=4 to CMD=1 with a delay between. If an error occurs while doing the HW switch, it instantly repeats from CMD=4.

    Without knowing the possible limitations of the position state model (e.g. are certain transitions invalid?) I don’t see what’s going on.
    I could imagine that for some reason changing the output from 2 to 1 fails. The output remains in state 2. Now the main loop gets an error and begins with 4 again. But transiting from 2 to 4 is invalid. The output remains in state 2. And main keeps trying again from 4. This is a deadlock. I can imagine this on a stepper motor driver.
    It is also possible that after some time of operation thermal effects cause the relay to switch slower than expected, so the feedback still reads the old state, gives an error and then the whole thing enters an endless loop of doing nothing.

    Sorry, I don’t have the time to do a deeper analysis. Especially if I don’t know the exact (or at least assumed) behavior of the external hardware.
    But at least I don’t see anything in the code that could accumulate and cause a deadlock over time. So my best guess is that it has to do with the external hardware. If you remove the SUPPORT_FEEDBACK feature, does the code still lock after some time? Or does the switching continue?

  • Hello Jens-Michael,


    I know that direct register manipulation needs the smallest and fastest code to get the same results. But for the first time it is a bit difficult.

    Thank you for your help so far. The reason for the MCU to stop was not the software but a bug in our hardware. After fixing that the program did what I expected. So now I can focus on further application development again.


    Once again thank you
    Helmut

**Attention** This is a public forum