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.

Compiler/EK-TM4C123GXL: A part of the program does not run as expected. I need to add useless code in order to make it run.

Part Number: EK-TM4C123GXL

Tool/software: TI C/C++ Compiler

Hi all. I was trying to build this simple program where a user would press and hold a button interfaced with pin PD2 for as long as he wants the interval to be between a toggle of the built-in LED. PD2 is configured as WTIMER2B and the built-in LED (PF3) is toggled by TIMER0A timeout interrupt.

The button is attached to a pull-down resistor. So I am confident it is not floating.

The full program is below:

/*
 * blinky.c
 *
 *  Created on: Nov 30, 2017
 *      Author: Solomon Tan
 *
 *  Connections required:
 *      None
 *
 *  What this code does: Toggle the built-in LED using GPTM input edge timer to toggle the light for interval as long as how long the button is pressed.
 *
 */


#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"
#include "inc/hw_ints.h"
#include "driverlib/pin_map.h"

#define PART_TM4C123GH6PM 1

uint32_t timeMeasured;
_Bool isTimeMeasured = false;
_Bool test = false;

//************************************************************
// This WTIMER2B interrupt measures how long the PD2 was high.
// The measured time is stored in variable timeMeasured.
//************************************************************
void edgeInputTimerInt(void){
    TimerIntClear(WTIMER2_BASE, TIMER_CAPB_EVENT); //acknowledge the signal edge interrupt

    uint32_t pinStatus = GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_1) & 0x2;
    if(pinStatus){
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, GPIO_PIN_3);
    } else {
       //read from timer and store in var timeEnd
       timeMeasured = TimerValueGet(WTIMER2_BASE, TIMER_B);
       isTimeMeasured = true;
       GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0x0);
    }

}

//************************************************************
// This TIMER0A interrupt toggles the built-in LED upon timeout.
// Timeout depends on the value set by timeMeasured.
//************************************************************
void timer0A_timeout (void){
    TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0x8^(GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_3) & 0x8));
    TimerEnable(TIMER0_BASE, TIMER_A);
}


int main(void){

    SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN); //set the clock to 40MHz

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_3 | GPIO_PIN_1); //set PF1 and PF3 to output. (Actually only PF3 is needed as our LED)

    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0));
    TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC); //configure as a full-width periodic timer. One-shot only counts once and automatically disables the timer.
    TimerIntRegister(TIMER0_BASE, TIMER_A, timer0A_timeout);

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOD));

    SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER2);
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_WTIMER2));


    GPIOPinConfigure(GPIO_PD1_WT2CCP1); //Configure PD1 as wide GPTM
    GPIOPinTypeTimer(GPIO_PORTD_BASE, GPIO_PIN_1); //configure PD1 as echo pin - it triggers the timer module.
    GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_2); //configure PD2 as output to be Trigger.
    TimerDisable(WTIMER2_BASE, TIMER_B); //disable timer before configuration
    TimerConfigure(WTIMER2_BASE,  TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_CAP_TIME_UP); //Timer B is an input event timer.
    TimerControlEvent(WTIMER2_BASE, TIMER_B, TIMER_EVENT_BOTH_EDGES); // Configure the timer (TimerB) to time both edges.
    TimerIntClear(WTIMER2_BASE, TIMER_CAPB_EVENT); //clear any stray interrupt from the last run
    TimerIntEnable(WTIMER2_BASE, TIMER_CAPB_EVENT); //Generate interrupt upon signal edge
    TimerIntRegister(WTIMER2_BASE, TIMER_B, edgeInputTimerInt); //register echoPinTimerInt() as the ISR for WTimer 2B
    TimerEnable(WTIMER2_BASE, TIMER_B); //Enable the timer
    IntEnable(INT_WTIMER2B); //enable NVIC interrupt for WTimer 2B
    IntMasterEnable();

    while(1){
        if(isTimeMeasured){ //after the WTIMER2B interrupt successfully measures the time the PD2 pin was high for, we enable TIMER0 and disable WTIMER2B so the LED just toggles periodically. We do not change the timing anymore afterwards.
            isTimeMeasured = false;
            TimerLoadSet(TIMER0_BASE, TIMER_A, timeMeasured);
            TimerEnable(TIMER0_BASE, TIMER_A);
            TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
            IntEnable(INT_TIMER0A);
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
            TimerDisable(WTIMER2_BASE, TIMER_B);
        } else { //This block of code is useless. But my program only worked after I added in this else block. Without it, even when isTimeMeasured is set to true by the WTIMER2B interrupt, if(isTimeMeasured){...} never gets run.
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x0);
        }
    }


}

// Color    LED(s) PortF
// dark     ---    0
// red      R--    0x02
// blue     --B    0x04
// green    -G-    0x08
// yellow   RG-    0x0A
// sky blue -GB    0x0C
// white    RGB    0x0E
// pink     R-B    0x06

// sky blue -GB    0x0C
// white    RGB    0x0E
// pink     R-B    0x06

The problem i am facing is at this section of code.  

    while(1){
        if(isTimeMeasured){ //after the WTIMER2B interrupt successfully measures the time the PD2 pin was high for, we enable TIMER0 and disable WTIMER2B so the LED just toggles periodically. We do not change the timing anymore afterwards.
            isTimeMeasured = false;
            TimerLoadSet(TIMER0_BASE, TIMER_A, timeMeasured);
            TimerEnable(TIMER0_BASE, TIMER_A);
            TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
            IntEnable(INT_TIMER0A);
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
            TimerDisable(WTIMER2_BASE, TIMER_B);
        } else { //This block of code is useless. But my program only worked after I added in this else block. Without it, even when isTimeMeasured is set to true by the WTIMER2B interrupt, if(isTimeMeasured){...} never gets run.
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x0);
        }
    }

The block of code in if(isTimeMeasured){...} will not get executed i(not even once) f I do not include the else{...}. I do not understand why. My WTIMER2B interrupt runs fine. isTimeMeasured will therefore become true after the falling edge of the PD2 pin.

As long as isTimeMeasured becomes true, then if(isTimeMeasured){...} should get executed, no? I wrapped that block with while(1){...} so the program would wait for the WTIMER2B interrupt to set isTimeMeasured to true. 

Could someone, maybe someone who had the same experience, share with me why this is so? 

If the information I gave is incomplete, kindly let me know what others you need. Thank you.

  • Solomon Tan said:
    _Bool isTimeMeasured = false;

    Solomon Tan said:
    //************************************************************

    // This WTIMER2B interrupt measures how long the PD2 was high.

    // The measured time is stored in variable timeMeasured.

    //************************************************************

    void edgeInputTimerInt(void){

        TimerIntClear(WTIMER2_BASE, TIMER_CAPB_EVENT);

             //acknowledge the signal edge interrupt

        uint32_t pinStatus = GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_1) & 0x2;

        if(pinStatus){

              GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, GPIO_PIN_3);

             }

        else {

             //read from timer and store in var timeEnd

            timeMeasured = TimerValueGet(WTIMER2_BASE, TIMER_B);

            isTimeMeasured = true;

           GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0x0);

           }

    }

    Note bold

    Solomon Tan said:
    while(1){ if(isTimeMeasured){

    Solomon Tan said:
    } else {

         //This block of code is useless. But my program only worked after I added in this else block. Without it, even when isTimeMeasured is set to true by the WTIMER2B interrupt, if(isTimeMeasured){...} never gets run.    

         GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x0); }

    First w/o the else:

    The compiler is free to read isTimeMeasured once since it never changes under program control within the while loop.

    Once you add the else, you add a call to a function that the compiler does not parse*. Now the compiler can no longer assume that isTimeMeasured does not change (GPIOPinWrite could have changed it) therefore it must re-read it each time through the while.

    The usual solution is to make isTimeMeasured volatile. This works but it is important to remember what volatile promises and more importantly what it does not promise. volatile promises that each access to the variable will be performed the number of times and in the same order as written. No more or less. It does not promise access will be atomic. It does not promise non volatile accesses will not be moved or optimized.

    Robert

    * Note: this means that if the compiler can tell that GPIOPinWrite does not modify isTimeMeasured it would be free not to read it again. Note also that the compiler's view is that the interrupt is never called since it is never referenced in the while loop.

  • Thank you for the help Robert! Adding volatile solved my issue. I can't believe I overlooked it. I was tearing my hair over it just now haha.