In my current project I am developing I recently moved up from using a Freetronics Ether-Ten(328) to a Freetronics Ether-Mega(2560) board.
In my software I make use of a timer library to do two things;
- Generate a 100mSec interrupt for calling a routine,
- and also to generate a PWM signal from the output of a PID control loop.
I use the library to generate a PWM output that is far slower than the standard core PWM as I use it to control a SSR (Solid State Relay).
The SSR is a 240VAC 25 Amp unit and will not operate with the fast built-in PWM.
As part of the 100mSec interrupt routine, I set the PWM value, so in essence, this is as fast as I switch it, PWM with a 100mSec period.
With the 328 board I used the TimerOne Library, and all appeared to be problem free.
Using the 2560 Mega board I shifted to using the TimerThree Library, essentially the same, just catering for the 2560.
TimerOne and TimerThree library Arduino Playground - Timer1
What I am experiencing is the following;
The 100mSec interrupt generated by the timer library works fine, no problems there at all.
But, I do have a problem with the generation of PWM in that after a period, typically a few days it appears to cease generating any PWM output.
This is a real problem for me as the PID control loop that drives the PWM which in turn operates the SSR which is dumping excess energy from my micro-hydro turbine.
My PID setpoint is 27.6Vdc for the battery bank, so when the PWM sigal stops and the PID loop continues trying to drive it, of course my battery volts go up, potentially too high.
I have searched for anyone else having such a problem using the TimerOne / TimerThree libraries, but haven't come across anything.
Below is my main program, main.cpp which is the section that initialises the TimerThree interrupt and attaches a routine as well as sets up the PWM.
Specifically, in the setup section, see the following code;
Timer3.initialize(100000); // initialize timer1, and set a 100 milli second period:
Timer3.pwm(pin_TurbineDump, 0); // pre-initialise the pwm on pin 2 and set for 0% duty cycle:
Timer3.attachInterrupt(timerInterrupt); // attach interrupt to timerInterrupt routine, used for PID and other timing control:
And then near the endof my code where I use the PID value to set it as the PWM value.
The PID output value has already been scaled to fall in the range of 0 to 1023 which is the range of the PWM input.
Timer3.setPwmDuty(pin_TurbineDump, hydroPID.cvPID); // set the pwm with the output of the pid output to control the SSR:
I apologise that the neatness of my original code is destroyed when pasting here, it is all niceand neat in my editor.
/*-------------------------------------------------------------------------
main.cpp
Created on: Sep 19, 2012
Author: Paul Alting van Geusau
*/
#define WEBDUINO_FAVICON_DATA ""
#define serialDebug true
#include "main.h"
#include "Ethernet.h"
#include "EthernetUdp.h"
#include "TimerThree.h"
#include "Local_files/sensor.h"
#include "Local_files/logData.h"
#include "Local_files/ntpClient.h"
#include "Local_files/pidControl.h"
#include "Local_files/serverCmds.h"
// const type variable = value; comment
const uint8_t pin_vTurbine = 0; // analog 0
const uint8_t pin_iTurbine = 1; // analog 1
const uint8_t pin_vBattery = 2; // analog 2
const uint8_t pin_iBattery = 3; // analog 3
const uint8_t pin_TurbineDump = 2; // PWM from OCR3B
const uint8_t pin_sdSelect = 4;
const uint8_t pin_TurbineDumpLed = 8; // LED to signal PID driving PWM
const uint8_t pin_CSether = 10;
const uint8_t pin_SPI_CS = 53;
boolean firstScan = true;
uint32_t sdLogCount = 0; // increental count of records written to sdRecordLog:
int16_t rtcTimeTrigger = 9; // incremented by TimerOne interrupt:
boolean ntpPacketSent = false;
uint8_t mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE}; // MAC address, default is {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}:
uint8_t ip[] = {192,168,1,9}; // IP network address:
//-------------------------------------------------------------------------
// void setup()
//
void setup() {
Serial.begin(115200);
pinMode(pin_vTurbine, INPUT);
pinMode(pin_iTurbine, INPUT);
pinMode(pin_vBattery, INPUT);
pinMode(pin_iBattery, INPUT);
pinMode(pin_TurbineDumpLed, OUTPUT);
pinMode(pin_TurbineDump, OUTPUT);
pinMode(pin_CSether, OUTPUT); // set ethernet CS as output:
pinMode(pin_SPI_CS, OUTPUT); // set ATMega-2560, pin 53 hardware cs pin output, even if un-used:
digitalWrite(pin_CSether, HIGH); // set CS high to disable ethernet spi until sd has been initialised:
sdBegin(); // call external begin to initialise SD card ready for use:
delay(250); // allow time for the ethernet cpu to come alive out of reset:
Ethernet.begin(mac, ip); // initialise the ethernet library:
webserverBegin(); // call exteranl begin to start webserver:
serverAddCommands(); // add webserver URL commands:
udpStart(); // start UDP service for getting NTP formatted time for software RTC:
Timer3.initialize(100000); // initialize timer1, and set a 100 milli second period:
Timer3.pwm(pin_TurbineDump, 0); // pre-initialise the pwm on pin 2 and set for 0% duty cycle:
Timer3.attachInterrupt(timerInterrupt); // attach interrupt to timerInterrupt routine, used for PID and other timing control:
}
//-------------------------------------------------------------------------
// void loop()
//
void loop() {
if (!ntpPacketSent) {
if (firstScan || rtcDayOns) { // if firstScan or if '00:00:00':
ntpPacketSend(); // send an NTP packet to a time server:
ntpPacketSent = true;
}
}
if (ntpPacketSent && ntpGetRequest()) {
rtcTime = ntpTime;
Serial.print(F("Time :"));
Serial.println(rtcTime);
ntpPacketSent = false;
// TODO write file system log that rtcTime was update successfully
}
if (rtcHourOns) { // check for hour rollover:
sdRecordLog();
sdLogCount ++;
vStatSensor(vacHydro, true, rtcDayOns); // reset and calculate hourly stats:
iStatSensor(idcHydro, true, rtcDayOns, rtcWeekOns);
vStatSensor(vdcSolar, true, rtcDayOns);
iStatSensor(idcSolar, true, rtcDayOns, rtcWeekOns);
vStatSensor(vdcBattery, true, rtcDayOns);
iStatSensor(idcBusInv, true, rtcDayOns, rtcWeekOns);
iStatSensor(idcBusAux, true, rtcDayOns, rtcWeekOns);
rtcHourOns = false;
rtcDayOns = false;
rtcWeekOns = false;
// TODO write to system log the sdRecordLog what was reset
}
processConnection(); // process any incoming Ethernet connection request:
firstScan = false;
}
//-------------------------------------------------------------------------
// void timerInterrupt()
//
void timerInterrupt() {
if (rtcTimeTrigger > 0) { // take care on software rtc:
--rtcTimeTrigger;
}
else {
++rtcTime;
rtcTimeTrigger = 9;
rtcGetDateTime(rtcTime); // update rtc date and time:
vStatSensor(vacHydro, false, false); // calculate maximum and minimum stats, no reset:
iStatSensor(idcHydro, false, false, false);
vStatSensor(vdcSolar, false, false);
iStatSensor(idcSolar, false, false, false);
vStatSensor(vdcBattery, false, false); // calculate maximum and minimum stats, no reset:
iStatSensor(idcBusInv, false, false, false);
iStatSensor(idcBusAux, false, false, false);
}
readSensor(vacHydro); // read analog sensors:
idcHydro.pv = 300; // XXX test 20 / 1023 * 300 = 5.865 Amps
// readSensor(idcHydro);
vdcSolar.pv = 0; // XXX test
idcSolar.pv = 0; // XXX test
// readSensor(vdcSolar);
// readSensor(idcSolar);
readSensor(vdcBattery);
readSensor(idcBusInv);
idcBusInv.pv = 0; // XXX test
readSensor(idcBusAux);
idcBusAux.pv = 0; // XXX test
hydroPID.pvPID = vdcBattery.pv; // pass the battery value into the PID data block:
controlPID(hydroPID); // call PID loop for dumpload of AC from turbine into heating element:
if (hydroPID.cvPID > 0) {
loadStateHydro = true; // code for displaying load state on web and on led:
}
else {
loadStateHydro = false;
}
digitalWrite(pin_TurbineDumpLed, loadStateHydro);
Timer3.setPwmDuty(pin_TurbineDump, hydroPID.cvPID); // set the pwm with the output of the pid output to control the SSR: *** problem appears to be here***
}
//------------------------------------------------------------------------------------------------------
//
//void softReset() {
// asm volatile (" jmp 0");
//}
I would appreciate any advice or clues that can help me sort this one out?
Paul