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.

SW-EK-TM4C123GXL: TM4C123GXL

Part Number: SW-EK-TM4C123GXL

Hi, 

I am using the TM4C123GXL's PD0 and PD1 to drive 2 DC servo motors.  Below is the code I used to setup the clock, PWM and port.  I also use the buttons on the Tiva C Launchapd to move the DC servos to a particular position based on the cutting position.  After the initial cutting position 0, I use one of the buttons to advance the cutting position to 1.  However, it doesn't work.  The servos get stuck in a particular position and it doesn't move regardless of which button I press.  The program works just fine when I run in debug mode with break point inside the IF statement that is monitoring the buttons.  I can't figure out why it works in Debug, but not in Run mode. 

Thanks,

Charlie   

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/debug.h"
#include "driverlib/pwm.h"
#include "driverlib/pin_map.h"
#include "inc/hw_gpio.h"
#include "driverlib/rom.h"

// 20 ms @ 50 Hz
#define PWM_FREQUENCY 50

int main(void)

{
volatile uint32_t ui32Load;
volatile uint32_t ui32PWMClock;
volatile uint8_t ui8Adjust1, ui8Adjust2;

int cutting_position; // Position


cutting_position = 0;
ui8Adjust1 = 72; // subtract 3
ui8Adjust2 = 77; // add 2

ROM_SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);
ROM_SysCtlPWMClockSet(SYSCTL_PWMDIV_64);

ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

ROM_GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_0);
ROM_GPIOPinConfigure(GPIO_PD0_M1PWM0);
ROM_GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_1);
ROM_GPIOPinConfigure(GPIO_PD1_M1PWM1);

// Buttons are associated with Port F
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= 0x01;
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0;
ROM_GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_4|GPIO_PIN_0, GPIO_DIR_MODE_IN);
ROM_GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4|GPIO_PIN_0, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);


ui32PWMClock = SysCtlClockGet() / 64;
ui32Load = (ui32PWMClock / PWM_FREQUENCY) - 1;

PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN);
PWMGenPeriodSet(PWM1_BASE, PWM_GEN_0, ui32Load);
PWMGenConfigure(PWM1_BASE, PWM_GEN_1, PWM_GEN_MODE_DOWN);
PWMGenPeriodSet(PWM1_BASE, PWM_GEN_1, ui32Load);

ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0, ui8Adjust1 * ui32Load / 1000);
ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, ui8Adjust2 * ui32Load / 1000);
ROM_PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT, true);
ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_0);
ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_1);


while(1)
{

if(ROM_GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4)==0x00) // SW1 CCW
{
//ui8Adjust1--;
cutting_position--;
//if (ui8Adjust1 < 25)
if (cutting_position <= 1 )
{
//ui8Adjust1 = 25;
cutting_position = 1;
ui8Adjust1 = 101;
ui8Adjust2 = 57;
}
else if (cutting_position == 2)
{
ui8Adjust1 = 64;
ui8Adjust2 = 69;
}
else if (cutting_position == 3)
{
ui8Adjust1 = 81;
ui8Adjust2 = 90;
}
else if (cutting_position == 4)
{
ui8Adjust1 = 78;
ui8Adjust2 = 108;
}
else if (cutting_position == 5)
{
ui8Adjust1 = 79;
ui8Adjust2 = 58;
}
ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0, ui8Adjust1 * ui32Load / 1000); //Motor 1
ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, ui8Adjust2 * ui32Load / 1000); //Motor 2
ROM_SysCtlDelay(10000);
} //if

if(ROM_GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_0)==0x00) // SW2 CW
{
//ui8Adjust1++;
//if (ui8Adjust1 > 125)
cutting_position++;
if (cutting_position >= 5 )
{
cutting_position = 5;
ui8Adjust1 = 79;
ui8Adjust2 = 58;
}
else if (cutting_position == 4)
{
ui8Adjust1 = 78;
ui8Adjust2 = 108;
}
else if (cutting_position == 3)
{
ui8Adjust1 = 81;
ui8Adjust2 = 90;
}
else if (cutting_position == 2)
{
ui8Adjust1 = 64;
ui8Adjust2 = 69;
}
else if (cutting_position == 1)
{
ui8Adjust1 = 101;
ui8Adjust2 = 57;
}
ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0, ui8Adjust1 * ui32Load / 1000);//Motor 1
ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, ui8Adjust2 * ui32Load / 1000);//Motor 2
ROM_SysCtlDelay(10000);
}
} // while
} // main

  • Hi Charlie,

      Which cutting_position is stuck at? Between 0 to 5 where is it stuck? 

      How fast are you pressing the switch? Is it possible that you press a switch too fast before the SysCtlDelay? For example, you press SW2. You have a SysCtlDelay(10000) added in your code. If you press SW1 before the SysCtlDelay(10000) is counted then the SW1 is missed. In addition, you do not have debounce in your implementation. Without a debounce code, a press on the switch can produce multiple presses (or edges) to the GPIO input. You need to address this. 

      I will suggest you add a UARTprintf() or a printf() function to your code so you have a better idea what is going on. 

      Another feedback, even though may not be the cause of your problem, is also worth mentioning. You are trying to use PD0 and PD1 as your PWM pins. These two pins are produced by the same PWM_GEN_0. Why do you need to setup PWM_GEN_1? It is redundant. 

  • Hi Charles,

    Charles Tsai said:
    Why do you need to setup PWM_GEN_1? It is redundant. 

    Maybe not - it is believed that the '4C123 chart' you presented yesterday proved lacking.    Kindly view this one - right from the LPad's User Manual.

    Your chart did not reveal PD0/PD1 as able to operate under PWM1_BASE!'   (or PWM0_BASE)    Thus poster's  (exclusive) use of  'PWM1_BASE' is correct.   (Nowhere in his code appears PWM0_BASE -  which as you noted - would qualify as 'redundant.')   Indeed - as I've written here in the past - the naming schemes employed for the (many) PWM sources here - 'Could be better!'   (i.e. they stink!)

    Your analysis of the 'ineffective read' of user switches is our conclusion - as well.   Along w/the 'long delay & switch bounce' you identified - we'd prefer to see (some) post switch detection 'time-out' - to insure the stability of switch reads.

    Note too - poster's board is the 'Basic '123 LPad.'    And that board has, 'SO LONG SUFFERED FROM THE DEAD-SHORTS (DELIBERATELY INFLICTED UPON 'PD0-PB6 AND PD1-PB7!'    

    Now - should PB6 & PB7 (both) default into GPIO Input Mode - no harm may occur.    Yet we cannot - and should not - 'count upon' so fortunate an occurrence.

    Tag: Reading low cost switches proves a challenge - must be well thought & considered...

  • Hi Charles,

    It's getting stuck in cutting_position = 1.  My suspicion is that ui8Adjust1 and ui8Adjust2 are not being updated thus it's being stuck in cutting_position = 1.      

    As for pressing the buttons too fast, I don' think that's the cause.  When I ran the example in Lab 15 to increment/decrement ui8Adjust, it works just fine for PD0.

    I have commented out  PWM_GEN_1 in my code so no difference.  

    I tried implementing the UARTprintf() function to display the values in Tera Term.  Now, the buttons don't work at all to advance the DC servos to position 1. Below is the code I used.  Note I commented out the UAARTPrint.  Please let me know if you see anything wrong.  I also setup the tera term serial port to COM 3 and baud rate to 115.2k.  I don't see anything in the terminal window.      

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/gpio.h"
    #include "driverlib/debug.h"
    #include "driverlib/pwm.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/uart.h"
    #include "inc/hw_gpio.h"
    #include "driverlib/rom.h"
    #include "utils/uartstdio.h"

    // 20 ms @ 50 Hz
    #define PWM_FREQUENCY 50

    //*****************************************************************************
    //
    // This function sets up UART0 to be used for a console to display information
    // as the example is running.
    //
    //*****************************************************************************
    void
    InitConsole(void)
    {
    //
    // Enable GPIO port A which is used for UART0 pins.
    // TODO: change this to whichever GPIO port you are using.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Configure the pin muxing for UART0 functions on port A0 and A1.
    // This step is not necessary if your part does not support pin muxing.
    // TODO: change this to select the port/pin you are using.
    //
    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);

    //
    // Enable UART0 so that we can configure the clock.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    ROM_UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

    //
    // Select the alternate (UART) function for these pins.
    // TODO: change this to select the port/pin you are using.
    //
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, 16000000); //16MHZ
    }

    int main(void)

    {
    volatile uint32_t ui32Load;
    volatile uint32_t ui32PWMClock;
    //volatile uint8_t ui8Adjust;
    volatile uint8_t ui8Adjust1, ui8Adjust2;

    int cutting_position = 0; // Position

    ui8Adjust1 = 72; // subtract 3
    ui8Adjust2 = 77; // add 2

    ROM_SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);

    ROM_SysCtlPWMClockSet(SYSCTL_PWMDIV_64);
    InitConsole(); //UART
    //ROM_SysCtlDelay(10000);

    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); // PWM
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); // Buttons

    ROM_GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_0); // PWM output
    ROM_GPIOPinConfigure(GPIO_PD0_M1PWM0);
    ROM_GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_1); // PWM output
    ROM_GPIOPinConfigure(GPIO_PD1_M1PWM1);

    // Buttons are associated with Port F
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
    HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= 0x01;
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0;
    ROM_GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_4|GPIO_PIN_0, GPIO_DIR_MODE_IN);
    ROM_GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4|GPIO_PIN_0, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);


    ui32PWMClock = SysCtlClockGet() / 64;
    ui32Load = (ui32PWMClock / PWM_FREQUENCY) - 1;

    ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN);
    ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_0, ui32Load);
    //PWMGenConfigure(PWM1_BASE, PWM_GEN_1, PWM_GEN_MODE_DOWN);
    //PWMGenPeriodSet(PWM1_BASE, PWM_GEN_1, ui32Load);

    ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0, ui8Adjust1 * ui32Load / 1000);
    ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, ui8Adjust2 * ui32Load / 1000);
    ROM_PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT, true);
    ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_0);
    //ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_1);
    //UARTprintf("starting loop! \r\n");
    while(1)
    {
    if(ROM_GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4)==0x00) // SW1 CCW
    {
    //ui8Adjust1--;
    cutting_position--;
    //if (ui8Adjust1 < 25)
    if (cutting_position <= 1 )
    {
    //ui8Adjust1 = 25;
    cutting_position = 1;
    ui8Adjust1 = 101;
    ui8Adjust2 = 57;
    }
    else if (cutting_position == 2)
    {
    ui8Adjust1 = 64;
    ui8Adjust2 = 69;
    }
    else if (cutting_position == 3)
    {
    ui8Adjust1 = 81;
    ui8Adjust2 = 90;
    }
    else if (cutting_position == 4)
    {
    ui8Adjust1 = 78;
    ui8Adjust2 = 108;
    }
    else if (cutting_position == 5)
    {
    ui8Adjust1 = 79;
    ui8Adjust2 = 58;
    }
    //UARTprintf("cutting position = %1i \r\n", cutting_position);
    //UARTprintf("DC Servo 1 position = %2i \r\n", ui8Adjust1);
    //UARTprintf("DC Servo 2 position = %2i \r\n", ui8Adjust2);
    ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0, ui8Adjust1 * ui32Load / 1000); //Motor 1
    ROM_SysCtlDelay(10000);
    ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, ui8Adjust2 * ui32Load / 1000); //Motor 2
    ROM_SysCtlDelay(10000);
    } //if

    if(ROM_GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_0)==0x00) // SW2 CW
    {
    //ui8Adjust1++;
    //if (ui8Adjust1 > 125)
    cutting_position++;
    if (cutting_position >= 5 )
    {
    cutting_position = 5;
    ui8Adjust1 = 79;
    ui8Adjust2 = 58;
    }
    else if (cutting_position == 4)
    {
    ui8Adjust1 = 78;
    ui8Adjust2 = 108;
    }
    else if (cutting_position == 3)
    {
    ui8Adjust1 = 81;
    ui8Adjust2 = 90;
    }
    else if (cutting_position == 2)
    {
    ui8Adjust1 = 64;
    ui8Adjust2 = 69;
    }
    else if (cutting_position == 1)
    {
    ui8Adjust1 = 101;
    ui8Adjust2 = 57;
    }
    //UARTprintf("cutting position = %1i \r\n", cutting_position);
    //UARTprintf("DC Servo 1 position = %2i \r\n", ui8Adjust1);
    //UARTprintf("DC Servo 2 position = %2i \r\n", ui8Adjust2);
    ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0, ui8Adjust1 * ui32Load / 1000);//Motor 1
    ROM_SysCtlDelay(10000);
    ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_1, ui8Adjust2 * ui32Load / 1000);//Motor 2
    ROM_SysCtlDelay(10000);
    }
    } // while
    } // main

  • Hi Charlie,

      Not sure what went wrong. Did you mean that it works if only PD0 is used but not both PD0 and PD1?

      When you said that ui8Adjust1 and ui8Adjust2 are not updated and hence get stuck at position 1, this leads me to the note below. 

      Can you try one experiment?

      Change from:

    ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN); 

      to:

    ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_GEN_SYNC_LOCAL); 

     or:

    ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_GEN_SYNC_GLOBAL); 

    Below is the description about the local and global synchronization. The synchronization defers the update to the compare register when the counter becomes zero. Try it and see if it makes a difference. 

     

  • Charlie Chi said:
    ...it doesn't work.  The servos get stuck in a particular position and it doesn't move regardless of which button I press.  The program works just fine when I run in debug mode with break point inside the IF statement that is monitoring the buttons.  I can't figure out why it works in Debug, but not in Run mode. 

    Pardon - but that, "Can't figure out" has (already) been answered - by both Vendor's Charles & myself.    You succeed while in debug due to the 'Debounce Function' being performed via the 'breakpoints.'   When Switch Activation is detected - the code halts - eliminating the (further reads) of your 'chattering user switch!'

    Low cost switches are notorious for exhibiting 'Contact Bounce' - both Charles & myself recognized & (past) reported that fact.   A proper method of 'Rejecting that bounce' IS required.

    Your code (attempts) such 'debounce' via,  "ROM_SysCtlDelay(10000);"    When analyzed we note that: 1/(40,000,000) x 3333 = 0.000,083,325 Seconds.    That's  just  '83µS' - almost surely too brief to serve as a 'proper switch debounce mechanism!'    It is suspected that  ~3mS of delay (after the switch's initial detection) would provide adequate debounce duration.   (Experimentation may be required.)

    A likely better method of monitoring 'ui8Adjust(1 & 2)' would be to direct each variable to a 'Live Watch Window.'     Such is easily done w/in Pro IDEs (IAR/Keil)  hopefully yours includes this 'most valuable' feature/capability.

    As to 'Why your ui8Adjust values stick' - that's likely explained by 'so many detections' of the switch contacts closing' - that your 'cutting_position variable' is 'driven negative'  (via contact_position --;) - and then immediately - Set to 1 via your 'if statement's action clause.'     (You  thus become stuck in a 'variable's negative value - then forced to '1 and only 1' - repetitive loop!)    You CAN write a code segment which 'captures & logs' 'cutting_position's' (many transitioning) [i.e. erroneous] values.   (as young yet talented staff here - did...)

    Any PWM issues are 'unlikely to cause'  'ui8Adjdust(1 or 2)' to become stuck.

    And do note - your (uncorrected LPad) ties PD0 to PB6 & PD1 to PB7!    That's not good - and should you employ (either PB6/7) as an output - disaster awaits!    Tombstone 'Plague-Istors' R9 & R10 to correct this LPad's (deliberate) inadequacy...

  • Hi cb1,

      Thanks again. You just reinforce my earlier suspicion. A pin press creates multiple bounces the will reload the compare value the same amount of times. As stated in datasheet, the compare register will be immediately overwritten multiple times. 

    Hi Charlie,

      Below is just one example of creating a software debounce. You can create your own. The example below creates a 40ms debounce assuming the system clock is running at 16MHz. 

        //
        // Wait until the SW1 button has been pressed for ~40ms (in order to
        // debounce the press).
        //
        ui32Count = 0;
        while(1)
        {
            //
            // See if the button is pressed.
            //
            if(ROM_GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4) == 0)
            {
                //
                // Increment the count since the button is pressed.
                //
                ui32Count++;
    
                //
                // If the count has reached 4, then the button has been debounced
                // as being pressed.
                //
                if(ui32Count == 4)
                {
                    break;
                }
            }
            else
            {
                //
                // Reset the count since the button is not pressed.
                //
                ui32Count = 0;
            }
    
            //
            // Delay for approximately 10ms.
            //
            SysCtlDelay(16000000 / (3 * 100));
        }

  • Thank you - however in defense of our young/beyond able staff:

    Charles Tsai said:
    You just reinforce my earlier suspicion.

    it is believed that 'far more' than 'just reinforce' was presented:

    • the actual 'defect' of poster's (attempted) debounce was calculated & presented
    • the mechanism by which his 'sticking' occurred was clearly noted
    • poster's direct questioning of, Why Debug (only) succeeded was addressed
    • 'Live Watch Window' proves vastly superior to 'Uart Usage'  (which failed - btw)
    • As No/Zero PWM feedback (i.e. Servo Positioning Feedback) is deployed - it is (beyond) unlikely that any 'PWM issue' caused such code 'sticking.'
    • the on-going plague of 'dead-shorts' across multiple (4) GPIO pins - despite multiple user protests - was clearly noted

    It is thus believed that staff performed comprehensively and 'Above & Beyond!'   (dealt with & answered everything) - not  merely  'reinforcing....'

  • Hi Charlie,

      Do you have any updates?

  • Hi Charles,

    I was able to get it to work by increasing the ROM_SysCtlDelay(10000) to (1000000) after PWMPulseWidth.  I think the issue is the DC servo driver circuits.  It needs some time to clear or read the input signal before the next signal (period).  

    Best,

    Charlie 

  • The travesty of 'Hugely Mistaken Self-Verifies' continues.     Hard and insightful effort in poster's behalf is, 'Kicked to the curb!'

    Poster's explanation reveals 'little understanding' (still) of 'Switch Bounce.'