Tasmania - Australia
Offline
Sr. Member
Karma: 7
Posts: 252
|
 |
« Reply #15 on: December 12, 2012, 09:32:36 pm » |
Well, I finnaly caught my Arduino out not sending PWM from the TimerThree library again, and have captured from the web page the values I was watching for TimerThree. What is different is the value of TCCR3A, which normally has a value of 32 when the PWM is working. This is the first time I have noticed it to be zero while also noticing no PWM output when there is supposed to be. So, even though I am seeing the counter clocking over, TCNT3 and the values in the output compare register OCR3B being updated, the problem is that somehow the value of TCCR3A has changed and is now zero rather than 32. Now, every 100mSec I make a call to a TimerThree function, the PWM function, and this is where TCCR3A is able to be changed, as follows; void TimerThree::pwm(char pin, int duty, long microseconds) // expects duty cycle to be 10 bit (1024) { if(microseconds > 0) setPeriod(microseconds); // sets data direction register for pwm output pin // activates the output pin if(pin == 5) { DDRE |= _BV(PORTE3); TCCR3A |= _BV(COM3A1); } if(pin == 2) { DDRE |= _BV(PORTE4); TCCR3A |= _BV(COM3B1); } // I use this pin if(pin == 3) { DDRE |= _BV(PORTE5); TCCR3A |= _BV(COM3C1); } setPwmDuty(pin, duty); start(); }
As you can see, I use pin 2, and TCCR3A is being bitwised OR'd with _BV(COM3B1) And I believe this may well be the source of the problem. Below is a screen capture of the Timer values as I see them in real-time on my web page .  I don't know what is causing the this problem yet, though I am certain if I can follow TCCR3A and _BV(COM3B1) back further I may find the route cause. Just to note: when I initialise the TimerThree at the start using the built-in initialise function, TCCR3A is set to zero, like thus; void TimerThree::initialize(long microseconds) { TCCR3A = 0; // clear control register A TCCR3B = _BV(WGM13); // set mode as phase and frequency correct pwm, stop the timer setPeriod(microseconds); }
So, I am guessing the problem may well be to do with _BV(COM3B1), which I don't yet know what that is or does until I dig deeper. Any thoughts or suggestions? Paul
|
|
|
|
|
Logged
|
|
|
|
|
nr Bundaberg, Australia
Online
Tesla Member
Karma: 71
Posts: 6815
Scattered showers my arse -- Noah, 2348BC.
|
 |
« Reply #16 on: December 12, 2012, 11:27:05 pm » |
TCCR3A is being bitwised OR'd with _BV(COM3B1) I bitwise OR can never force something to 0, so both those expressions must already have been 0. But how can _BV(COM3B1) ever be 0? What is the value of COM3B1, if it's > 7 the bit will be off the end and not used. ______ Rob
|
|
|
|
|
Logged
|
|
|
|
|
Tasmania - Australia
Offline
Sr. Member
Karma: 7
Posts: 252
|
 |
« Reply #17 on: December 13, 2012, 04:33:13 am » |
Hey Rob and others, so both those expressions must already have been 0.
OK, right, so if I see that the value of TCCR3A is 32 and all that is done to it is a compound bitwise OR with _BV(COM3B1) as on the following line; if(pin == 2) { DDRE |= _BV(PORTE4); TCCR3A |= _BV(COM3B1); }
Then how does it get back to being zero and stopping the PWM output on pin 2? I don't yet know what _BV(COM3B1) is? I know that when I hover over it in Eclipse IDE it tells me it is a 2 step macro expression; 1. #define COM3B1 5 2. (1 << (5)) Would someone be able to explain this to me, or help me find some resource for it. Would or could this be the source of the problem or do I need to look elsewhere? Paul
|
|
|
|
« Last Edit: December 13, 2012, 04:36:02 am by rockwallaby »
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #18 on: December 13, 2012, 04:44:52 am » |
In the datasheet for the Atmega2560:  You can see that for register TCCR3A the "bit" called COM3B1 is bit 5 (the 6th one across, it is bit 5 if you start counting at zero). So your expression: 1 << 5 ... gives you that particular bit. If you "or" that into the register you have set that bit.
|
|
|
|
|
Logged
|
|
|
|
|
nr Bundaberg, Australia
Online
Tesla Member
Karma: 71
Posts: 6815
Scattered showers my arse -- Noah, 2348BC.
|
 |
« Reply #19 on: December 13, 2012, 05:20:05 am » |
_BV is just a macro as you've found, so the end result of that is
TCCR3A |= 32;
because COM3B1 is 5 and 1<<5 = 32. Therefore TCCR3B was either 0 or 32 before the operation.
If it was 0 it becomes 32, if it was 32 it stays 32.
Any other value and it becomes some value > 32, but it can never be 0 after that OR.
So how does it become 0? Something must be screwing with it elsewhere, or maybe your reporting it at the wrong time/place.
______ Rob
|
|
|
|
|
Logged
|
|
|
|
|
Tasmania - Australia
Offline
Sr. Member
Karma: 7
Posts: 252
|
 |
« Reply #20 on: December 13, 2012, 05:22:20 am » |
Thanks so much for that Nick, you have explained that wonderfully and I can now understand that better. So, shifting that bit 5 places gives the value of 32 I normally see in this TCCR3A register. I didn't realise that what I was looking at pointed back to a bit position within that register. So, just to summarise my program flow around the use of this TimerThree function. Firstly, I have removed the TimerThree generated 1000mSec interrupt used to call a routine. That interrupt has been replaced with a standard 1000mSec millis() time check, which works fine. In my setup() I do the following; Timer3.initialise(100000); // initialise timer 3 and set for a 100mSec period Timer3.pwm(pin_TurbineDumpPWM, 0); // pre-initialise the pwm on pin 2 and set for 0% duty cycle
Nick, I need to do that second step to setup the correct operating mode for pwm on specified pin, (you did question my use of it in an earlier post) When you call the Timer3.pwm, that is where DDRE and TCCR3A get set. Then in my routine that gets called each 100mSec I do the following; Timer3.setPwmDuty(pin_TurbineDumpPWM, hydroPID.cvPID)
In the above routine, DDRE and TCCR3A are not re-set at all, and it is assumed that they will hold their initial values from the initial setup function This function is called by Timer3.pwm and from the Timer3 documentation it states the following; setPwmDuty(pin, duty) A fast shortcut for setting the pwm duty for a given pin if you have already set it up by calling pwm() earlier. This avoids the overhead of enabling pwm mode for the pin, setting the data direction register, checking for optional period adjustments etc. that are mandatory when you call pwm()
Rob, just noticed your post, yes, what you are saying is clear, I can understand that. But still the problem.. Paul
|
|
|
|
|
Logged
|
|
|
|
|
Tasmania - Australia
Offline
Sr. Member
Karma: 7
Posts: 252
|
 |
« Reply #21 on: December 13, 2012, 05:43:32 am » |
Rob, or maybe your reporting it at the wrong time/place.
To check the various values of Timer3 I have inserted the following debugs in the Timer3.setPwmDuty(..) function. The very function I loop through each 100mSec I know the values are continuously updated as the TCNT3 counts, as do the actual PWM as set and is seen in both variables 'dutyCycleDebug' and 'OCR3BDebug' void TimerThree::setPwmDuty(char pin, int duty) { unsigned long dutyCycle = pwmPeriod; dutyCycle *= duty; dutyCycle >>= 10; if(pin == 5) OCR3A = dutyCycle; if(pin == 2) OCR3B = dutyCycle; if(pin == 3) OCR3C = dutyCycle;
dutyCycleDebug = dutyCycle; // XXX debug OCR3BDebug = OCR3B; // XXX debug ICR3Debug = ICR3; // XXX debug TCCR3ADebug = TCCR3A; // XXX debug TCCR3BDebug = TCCR3B; // XXX debug TCNT3Debug = TCNT3; // XXX debug
}
Check out the 'Alarms' page and you can see them all ticking away nicely. When the problem hits, those values still tick away, except that TCCR3ADebug which is equated to TCCR3A has been set to zero. Reset the board and away it goes for another few days. Something must be screwing with it elsewhere Somewhere, somehow......... I'm sure someone wiser than me mentioned to me today Ain't programming fun? , some grayish nomad from up north Paul
|
|
|
|
|
Logged
|
|
|
|
|
Tasmania - Australia
Offline
Sr. Member
Karma: 7
Posts: 252
|
 |
« Reply #22 on: December 13, 2012, 06:40:47 am » |
I checked for the occurence of TCCR3A being used elsewhere in the project, and this is the result I get with the reference check;  There are 8 matches, all of which are in the Timer3 library. You will notice in the image the function disablePwm(..), where TCCR3A bits are cleared, but this function is not called in my program. In the search section, line 26 is part of the Timer3 initialise function. There's some smokey black magic happening in there somewhere..... Paul
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #23 on: December 13, 2012, 04:10:38 pm » |
Can you please assemble a sketch that just demonstrates your problems with the Timer 3 PWM output? Then I can test that and check what is really happening. Thanks.
|
|
|
|
|
Logged
|
|
|
|
|
Tasmania - Australia
Offline
Sr. Member
Karma: 7
Posts: 252
|
 |
« Reply #24 on: December 13, 2012, 06:44:42 pm » |
From my main program I have cut out the components that relate specifically to TimerThree. /*------------------------------------------------------------------------- Timer3_test.cpp
Created on: Dec 14, 2012 Author: Paul Alting van Geusau A very simple sketch to check the operation of the TimerThree Library for reliable and continuous PWM operation. Step 1. initialise the Timer3 for correct mode, PWM on pin 2 with a period of 100 milli second period Step 2. call a function every 100 milli seconds to call the Timer3.setPwmDuty(..) function
*/
#include "timer3_test.h" #include "TimerThree.h"
// const type variable = value comment const uint8_t pin_PWM = 2; // PWM from OCR3B uint32_t lastIntTime = 0; // last time through the timerInterrupt loop: const uint32_t intPeriod = 100; // delay period to call timerInterrupt loop:
//------------------------------------------------------------------------- // void setup() // void setup() { // Serial.begin(115200);
pinMode(pin_PWM, OUTPUT); // configure pin 2 for output:
Timer3.initialize(100000); // initialize Timer3, and set a 100 milli second period: Timer3.pwm(pin_PWM, 0); // pre-initialise the pwm on pin 2 and set for 0% duty cycle: }
//------------------------------------------------------------------------- // void loop() // void loop() { if (millis() - lastIntTime >= intPeriod) { // is it time to call the process100mS routine: process100mS(); lastIntTime = millis(); } }
//------------------------------------------------------------------------- // void process100mS() // Instead of PID output, we just give it a steady value of 512 for the test // void process100mS() { Timer3.setPwmDuty(pin_PWM, 512); // set the pwm with the output of the pid output to control the SSR: }
And header file #ifndef Timer3_test_ #define Timer3_test_ #include "Arduino.h"
// function definitions for the project void loop(); void setup(); void process100mS();
#endif /* Timer3_test_ */
Edit: remember that I have also made a small change in the TimerThree library in the function setPeriod(..) as mentioned earlier and as follows; void TimerThree::setPeriod(long microseconds) { // (F_CPU * microseconds) / 2000000 was original but changed to (F_CPU / 2000000) * microseconds due to potentail overflow condition. long cycles = (F_CPU / 2000000) * microseconds;
// rest of function
}
Note: I am using the Freetronics Ether-Mega, so a 2560 based system. Paul
|
|
|
|
« Last Edit: December 13, 2012, 07:09:37 pm by rockwallaby »
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #25 on: December 13, 2012, 07:33:08 pm » |
I ran this slightly modified sketch (added a serial print): /*------------------------------------------------------------------------- Timer3_test.cpp
Created on: Dec 14, 2012 Author: Paul Alting van Geusau A very simple sketch to check the operation of the TimerThree Library for reliable and continuous PWM operation. Step 1. initialise the Timer3 for correct mode, PWM on pin 2 with a period of 100 milli second period Step 2. call a function every 100 milli seconds to call the Timer3.setPwmDuty(..) function
*/
#include "TimerThree.h"
// const type variable = value comment const uint8_t pin_PWM = 2; // PWM from OCR3B uint32_t lastIntTime = 0; // last time through the timerInterrupt loop: const uint32_t intPeriod = 100; // delay period to call timerInterrupt loop:
//------------------------------------------------------------------------- // void setup() // void setup() { Serial.begin(115200); Serial.println ("Starting sketch ..."); pinMode(pin_PWM, OUTPUT); // configure pin 2 for output:
Timer3.initialize(100000); // initialize Timer3, and set a 100 milli second period: Timer3.pwm(pin_PWM, 0); // pre-initialise the pwm on pin 2 and set for 0% duty cycle: }
//------------------------------------------------------------------------- // void loop() // void loop() { if (millis() - lastIntTime >= intPeriod) { // is it time to call the process100mS routine: process100mS(); lastIntTime = millis(); } }
//------------------------------------------------------------------------- // void process100mS() // Instead of PID output, we just give it a steady value of 512 for the test // void process100mS() { Timer3.setPwmDuty(pin_PWM, 512); // set the pwm with the output of the pid output to control the SSR: }
I didn't bother with the .h file. I modified Timer3 as you showed. This is the result:  That's correct isn't it? 100 mS for one full pulse, 50% duty cycle. That was on an Arduino Mega2560 board.
|
|
|
|
|
Logged
|
|
|
|
|
Tasmania - Australia
Offline
Sr. Member
Karma: 7
Posts: 252
|
 |
« Reply #26 on: December 13, 2012, 07:42:50 pm » |
That's correct isn't it? 100 mS for one full pulse, 50% duty cycle. That was on an Arduino Mega2560 board.
Nick, yes that is correct, the period is 100mSec, with a 50% duty cycle as we are feeding 512 into Timer3.setPwmDuty(..) On an Arduino 2560 board. Paul
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #27 on: December 13, 2012, 08:00:24 pm » |
Right, so to save me re-reading everything above, the problem is ...?
|
|
|
|
|
Logged
|
|
|
|
|
Tasmania - Australia
Offline
Sr. Member
Karma: 7
Posts: 252
|
 |
« Reply #28 on: December 13, 2012, 08:12:28 pm » |
Yes, I can understand the thread is getting longer and easy to loose the issue I am having.
Importantly After a period of time my Arduino stops providing the pwm wiggling on pin 2. Even though I still call a function to do so every 100mSec, though it should continue regardless of this call. All other functions in my program continue to operate normally when this problem is present.
In this test we provide a static value of 512 but on my board, this value will range over 0 to 1023 depending on my PID loop controller, hence the 100mSec update. The period before the failure occurs can vary, I have found it to be typically one to three days, apparently no set period.
For example I reset my board last night and awoke this morning to find no pwm happening when there should have been, my battery bank hit 30Vdc.
Many thanks Nick for your assistance.
Paul
|
|
|
|
« Last Edit: December 13, 2012, 08:17:09 pm by rockwallaby »
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #29 on: December 13, 2012, 09:37:03 pm » |
Well, can you reproduce this? Try different values. In particular, extreme ones (eg. 0, 1023 or close to that).
|
|
|
|
|
Logged
|
|
|
|
|
|