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.

0% and 100% Duty Cycle by Toggling Pin Configuration

Other Parts Discussed in Thread: TM4C1294NCPDT, EK-TM4C1294XL

Hi all,

I am attempting to tackle an MCU limitation in the PWM Module in extreme cases in 0% and 100% duty cycle. Upon reading the following threads:

I figured it be best that I just attempt toggling the configuration of the pins.

My current apparatus is as follows:

  • Custom board utilizing the TM4C1294NCPDT MCU
  • EK-TM4C1294XL Development Board as my debugger/programmer
  • Code Composer Studio 6.1.2 as my coding environment

My question is, how would I go about switching between a GPIO configuration to a PWM configuration?

Currently the program initiates the pins of interest (noted in the initPWM() function below) as a PWM GPIO pin type. I am interested in figuring out a way to change said pins into an digital output and back [to PWM pin type] at users request.

Am I required to repeat the whole configuration process every time I switch between the two (see code below)? Or do I just call one of the GPIOPinTypex() functions?

void initPWM (void) {

    // Set the PWM clock to the system clock / 1.
	MAP_SysCtlPWMClockSet(SYSCTL_PWMDIV_1);

	// TM4C1294NCPDT MCU only has one module for PWM use (M0PWMn)
	MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);

	// Enable the ports used in the pwm configuration.
	MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
	MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
	MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);

	// Configure pins being used for it's corresponding PWM function
	MAP_GPIOPinConfigure(GPIO_PF0_M0PWM0);
	MAP_GPIOPinConfigure(GPIO_PF1_M0PWM1);
	MAP_GPIOPinConfigure(GPIO_PF2_M0PWM2);	
	MAP_GPIOPinConfigure(GPIO_PF3_M0PWM3);	
	MAP_GPIOPinConfigure(GPIO_PG0_M0PWM4);	
	MAP_GPIOPinConfigure(GPIO_PG1_M0PWM5);	
	MAP_GPIOPinConfigure(GPIO_PK4_M0PWM6);
	MAP_GPIOPinConfigure(GPIO_PK5_M0PWM7);
    
    // Configure the PWM function for the pins used on the board.
	MAP_GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_0);	// M0PWM0
	MAP_GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1);	// M0PWM1
	MAP_GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_2);	// M0PWM2
	MAP_GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_3);	// M0PWM3
	MAP_GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_0);	// M0PWM4
	MAP_GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_1);	// M0PWM5
	MAP_GPIOPinTypePWM(GPIO_PORTK_BASE, GPIO_PIN_4);	// M0PWM6
	MAP_GPIOPinTypePWM(GPIO_PORTK_BASE, GPIO_PIN_5);	// M0PWM7

    // Configure the PWM0 to count up/down without synchronization.
	MAP_PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
	MAP_PWMGenConfigure(PWM0_BASE, PWM_GEN_1, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
	MAP_PWMGenConfigure(PWM0_BASE, PWM_GEN_2, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
	MAP_PWMGenConfigure(PWM0_BASE, PWM_GEN_3, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);

	// Default period during setup is 1% duty cycle at 10KHz
    MAP_PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, 12000);
	MAP_PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, 12000);
	MAP_PWMGenPeriodSet(PWM0_BASE, PWM_GEN_2, 12000);
	MAP_PWMGenPeriodSet(PWM0_BASE, PWM_GEN_3, 12000);

	// Start up duty cycle is set at 1% as starting at 0% causes issues with the PWM output.
	MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, PWMGenPeriodGet(PWM0_BASE, PWM_GEN_0) * 0.01);
	MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, PWMGenPeriodGet(PWM0_BASE, PWM_GEN_0) * 0.01);
	MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, PWMGenPeriodGet(PWM0_BASE, PWM_GEN_1) * 0.01);
	MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_3, PWMGenPeriodGet(PWM0_BASE, PWM_GEN_1) * 0.01);
	MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_4, PWMGenPeriodGet(PWM0_BASE, PWM_GEN_2) * 0.01);
	MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_5, PWMGenPeriodGet(PWM0_BASE, PWM_GEN_2) * 0.01);
	MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_6, PWMGenPeriodGet(PWM0_BASE, PWM_GEN_3) * 0.01);
	MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_7, PWMGenPeriodGet(PWM0_BASE, PWM_GEN_3) * 0.01);

    // Enable the PWM generator block to output PWM
	MAP_PWMGenEnable(PWM0_BASE, PWM_GEN_0);
	MAP_PWMGenEnable(PWM0_BASE, PWM_GEN_1);
	MAP_PWMGenEnable(PWM0_BASE, PWM_GEN_2);
	MAP_PWMGenEnable(PWM0_BASE, PWM_GEN_3);

	// Default the PWM outputs to normally closed during initialization and start up.
	MAP_PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT, true);
	MAP_PWMOutputState(PWM0_BASE, PWM_OUT_1_BIT, true);
	MAP_PWMOutputState(PWM0_BASE, PWM_OUT_2_BIT, true);
	MAP_PWMOutputState(PWM0_BASE, PWM_OUT_3_BIT, true);
	MAP_PWMOutputState(PWM0_BASE, PWM_OUT_4_BIT, true);
	MAP_PWMOutputState(PWM0_BASE, PWM_OUT_5_BIT, true);
	MAP_PWMOutputState(PWM0_BASE, PWM_OUT_6_BIT, true);
	MAP_PWMOutputState(PWM0_BASE, PWM_OUT_7_BIT, true);
}
void initDigital(void) {

    // Enables ports that digital pins are attached to
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOP);

    // Configure correspoding pins as digital outputs
    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTP_BASE, GPIO_PIN_1); // DIG1
    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTP_BASE, GPIO_PIN_0); // DIG2
    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_5); // DIG3
    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_4); // DIG4
    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_3); // DIG5
    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_2); // DIG6
    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_1); // DIG7
    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTP_BASE, GPIO_PIN_2); // DIG8    

    // Configure outputs with weak pull-down and output current of up to 4 mA.
    // DIG2, DIG1, DIG8
    MAP_GPIOPadConfigSet(GPIO_PORTP_BASE, (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2), 
        GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPD);

    // DIG7, DIG6, DIG5, DIG4, DIG3
    MAP_GPIOPadConfigSet(GPIO_PORTN_BASE, (GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5), 
        GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPD);
}

  • Hello Stephen

    A GPIO configuration is sufficient. You may required to call the full corresponding GPIO configuration function for the pin to be in PWM or Output Mode

    Regards
    Amit
  • Hi Amit,

    Thank you for your reply.

    I'm not sure I fully understand what you mean.

    Here's some quick coding to see if I understand what you meant, here's what I came up with:

    void changeDutyCycle (uint8_t pwmPin, uint8_t dutyCycle ) {
    	if (dutyCycle == 0 || dutyCycle == 100) {
    		// Check if the pin of interest is being modified is already an output type.
    		if (isDigital == TRUE) {
    			if (dutyCycle == 0) {
    				// Note: There are 8 of these
    				if (pwmPin == 0) {
    					GPIOPinWrite(GPIO_PORTx_BASE, GPIO_PIN_0, 0)
    				}	
    			}
    			else {
    				// Note: There are 8 of these
    				if (pwmPin == 0) {
    					GPIOPinWrite(GPIO_PORTx_BASE, GPIO_PIN_0, 1)
    				}	
    			}
    		}
    		else {
    			switchToDigital(pwmPin); 
    		}
    	}
    	else {
    		switchToPWM(pwmPin);
    		if (pwmPin == 0){
    			MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, PWMGenPeriodGet(PWM0_BASE, PWM_GEN_0) * load/100);
    		}
    	}
    }
    
    void switchToDigital(uint8_t pwmPin) {
    	if (pwmPin == 0) {
    		MAP_GPIOPinTypeGPIOOutput(GPIO_PORTx_BASE, GPIO_PIN_0);
    		isDigital[0] = TRUE;
    	}
    }
    
    void switchToPWM(uint8_t pwmPin) {
    	if (pwmPin == 0) {
    		MAP_GPIOPinTypePWM(GPIO_PORTx_BASE, GPIO_PIN_0);
    		isDigital[0] = FALSE;
    	}	
    }

    Did I capture what you meant?

    Additionally, this is a bit off-topic, is there a way for me to clean this up? The code will be fairly large with plenty of else-if statements and I figured that could get plenty confusing.

    Thank you,

    Stephen B.

  • Hello Stephen

    In the switch to PWM make sure that the GPIOPinConfigure function is also called so that the PCTL is correctly programmed.

    Inline functions may be used or if the functions can kept in another C file for maintenance.

    Regards
    Amit
  • Hi Amit,

    Thank you very much for you help! I honestly thought that would take me a lot longer to get.

    Cheers,

    Stephen B.

  • While this software intensive method can be made to work - one asks, (or should ask) might a simpler means be available?

    Hardware dawns - the addition of one (properly valued) resistor - placed in series w/the MCU's PWM output - and one GPIO pin, programmed as:

    • "push-pull" output - set "high" when 100% Duty Cycle is desired     
    •  "open-drain" output - set "on" when 0% Duty Cycle is desired
    • "open-drain" output - set "off" when the Duty Cycle is "not" @ extremes   (then the MCU's PWM output is passed w/out modification)

    The GPIO attaches to the PWM device's side (non-MCU) side of the resistor - is able to "over-ride" the (improper/extreme) MCU, PWM levels.   The "over-ride" series resistor is chosen such that the current draw remains - at all times - w/in the MCU's specification.

    This (slight) HW addition will be faster responding and more robust than the "brute force/major switch-over" (PWM <-> GPIO) earlier described...

    In addition - this method is far more, "general in nature" and may be employed when (and whereever)  a, "2nd opinion" adds robustness to a critical, GPIO output...

  • Hi cb1_mobile,

    While the idea is actually quite ingenious, I'm afraid we can't implement this solution now.

    We will surely look to implementing this hardware fix in future iterations of the board. For now, I was only told to program in the extreme case duty cycle for "just-in-case" situations. The regular use of the board will not see the use of 0% or 100% duty cycle, but only values between (and including) 1% and 99%.

    Thank you,

    Stephen Bolanos

  • Hello Stephen,

    Thank you - might I suggest that you, "Test/Verify" that your PWM Duty Cycle "maintains" @ (both) 1 & 99%. (some devices may prove unstable even when "not" @ their "max/min.")

    In most all cases - the "effective difference" between 95% and 100% duty cycle will be (very) difficult to detect - the 0% (or near) level may be more sensitive - much depends upon the external device's response...