Other Parts Discussed in Thread: EK-TM4C123GXL
I want to measure 4ch PWM using capture mode of timer.Here is my configuration:
But somehow, I can't enter into the interrupt.Did I make some mistakes?
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.
I want to measure 4ch PWM using capture mode of timer.Here is my configuration:
But somehow, I can't enter into the interrupt.Did I make some mistakes?
Yes,I write it before initRC() after initClock().
#include "userDefine.h" u8 All_init() { initClock(); initSysTick(); initLED(); initPWM(); initKey(); initEEPROM(); initFlag(); initUART(); blueLed();//Blue LED means "initAll is ok" return 1; } void initClock() { ROM_FPUEnable(); ROM_FPULazyStackingEnable(); // Enable lazy stacking for interrupt handlers. This allows floating-point instructions to be used within interrupt handlers, // but at the expense of extra stack usage. ROM_SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN); ROM_IntMasterEnable(); //<<------- } void initSysTick() { ROM_SysTickPeriodSet(ROM_SysCtlClockGet()/SysTickPeriod); ROM_SysTickIntEnable(); SysTickIntRegister(SysTickIntHandler);//register systick interrupts ROM_SysTickEnable(); } void initRC() { ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0); //enable Wtimer0 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER1); //enable Wtimer1 ROM_TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_COUNT| TIMER_CFG_B_CAP_COUNT);//TIMER0 AB ROM_TimerConfigure(WTIMER1_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_COUNT| TIMER_CFG_B_CAP_COUNT);//TIMER1 AB ROM_TimerControlEvent(WTIMER0_BASE, TIMER_A, TIMER_EVENT_BOTH_EDGES ); //capture the up and down at the same time ROM_TimerControlEvent(WTIMER0_BASE, TIMER_B, TIMER_EVENT_BOTH_EDGES ); ROM_TimerControlEvent(WTIMER1_BASE, TIMER_A, TIMER_EVENT_BOTH_EDGES ); ROM_TimerControlEvent(WTIMER1_BASE, TIMER_B, TIMER_EVENT_BOTH_EDGES ); ROM_TimerLoadSet(WTIMER0_BASE, TIMER_A, 2000000-1); ROM_TimerLoadSet(WTIMER0_BASE, TIMER_B, 2000000-1); ROM_TimerLoadSet(WTIMER1_BASE, TIMER_A, 2000000-1); ROM_TimerLoadSet(WTIMER1_BASE, TIMER_B, 2000000-1); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); //enable gpioC for PC4567 ROM_GPIOPadConfigSet(GPIO_PORTC_BASE,GPIO_PIN_4,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPD); ROM_GPIOPadConfigSet(GPIO_PORTC_BASE,GPIO_PIN_5,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPD); ROM_GPIOPadConfigSet(GPIO_PORTC_BASE,GPIO_PIN_6,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPD); ROM_GPIOPadConfigSet(GPIO_PORTC_BASE,GPIO_PIN_7,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPD); ROM_GPIOPinConfigure(GPIO_PC4_WT0CCP0|GPIO_PC5_WT0CCP1|GPIO_PC6_WT1CCP0|GPIO_PC7_WT1CCP1); ROM_GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7); TimerIntRegister(WTIMER0_BASE, TIMER_A, WTimer0AIntHandler); // TimerIntRegister(WTIMER0_BASE, TIMER_B, WTimer0BIntHandler); // TimerIntRegister(WTIMER1_BASE, TIMER_A, WTimer1AIntHandler); // TimerIntRegister(WTIMER1_BASE, TIMER_B, WTimer1BIntHandler); ROM_IntMasterEnable(); ROM_TimerIntEnable(WTIMER0_BASE, TIMER_CAPA_EVENT); ROM_TimerIntEnable(WTIMER0_BASE, TIMER_CAPB_EVENT); ROM_TimerIntEnable(WTIMER1_BASE, TIMER_CAPA_EVENT); ROM_TimerIntEnable(WTIMER1_BASE, TIMER_CAPB_EVENT); ROM_IntEnable(INT_WTIMER0A); ROM_IntEnable(INT_WTIMER0B); ROM_IntEnable(INT_WTIMER1A); ROM_IntEnable(INT_WTIMER1B); ROM_TimerEnable(WTIMER0_BASE, TIMER_A); ROM_TimerEnable(WTIMER0_BASE, TIMER_B); ROM_TimerEnable(WTIMER1_BASE, TIMER_A); ROM_TimerEnable(WTIMER1_BASE, TIMER_B); } //INTHANDLER void WTimer0AIntHandler(void) { TimerIntClear(WTIMER0_BASE,TimerIntStatus(WTIMER0_BASE, TIMER_CAPA_EVENT)); a=TimerValueGet(WTIMER0_BASE, TIMER_A); printf("ok"); // if(GPIOPinRead(GPIO_PORTC_BASE,GPIO_PIN_4)==GPIO_PIN_4) // a=a1/(a1+TimerValueGet(WTIMER0_BASE, TIMER_A)); // else // a1=TimerValueGet(WTIMER0_BASE, TIMER_A); }
#include "userDefine.h" u8 All_init() { initClock(); initRC(); return 1; } void initClock() { ROM_FPUEnable(); ROM_FPULazyStackingEnable(); ROM_SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN); ROM_IntMasterEnable(); //<<-------- } void initRC() { ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0); //enable Wtimer0 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER1); //enable Wtimer1 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); //enable gpioC for PC4567 ROM_GPIOPinConfigure(GPIO_PC4_WT0CCP0); ROM_GPIOPinConfigure(GPIO_PC5_WT0CCP1); ROM_GPIOPinConfigure(GPIO_PC6_WT1CCP0); ROM_GPIOPinConfigure(GPIO_PC7_WT1CCP1); ROM_GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_4); ROM_GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_5); ROM_GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_6); ROM_GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_7); ROM_GPIOPadConfigSet(GPIO_PORTC_BASE,GPIO_PIN_4,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPD); ROM_GPIOPadConfigSet(GPIO_PORTC_BASE,GPIO_PIN_5,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPD); ROM_GPIOPadConfigSet(GPIO_PORTC_BASE,GPIO_PIN_6,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPD); ROM_GPIOPadConfigSet(GPIO_PORTC_BASE,GPIO_PIN_7,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPD); ROM_TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP ); ROM_TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_CAP_TIME_UP ); ROM_TimerConfigure(WTIMER1_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP ); ROM_TimerConfigure(WTIMER1_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_CAP_TIME_UP ); ROM_TimerControlEvent(WTIMER0_BASE, TIMER_A, TIMER_EVENT_BOTH_EDGES ); //capture the up and down at the same time ROM_TimerControlEvent(WTIMER0_BASE, TIMER_B, TIMER_EVENT_BOTH_EDGES ); ROM_TimerControlEvent(WTIMER1_BASE, TIMER_A, TIMER_EVENT_BOTH_EDGES ); ROM_TimerControlEvent(WTIMER1_BASE, TIMER_B, TIMER_EVENT_BOTH_EDGES ); ROM_TimerLoadSet(WTIMER0_BASE, TIMER_A, 2000000-1); ROM_TimerLoadSet(WTIMER0_BASE, TIMER_B, 2000000-1); ROM_TimerLoadSet(WTIMER1_BASE, TIMER_A, 2000000-1); ROM_TimerLoadSet(WTIMER1_BASE, TIMER_B, 2000000-1); ROM_IntEnable(INT_WTIMER0A); ROM_IntEnable(INT_WTIMER0B); ROM_IntEnable(INT_WTIMER1A); ROM_IntEnable(INT_WTIMER1B); TimerIntRegister(WTIMER0_BASE, TIMER_A, WTimer0AIntHandler); TimerIntRegister(WTIMER0_BASE, TIMER_B, WTimer0BIntHandler); TimerIntRegister(WTIMER1_BASE, TIMER_A, WTimer1AIntHandler); TimerIntRegister(WTIMER1_BASE, TIMER_B, WTimer1BIntHandler); ROM_TimerIntEnable(WTIMER0_BASE, TIMER_CAPA_EVENT); ROM_TimerIntEnable(WTIMER0_BASE, TIMER_CAPB_EVENT); ROM_TimerIntEnable(WTIMER1_BASE, TIMER_CAPA_EVENT); ROM_TimerIntEnable(WTIMER1_BASE, TIMER_CAPB_EVENT); ROM_TimerEnable(WTIMER0_BASE, TIMER_A); ROM_TimerEnable(WTIMER0_BASE, TIMER_B); ROM_TimerEnable(WTIMER1_BASE, TIMER_A); ROM_TimerEnable(WTIMER1_BASE, TIMER_B); } void WTimer0AIntHandler(void) { TimerIntClear(WTIMER0_BASE,TimerIntStatus(WTIMER0_BASE, TIMER_CAPA_EVENT)); a=TimerValueGet(WTIMER0_BASE, TIMER_A); } void WTimer0BIntHandler(void) { TimerIntClear(WTIMER0_BASE,TimerIntStatus(WTIMER0_BASE, TIMER_CAPB_EVENT)); b=TimerValueGet(WTIMER0_BASE, TIMER_B); } void WTimer1AIntHandler(void) { TimerIntClear(WTIMER1_BASE,TimerIntStatus(WTIMER1_BASE, TIMER_CAPA_EVENT)); c=TimerValueGet(WTIMER1_BASE, TIMER_A); } void WTimer1BIntHandler(void) { TimerIntClear(WTIMER1_BASE,TimerIntStatus(WTIMER1_BASE, TIMER_CAPB_EVENT)); d=TimerValueGet(WTIMER1_BASE, TIMER_B); }
After some modification, I have got some data.However,only Timer0B and Timer1B displayed(through the debug watch window).Here are screenshots:
As you can see,the 2ch data are unstable. but the input which I can insure is 4ch same 7% 50hz PWM from the model airplane receiver.
I still don't know where is my fault and I can't get the correct data....
Rongze Li said:I still don't know where is my fault and I can't get the correct data....
And indeed - as SO often revealed here - that occurs when, 'Multiple Program Elements are (all) clustered together - which (rarely) IF EVER - 'Works 'Out of the Box' - while adding greatly to the Time & Effort to 'Properly & Effectively' Troubleshoot!
'KISS' - the (advantaged) discipline to build, 'ONE MEASURABLE Function at a time' - then 'Test/Verify that' - and then (and only then) proceed to function two - has been (repeatedly) proven - to work FAR BETTER!
One simplification - which I've suggested (repeatedly here) is to employ TWO Timers - one to detect the Signal's 'Rising Edge' - the second the 'Falling Edge.' (You must 'strap' your PWM signal to both Timer inputs.) In both cases - you'd employ the timers in 'Edge-time mode' - which logs each 'event' automatically - for you. Provided that you 'read & safely store' each timer's content (prior to the next signal edge's arrival) the simple subtraction of the 'Rising Edge's value - from that of the 'Falling Edge's' (provided neither timer has 'rolled over' - presents (gift-wraps, actually) your PWM Signal's Duty Cycle and Frequency.
Use of the Forum's Search Box (atop the forum page) is sure to reveal 'multiple poster's having succeeded - via this (sure) 'KISS' method...
Hello cb1
Thanks for your suggestion.
I have read many related posts,but I really want to measure 1ch PWM with one GPIOPIN. Because in my entire quadrotor design,there's not much gpio left for me with EK-TM4C123G LaunchPad.
And I noticed it in the manual,which says TIMERA and TIMERB are free-running.I guess they are independent.
Thank you again. And sorry for my poor English. I am a student from China.I have been troubled by this problem for a long time.
I'm trying unit tests as you said.
Thank you - may the following be noted:
But NOT here - and NOT now! Is not your First Job to be able to achieve the basic measurement? The MANY complications your, 'HIGHLY BURDENED & COMPLEX METHOD PRESENTS' - have 'Stalled (blocked) your Progress' - you stated 'just that' - in your (quite good) English...
It proves far easier to 'Succeed with a Complex Design' by Reducing it into: 'Multiple, Much Smaller, More Measurable, Constituent (Distinct) Parts' - which may FAR MORE Quickly & Easily be Probed/Examined - thus Tested and Verified! And only 'then' (if & when successful) - joined to an 'Expanding Whole.'
The 'Use of 2 Timers' each one devoted to 'Opposite Edge, GPIO Triggered Interrupts' provides such 'Speed & Ease' - which 'Sets the Stage' (by providing 'Proof of concept' & building highly focused insight) - for later 'refinements' ... such as a single timer - able to fully/properly manage - both arriving signal edges. Again - this method has long been noted as, 'Proceeding by Refinement' - or more colorfully as 'KISS.' (KISS clearly works - even though banned here - especially though banned here!) (as w/'LIKE' - for Never Justified (thus suspect) reason.)
During your development & experimentation - I suggest that you employ (yet another) MCU Split Timer (configured as PWM Output @ 50% Duty) as a 'Known & Easily Programmable Signal Generator' - which feeds 2 Timer pins (i.e. is strapped to two) - speeding & easing your 'Edge Time Delta calculations.' (eliminates all uncertainties introduced by the 'foreign' signal generator (possibly NOISE producing!))
Instead of calling TimerEnable separately for each half of a timer, you should call it once and use "TIMER_BOTH"
Instead of:
ROM_TimerEnable(WTIMER0_BASE, TIMER_A); ROM_TimerEnable(WTIMER0_BASE, TIMER_B); ROM_TimerEnable(WTIMER1_BASE, TIMER_A); ROM_TimerEnable(WTIMER1_BASE, TIMER_B);
Use:
ROM_TimerEnable(WTIMER0_BASE, TIMER_BOTH); ROM_TimerEnable(WTIMER1_BASE, TIMER_BOTH);
Similarly, your calls to TimerConfigure should be one call for each timer.
Instead of:
ROM_TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP ); ROM_TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_CAP_TIME_UP ); ROM_TimerConfigure(WTIMER1_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP ); ROM_TimerConfigure(WTIMER1_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_CAP_TIME_UP );
Use:
ROM_TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP | TIMER_CFG_B_CAP_TIME_UP); ROM_TimerConfigure(WTIMER1_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP | TIMER_CFG_B_CAP_TIME_UP);
Hola Bob,
Are you suggesting that the use of: 'Two such - Sequenced & Individual 'Timer Configure' function calls - such as:
ROM_TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP );
ROM_TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_CAP_TIME_UP );
is likely to cause the 'over-write' of the 'first function call's effect?' (by that second call?) If true - then poster's parameter 'TIMER_CFG_A_CAP_TIME_UP' is (unexpectedly & unwantedly) 'removed!' Clearly that's NOT GOOD!
Or - is it just 'Programming Efficiency' that you seek? (nothing wrong w/that - although the 'Unexpected LOSS/Corruption of an earlier parameter' - SURELY deserves CLEAR NOTE!) (and - we cannot tell - if that 'Loss/Corruption' - is what your post aims to prevent...)
Hi CB1,
A good thing about library function is that they hide the hardware details. A bad thing about library functions is that they hide the hardware details. I tried to put together a simple example that uses wide timer 0 to create two PWMs and uses wide timer 1 to measure the high and low periods. I use an EK-TM4C123GXL launchpad and two jumper wires to connect wide timer 0 pins to wide timer 1 pins. I found the overwriting of the TimerControlEvent() function so troublesome, I resorted to switching from rising edge to falling edge trigger by using HWREG reads and writes. There is probably a more elegant way to do it, perhaps always trigger on both edges and check the pin level to determine if it was rising or falling. This simple example does not cover the case where the pulse phase is longer than can be captured with the 32-bit timer, or the case where the pulse is so short that the interrupt routine cannot setup for the second edge in time.
#include <stdint.h> #include <stdbool.h> #include "inc/tm4c123gh6pm.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" #include "driverlib/interrupt.h" #include "driverlib/timer.h" #include "inc/hw_timer.h" void WTimer1AIntHandler(); void WTimer1BIntHandler(); enum EDGE { Rising, Falling }; unsigned int A_RisingEdge, A_FallingEdge, B_RisingEdge, B_FallingEdge; volatile unsigned int A_LowPeriod, A_HighPeriod, B_LowPeriod, B_HighPeriod; enum EDGE A_Edge, B_Edge; void main(void) { SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ); // Use Wide Timer 0 to generate two PWMs SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); SysCtlDelay(3); GPIOPinConfigure(GPIO_PC5_WT0CCP1); GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_5); GPIOPinConfigure(GPIO_PC4_WT0CCP0); GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_4); SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0); SysCtlDelay(3); TimerConfigure(WTIMER0_BASE,TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM|TIMER_CFG_B_PWM); TimerLoadSet(WTIMER0_BASE, TIMER_A, 80000); TimerMatchSet(WTIMER0_BASE, TIMER_A, 30000); TimerLoadSet(WTIMER0_BASE, TIMER_B, 70000); TimerMatchSet(WTIMER0_BASE, TIMER_B, 20000); TimerControlStall(WTIMER0_BASE, TIMER_BOTH, true); TimerEnable(WTIMER0_BASE, TIMER_BOTH); // Use Wide Timer 1 to capture edges from WTIMER0 GPIOPinConfigure(GPIO_PC6_WT1CCP0); GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_6); GPIOPinConfigure(GPIO_PC7_WT1CCP1); GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_7); SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER1); SysCtlDelay(3); TimerConfigure(WTIMER1_BASE,TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP | TIMER_CFG_A_PERIODIC | TIMER_CFG_B_CAP_TIME_UP | TIMER_CFG_B_PERIODIC); TimerControlEvent(WTIMER1_BASE, TIMER_BOTH, TIMER_EVENT_NEG_EDGE); A_Edge = Falling; B_Edge = Falling; TimerIntRegister(WTIMER1_BASE, TIMER_A, WTimer1AIntHandler); TimerIntRegister(WTIMER1_BASE, TIMER_B, WTimer1BIntHandler); TimerIntEnable(WTIMER1_BASE, TIMER_CAPA_EVENT | TIMER_CAPB_EVENT); IntEnable(INT_WTIMER1A); IntEnable(INT_WTIMER1B); IntMasterEnable(); TimerControlStall(WTIMER1_BASE, TIMER_BOTH, true); TimerEnable(WTIMER1_BASE, TIMER_BOTH); while(1) { } } void WTimer1AIntHandler() { if(A_Edge == Falling) { A_FallingEdge = TimerValueGet(WTIMER1_BASE, TIMER_A); A_HighPeriod = A_FallingEdge - A_RisingEdge; // Change timer A to rising edge HWREG(WTIMER1_BASE + TIMER_O_CTL) &= ~0x0000000C; A_Edge = Rising; } else { A_RisingEdge = TimerValueGet(WTIMER1_BASE, TIMER_A); A_LowPeriod = A_RisingEdge - A_FallingEdge; // Change timer A to falling edge HWREG(WTIMER1_BASE + TIMER_O_CTL) |= 0x00000004; A_Edge = Falling; } TimerIntClear(WTIMER1_BASE, TIMER_CAPA_EVENT); } void WTimer1BIntHandler() { if(B_Edge == Falling) { B_FallingEdge = TimerValueGet(WTIMER1_BASE, TIMER_B); B_HighPeriod = B_FallingEdge - B_RisingEdge; // Change timer B to rising edge HWREG(WTIMER1_BASE + TIMER_O_CTL) &= ~0x00000C00; B_Edge = Rising; } else { B_RisingEdge = TimerValueGet(WTIMER1_BASE, TIMER_B); B_LowPeriod = B_RisingEdge - B_FallingEdge; // Change timer B to falling edge HWREG(WTIMER1_BASE + TIMER_O_CTL) |= 0x00000400; B_Edge = Falling; } TimerIntClear(WTIMER1_BASE, TIMER_CAPB_EVENT); }
Hi Bob,
Outstanding! Resourceful and well demonstrates different methods and operating objectives.
As Is the case of (most) Tech firms - we seek a 'KNOWN/PROVEN Body of Code' - which is (almost) infinitely re-usable. And supplies various 'hooks' - which guard against 'misuse' - while optimizing performance (as best as possible) under the unique conditions - each time - presented.
The (proper) management of 'special conditions' (such as a Timer over-flow) - or 'distressed' input signal - must also be 'anticipated & safe-guarded.'
As it is mid-summer - and both your firm (and ours) are 'rich in engineering interns' - our exercise of your neat code proves a nice TM4C (123) 'intro' for a 'gifted' few.
In particular: your 'does not cover the cases' where:
Indeed your code IS resourceful & clever - yet my crüe may want to (slightly) modify:
We applaud your use of the MCU's (other) Timer (as a PWM Signal Generator) - which proves most efficient (and infinitely controllable/programmable) and (just maybe) - has been (past) suggested...