Stange Timer Reset Behavior XC8 1.34 PIC18f4520 - Page 1
Forum section: electronics / beginners

author: FrenchRoast
author_rank: Newbie
timestamp: June 28, 2015, 03:28:28 pm
content: Hello,I'm trying to create a low frequency PWM signal for servo motor control using software and timer0. I've hit a snag that i do not understand and was hoping someone here could make sense of it.My plan was use Timer0 as 16 bit with no prescaler and an 8MHZ clock. using this setup and a preloaded timer value of 25535 produces a 20ms delay. Using that as my reference, i could calculate the appropriate preload values for the pulse width i wanted while keeping a total period of 20ms. it worked!..... in theory.here is the codevoid pwMod(uint16_t period, uint16_t PW){uint16_t highTime = 0;uint16_t lowTime = 0;highTime = 55535;lowTime = 35535;MOTOR=HIGH;INTCONbits.TMR0IF = 0;TMR0 = 55535;//TMR0 = highTime;while(!INTCONbits.TMR0IF);MOTOR=LOW;INTCONbits.TMR0IF = 0;TMR0 = 35535;//TMR0 = lowTime;while(!INTCONbits.TMR0IF);}Basically, i set the output pin high, preload my timer for 5ms, then wait for overflow. Then set the output low, preload for 15ms and wait for overflow. Dirt simple.This code produces one cycle of a 50Hz 25% duty cycle PWM signal. BUT, only if the preloaded values are loaded as fixed values. If i assign the same delay values into the variables highTime and lowTime and write them to Timer0. (as shown in the comments) The code produces a 50Hz 75% duty cycle PWM signal. Exactly the opposite of what i wanted.Now, I know I could just flip the delays and get where i want to go. But what is going on here? does anyone know? I'm at a loss as to why using the variables would produce the wrong output.for reference here is my timer setup code. all interrupts are disabled.T0CONbits.TMR0ON = 1; // TMR0 on/offT0CONbits.T08BIT = 0; // configured as 16-bitT0CONbits.T0CS = 0; // Clock source is Fosc/4T0CONbits.PSA = 1; // 1/32 prescaler for use 1 to bypass prescalerT0CONbits.T0PS2 = 0;T0CONbits.T0PS1 = 1;T0CONbits.T0PS0 = 1;

author: RogerRowland
author_rank: Regular Contributor
timestamp: June 28, 2015, 04:04:53 pm
content: Does it do the same if you specifically qualify the numeric literals as unsigned int? For example:Code:highTime = 55535U;lowTime = 35535U;

author: FrenchRoast
author_rank: Newbie
timestamp: June 28, 2015, 06:28:10 pm
content: Hi,i tried adding the specific qualifiers as you suggested. It made no difference. I also confirmed via printf statements and debugging breakpoints that the correct values are being loaded into the variables.Another strange thing, when i change the value highTime to be longer or shorter. The duration of the low period changes accordingly. It's almost like the wrong variable is being loaded into timer.Also, the delays seem to load correctly from the variables if i remove the code for the second delay and just toggle my output. I get a 200Hz 50% duty cycle square wave.I'm beginning to suspect this is some compiler optimization thing.

author: Ian.M
author_rank: Super Contributor
timestamp: June 28, 2015, 06:47:30 pm
content: That's a fairly ugly way of generating 20ms period servo signals. Take a loop at my code for generating Futaba servo signals using a free running Timer 1 and a CCP module. Its jitter free with rock solid timing so long as no other ISR or routine disables interrupts fpr longer than the pulse width.Here's my PIC16F88 version:http://www.microchip.com/forums/FindPost/819452

author: lewis
author_rank: Frequent Contributor
timestamp: June 28, 2015, 06:51:04 pm
content: TMR0 is not a 16 bit register on the PIC18, it is split into TMR0L and TMR0H for the low and high byte of the integer respectively."TMR0" is a placeholder and not an actual register. The compiler recognises this when you assign a constant value to "TMR0", but is not smart enough to do the arithmetic on variables.In my compiler i'd use:Code:TMR0H = hi(highTime);TMR0L = lo(highTime);lo() and hi() strip out the low and high bytes.or alternatvely:Code:TMR0H = (unsigned char)((highTime >> 8) & 0x00FF);TMR0L = (unsigned char)(highTime & 0x00FF);you might not need the "& 0x00FF" bit.

author: miguelvp
author_rank: Super Contributor
timestamp: June 28, 2015, 07:05:32 pm
content: First I'm a bit confused about the times.8MHz is 0.125us per tick right? (1/8000000)So when you use 25535, it's going to need 40000 cycles to overflow and that should be 5ms (40000*0.000125)55535 would be 1.25ms and 35535 would be 3.75msMaybe it takes 4 cycles per timer count, but if you did get 20ms using 25535 then 55535 would indeed be 5ms and 35535 would be 15ms giving your 50Hz 25% duty cycle. Of course not spot on because you'll have some setup times in between plus the function call overhead but that would be in the micro second range so let's ignore that for now.On the problem, I think it's all about MOTOR, it might be inverted logic so when you assign 1 you get 0V and 0 gives you VDD.Now for the precision of the timing, wouldn't it make more sense to use an interrupt on those timers instead of just looping around doing nothing?Here I did look for an interrupt based PWM and got this:http://www.best-microcontroller-projects.com/pic-pwm-interrupt.html

author: FrenchRoast
author_rank: Newbie
timestamp: June 28, 2015, 07:39:18 pm
content: Lewis,You were absolutely correct. I looked over the datasheet and found logic for writes to timer0 and ended up writing up something almost identical to what you suggested. Thanks for the help. I might have thought of that sooner but the fact that it worked with the literals threw me off that trail. I should have just checked the datasheet first.miguelvp,I had originally tried to implement this as interrupt driven as you suggested. It worked, but i made the same error with TMR0 write. But i didn't notice it at the time because i had the logic flipped for toggling the motor output. My motor was also very jittery and i though sending only one pulse might steady things up. Now that i have this figured out i will probably make it interrupt driven again.Ian.M,You are also correct, it is ugly and prone to jitter. But, this whole excursion was an exercise to try and find a way to do the motor control without using the CCP1 module in anyway. I'll take a look at what you posted.Thanks again everyone for you input.

author: Howardlong
author_rank: Super Contributor
timestamp: June 28, 2015, 08:10:57 pm
content: While this has been resolved now, I did breadboard up a device to check this for myself before the explanation came through.Once I'd recreated the scenario, I dropped straight into the dissasembled version, watched TMR0, and sure enough there it was. Light bulb in head thinks 'buffered register', checked the data sheet, yes, high byte is buffered. Time taken once the problem was recreated was under a minute.This probably shows the value of at least a little assembly knowledge. While you don't need to be able to write it, just stepping through it will often give you, or at least point you to the answer.One further point, I just checked it with the MPLAB simulator on MPLAB 8.92 or MPLAB X 3.05 the problem does not occur. While the simiulator has its place, apart from a few isolated use cases it has limited value IMHO.While there is of course value in "reading the data sheet" and the errata, it is not uncommon for these kinds of intricacies to be forgotten. Right now I have seven projects on the go using different PICs, and it's pretty much humanly impossible to remember exactly all these little nuances.But yes, irrespective, CCP is the right peripheral for this.

author: FrenchRoast
author_rank: Newbie
timestamp: June 28, 2015, 08:43:50 pm
content: Thanks for looking into it. Might I ask, How did you pull up the assembly code? I don't recall seeing an obvious way to look at it.

author: Ian.M
author_rank: Super Contributor
timestamp: June 28, 2015, 09:25:26 pm
content: Assuming you are using XC8 (which is likely from the SFR bit naming), simply locate and open the .lst file which will be named the same as the project. It contains an assembler listing of the whole project with the C source as comments.

author: Howardlong
author_rank: Super Contributor
timestamp: June 28, 2015, 10:12:07 pm
content: Quote from: FrenchRoast on June 28, 2015, 08:43:50 pmAdding to Ian.M's note:If you're debugging in MPLAB X, it's in Window->Debugging->Dissassembly. You can also use Window->PIC Memory Views->Program Memory when the disassembly can't find matching source code lines.Similarly, for MPLAB 8.92, you can use View->Dissassembly Listing or View->Program Memory.Once you are in the disassembly listing up and active, you can single step the instructions.You really don't need to know PIC18 assembly language to take advantage of this, as long as the optimiser's off it should appear fairly self-explanatory. When the optimiser's on, it can be a very different matter!