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.

PWM taking Hardfault with Keil v5 TM4C123GH6PM

Other Parts Discussed in Thread: TM4C123GH6PM

I have several programs using PWM module of TM4C123GH6PM (Tiva LaunchPad). These programs were written using Keil v4.72a. They seemed to work fine.

Recently, I opened these projects in Keil v5.12 and they all stop working.

Here is the listing of the simplest program that sets up the PWM1 to generate 1 kHz 50% output at PF3:

1 #include "TM4C123GH6PM.h"
2
3 int main(void)
4 {
5 /* Enable Peripheral Clocks */
6 SYSCTL->RCGCPWM |= 2; /* enable clock to PWM1 */
7 SYSCTL->RCGCGPIO |= 0x20; /* enable clock to PORTF */
8 SYSCTL->RCC &= ~0x00100000; /* no pre-divide for PWM clock */
9
10 /* Enable port PF3 for PWM1 M1PWM7 */
11 GPIOF->AFSEL = 8; /* PF3 uses alternate function */
12 GPIOF->PCTL &= ~0x0000F000; /* make PF3 PWM output pin */
13 GPIOF->PCTL |= 0x00005000;
14 GPIOF->DEN |= 8; /* pin digital */
15
16 PWM1->_3_CTL = 0; /* stop counter */
17 PWM1->_3_GENB = 0x0000008C; /* M1PWM7 output set when reload, */
18 /* clear when match PWMCMPA */
19 PWM1->_3_LOAD = 16000; /* set load value for 1kHz (16MHz/16000) */
20 PWM1->_3_CMPA = 8000; /* set duty cycle to 50% */
21 PWM1->_3_CTL = 1; /* start timer */
22 PWM1->ENABLE = 0x80; /* start PWM1 ch7 */
23
24 for(;;) { }
25 }
26
27 //void SystemInit(void)
28 //{
29 // SCB->CPACR |= 0x00f00000;
30 //}
31

Line 27-30 were commented out because in Keil v5 there is a SystemInit() function in system_TM4C123c that was supplied by the device support package. The other code in the SystemInit() function is to configure the system clock from 16 MHz to 49.84 MHz. I will attach the code at the end of the post.

When I ran this program, there was no output and the program is stuck in the HardFault_Handler. FAULTSTAT was 0x00000400, HFAULTSTAT was 0x40000000, MMADDR was 0xE000EDF8, and FAULTADDR was 0xE000EDF8.

If I put a breakpoint after line 16, I always got the hard fault. If I put a breakpoint at line 16, no fault would occur. I could run the program by hitting continue without any problem from the breakpoint at line 16.

If I added a small delay before line 16 such as:

for(i = 0; i < 6; i++) ;

The program would run fine. If I reduced the loop count to 5, it would take the hard fault.

If I skipped the clock configuration in SystemInit(), the program ran fine without fault.

Here is the code for SystemInit():

void SystemInit (void)
{
#if(CLOCK_SETUP)
uint32_t i;
#endif

/* FPU settings ------------------------------------------------------------*/
#if (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2) | /* set CP10 Full Access */
(3UL << 11*2) ); /* set CP11 Full Access */
#endif

#if(CLOCK_SETUP)
SYSCTL->RCC2 = 0x07802810; /* set default value */
SYSCTL->RCC = 0x078E3AD1; /* set default value */

SYSCTL->RCC = (RCC_Val | (1UL<<11) | (1UL<<13)) & ~(1UL<<22); /* set value with BYPASS, PWRDN set, USESYSDIV reset */
SYSCTL->RCC2 = (RCC2_Val | (1UL<<11) | (1UL<<13)); /* set value with BYPASS, PWRDN set */
for (i = 0; i < 1000; i++); /* wait a while */

SYSCTL->RCC = (RCC_Val | (1UL<<11)) & ~(1UL<<22); /* set value with BYPASS, USESYSDIV reset */
SYSCTL->RCC2 = (RCC2_Val | (1UL<<11)); /* set value with BYPASS */
for (i = 0; i < 1000; i++); /* wait a while */

SYSCTL->RCC = (RCC_Val | (1<<11)); /* set value with BYPASS */

if ( (((RCC_Val & (1UL<<13)) == 0) && ((RCC2_Val & (1UL<<31)) == 0)) ||
(((RCC2_Val & (1UL<<13)) == 0) && ((RCC2_Val & (1UL<<31)) != 0)) ) {
while ((SYSCTL->RIS & (1UL<<6)) != (1UL<<6)); /* wait until PLL is locked */
}

SYSCTL->RCC = (RCC_Val); /* set value */
SYSCTL->RCC2 = (RCC2_Val); /* set value */
for (i = 0; i < 10000; i++); /* wait a while */

#endif
}

The value of RCC_Val is 0x01DE0542 and RCC2_Val is 0x01800000.

  • Hey Shujen Chen,

    This is a common known 'problem' with the TivaC Series. It is already mentioned here by our fellow comrade Amit Ashara under #ISSUE 2.

    The System Control Registers simply need some time to take over the changes before you modify another register. The count of 6 in your for-loop covers the necessary 5 cycles the register needs to take over changes.

    Why that code worked in Keil 4.73 however is puzzling me.

    Regards,

     

    Michel

  • Michel,

    I am not sure what you meant by 'common known problem'. For one, I did not see it in the errata where I believe any known problems should be included whether they are common or not.

    I checked the thread you mentioned by Amit Ashara #issue 2. It says: "The Enable Function writes to the SYSCTL.RCGCxxx Register for the xxx Peripheral. It takes 5 System Clock Cycles after which the peripheral will be addressable."

    Looking at my code, SYSCTL->RCGCPWM was set at line 6 and PWM register access started at line 16. There were 32 instructions between them, much longer than the 5 system clock cycles. I know that it takes time to enable the clock to the peripherals and that was why I inserted the configuration of GPIOF in between. In my experience, one statement was enough for a GPIO (hence line 8). When I was able to get the code running with the for loop delay, the gap between SYSCTL->RCGCPWM set and PWM register access had 49 instructions.

    The main difference I see between Keil v4.7x and Keil v5.1x is the addition of system clock initialization in the system_TM4C123.c SystemInit() function.

    Shujen

     

  • Hey Shujen,

    with 'common known' i was referring to this forum. If it is the problem i think it is, you are not the first one to ask into this direction, that's why i referred to the post of Amit.

    Also, taking into consideration that the clock setup (SystemInit()) is the one 'causing' that problem as well, it is a quite suspicious pointer towards a timing problem between the module initialization and the register accesses. The debugging 'hack' is also adding to it.

    Would be interesting to see if programming it at even a higher Clock (80MHz) would require you to add even more delay in between.

    Does it work in Keil v4.7x with a higher Clock?

    Regards,

     

    Michel

     

  • Michel,

    I am old school. My design process is to read the datasheet and errata. Searching forum for errata is not in my routine and maybe it should be. For this problem, I did search the forum and got this thread:

    http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/p/379194/1334994.aspx#1334994

    but I could not figure out how that was resolved.

    You asked several good questions. I replaced the clock initialization code in Keil system_TM4C123.c SystemInit() function with my own code to configure for 80 MHz. Surprisingly, I don't need any additional delay for PWM to work!

    Here is my 80 MHz initialization code:

    // configure for 80 MHz operation with external 16 MHz
    void sysctlClock_init_ext_80MHz(void)
    {
    SYSCTL->RCC &= ~0x000007C0; // clear xtal freq field
    SYSCTL->RCC |= 0x00000540; // set external xtal to 16 MHz
    SYSCTL->RCC2 |= 0x80000000; // use RCC2 to overwrite RCC
    SYSCTL->RCC2 |= 0x00000800; // bypass PLL during configuration
    SYSCTL->RCC2 &= ~0x00000070; // use main oscillator (16 MHz)
    SYSCTL->RCC2 &= ~0x00002000; // enable PLL
    SYSCTL->RCC2 |= 0x40000000; // set PLL to 400 MHz
    SYSCTL->RCC2 &= ~0x1FC00000; // clear system clock divider field
    SYSCTL->RCC2 |= 0x01000000; // configure for 80 MHz clock
    while(!(SYSCTL->RIS & 0x00000040)) ; // wait for PLL to lock
    SYSCTL->RCC2 &= ~0x00000800; // turn off PLL bypass
    }

     

  • One more piece of information:

    If I put in an empty HardFault_Handler, the program ran just fine.

    Does that mean the hard fault is false positive?

    I am not suggesting that this is the solution.

  • I don't think i understand what you mean with 'false positive', but if you want to say that the HardFault Error is falsely activated, i would strongly disagree.

    These Hard Faults are an Hardware index for something going wrong. It is a safety switch that must not be ignored and there is a good reason it was activated. And in this case it doesn't seem to like the 'early' access on your PWM register.

    I suppose it runs fine with the HardFault ignored because entering into the empty HardFault Routine offers enough time for the Modules to be initiated once you return back after that empty HardFault Handler. This however is only speculative.

  • Michel Raabe said:
    want to say that the HardFault Error is falsely activated, i would strongly disagree.

    Shujen my friend - make that (now) two such - strong disagreements.  Danger lurks...

    Yes you're teaching - and yes Direct Register is more "exotic" - while offering more focused (i.e. "pin-point") control.  Yet there's "price to pay" when one "pushes off" from the safe harbor of Driver Lib. (Yes - sailing this weekend)

    My take - does your Direct Register use properly follow the, "sequence of instructions" imposed by the more encapsulated, equivalent Driver Lib function?  (we've seen such cause/claim victims...)   Quick/dirty/eased test - (temporarily "kill" that beloved DR sequence - substitute Driver Lib function call) bet you issue departs...  (i.e. after you re-order DR sequence)

  • "In medical testing, and more generally in binary classification, a false positive is when a test result indicates that a condition – such as a disease – is present (the result is positive), but it is not in fact present (the result is false), while a false negative is when a test result indicates that a condition is not present (the result is negative), but it is in fact present (the result is true). These are the two kinds of errors in a binary test, and are contrasted with a correct result, either a true positive or a true negative." - Wikipedia

    If the device indicated that there was a hard fault and the fault was ignored then everything worked fine after that, was it really a fault?

    Should I avoid all these direct register access and use only the tried and true driverlib in Tivaware?

    Do the manufacturers only guarantee their devices will properly function with the software they provide? Or should they work according to the datasheet?

    Should education treats microcontrollers as blackbox and the only thing you need to know is the programming interface of driverlib or more specifically the programming interface of Tivaware driverlib? Would a microcontroller course based on Tivaware driverlib prepare the students to deal with other manufacturers' devices and programming support system?

    Just a decade ago, chip manufacturers were charging thousands dollars a seat for a simple C compiler and most of the schools were teaching micros with assembly language. These days, some are still teaching assembly programming and some go all the way to the other extreme to use Arduino. Will you hire a candidate for an embedded design position whose experience was solely based on Arduino?

    Technology is advancing fast and I do not believe I know all the answers to these questions.

  • Shujen Chen said:
    Technology is advancing fast and I do not believe I know all the answers to these questions.

    Clearly - nor do I.

    Made the time/effort to address your expressed issue - it's a "reach" for you to broaden/extend to general policy.

    You appear "not" to appreciate the "sequence" of instructions (or function calls) I offered.  Failure to first "clock" any peripheral will insure hard fault visitation - will it not?  (and that's just one illustration - but proves the point)

    We note you've not attempted the sequence "fix" suggested - I'll "back/forth" with you - but not when suggestion is so quickly rejected/abandoned...  As stands now - your argument is perilously close to, "Reductio ad absurdum" and I attribute that to emotion - not your usual clear, detailed thought...  (No "helper" here caused your issue - we seek only to assist - perhaps extend our understanding in the process...)

  • cb_1,

    I am not sure what I missed in the sequence of instructions. It was pretty much following the datasheet.

    This may be another interesting point of data. I lifted the code using driverlib from Lab15 of the Tiva LaunchPad Workshop. 

    http://processors.wiki.ti.com/index.php/Getting_Started_with_the_TIVA%E2%84%A2_C_Series_TM4C123G_LaunchPad

    The code worked fine if I build it with CCS but causes hard fault if built by Keil MDK v5.12. Again if I put a breakpoint at the first access to registers in PWM1 module or if I inserted a dummy loop to stall for several cycles, the code would work.

    Here is the code:

    #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"
    
    #define PWM_FREQUENCY 55
    
    int main(void)
    {
    	volatile uint32_t ui32Load;
    	volatile uint32_t ui32PWMClock;
    	volatile uint8_t ui8Adjust;
    	ui8Adjust = 83;
    
    	SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);
    	SysCtlPWMClockSet(SYSCTL_PWMDIV_64);
    
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    
    	GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_0);
    	GPIOPinConfigure(GPIO_PD0_M1PWM0);
    
    	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);
    
    	PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0, ui8Adjust * ui32Load / 1000);
    	PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT, true);
    	PWMGenEnable(PWM1_BASE, PWM_GEN_0);
    
    	while(1)
    	{
    	}
    }
    

  • @ Shujen,

    Check the sequence of Assembly instructions generated by the Keil compiler.  (you may wish to compare/contrast between CCS & Keil versions)

    Review of the function call (sequence) as implemented w/in Driver Lib will reveal the proper sequence of function calls.  That's the best place to start - imho...

  • cb1,

    Thanks for the suggestion. I think you just answered a question I had:

    There is still value in teaching direct register access.

  • Shujen Chen said:
    There is still value in teaching direct register access.

    Indeed there is - but as small tech firm owner we cannot "discard" the enhanced: Speed, Ease, Encapsulation & "Passage thru hands/eyes of many" - enjoyed (not) by Direct Register but thru the long tested/proven (in most cases TWare - in ALL cases via StellarisWare) Driver Library!

    Is it not too easy to miss a critical Register bit - or misinterpret some bit's impact - or fail to include a critical, key Register - when employing Direct Register?  (Ans: mais certainement)   Where are the safeties?  (Ans: there are none)  Who has past created - then employed - that exact same Direct Register code - in that exact order?  (Ans: most likely - no one!)  Is not such water, "uncharted?"

    May I suggest a better means (to me) to prepare students for real career success.  Problem solving is uber critical - knowing how/where to find critical data - knowing how to deal w/people - and having some "efficiency" when bringing their tech skills to bear upon an MCU-suitable problem - are of utmost importance.  Of course Direct Register has (some) importance - but knowing how/where to maximize its use may prove more essential for small business. 

    Surely this (and other) vendor's code libraries provide serious, measureable shortcuts - and safety - both missed by Direct Register.  And that's what we - out of necessity - must stress!  Yes familiarity with the key registers is most helpful - but exclusive use of Direct Register will (assuredly) slow/complicate the problem solving process!  (due to the near incessant "paging" through complex Register bit listings - to insure that every single one is fully grasped) - this necessarily "breaking" the normal "flow" of problem thought, analysis and test trials. 

    As with most things in engineering - "Trade Offs" prove very necessary - and while Direct Register has merit - it is very much secondary to Driver Library - when (and where) "real world" software production is required.  (i.e. everywhere!)