I built an LED array with 3W Cree LEDs and Meanwell LDD-H drivers. These drivers take a 5V PWM input and send it right along to the LEDs by switching the full current rating of the driver on and off with the PWM signal. I have a Neptune Systems Apex aquarium controller which doesn't use PWM for dimming. It uses 0-10V analog voltage for a different type of driver.
A guy on the Reef Central forums built a board with an Atmega328, integrated voltage dividers (to switch it to 0-5V input), a crystal, and terminal blocks to hook up the analog input wires and PVM output wires. It's programmable using the Uno type setting in the Arduino IDE, and I've been trying to get a code working on it which would give good response rates to the analog dimming signal. It basically works, but I'm having a problem at low duty cycles. Basically anything under 5% jitters quite a bit as it switches between different PWM "channels." It probably happens at higher duty cycles as well, but the difference between 1 and 2 is way more noticeable than the difference between 51 and 52.
Below is the code that I'm trying. It's really simple. Is there a better way to do a smooth that's still responsive to changes in input voltages? Right now I'm just doing a rolling average, which is surely the naive way to do it. I could use some pointers to do this right,and I haven't found much in my searching. Seems like it's kind of a signal processing thing that must happen all the time with LED dimming.
int inPins[] = {A0, A1, A2, A3}; // input pins
int outPins[] = {10,9,6,5}; // output pins
const int numReadings = 100; // how many previous values do we want to average?
int readings[4][numReadings]; // the readings from the analog input
unsigned int totals[] = {0,0,0,0}; // array of totals per channel.
int average; // average value
void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600);
}
// This will loop forever. Note that I don't zero out the contents of the "totals" array between iterations
// of the loop function, so it's really a running average where after a step up in voltage each new reading
// bumps up the average a bit and the totals array should be full of new values within a second
// (10ms * 4 channels * 100 elements in totals). This should cause a ramp of about 4 seconds for any step
// changes in input voltage, but should also be enough elements in the totals array to even out any jitter. No
// lightning effect with this, but who really wants to scare their fish?
void loop() {
for (int count = 0; count < numReadings; count++) { // average over numReadings to avoid jitter
for (int c = 0; c < 4; c++) { // loop through all 4 channels
totals[c] -= readings[c][count]; // take old value off the stack
readings[c][count]=map(analogRead(inPins[c]),0,1023,0,255); // read the new value, map it and store it in the readings array
totals[c] += readings[c][count]; // put new value on the stack of totals
average = totals[c]/numReadings; // calculate the average for output
if ((average > 0) && (average < 4)) { // bin the readings at the lowest level so we can actually get to 1
average=1; // on the PWM since 1 on Apex = 2.55 in PWM, 5 in PWM is 2 in Apex
} // we're skipping some PWM values this way, but 1 and 2 on the Apex are stable, 3 jitters a bit, but it settles down after that.
analogWrite(outPins[c],average); // send the average out to the pin.
delay(10); // delay to let the ADC to stabilize could probably do less than this, but I like the ramp with this level of delay
}
}
}