Hi all, I've been having a blast learning how to do bit angle modulation and wrote some code which works great adjusting the brightness of 1 led but it is not working so well when I extended it to support more.
My first attempt controlling the pin 13 led is below and it works great, adjusting the brightness up and down with 4bit precision (0 - 15 brightness levels where 0 is completely off). It took me awhile to write and understand so it has a lot of comments describing exactly what is happening.
#define PIN_LED 13
byte _bitMask;
byte _ledBrightness;
byte _increaseBrightness = true;
unsigned long _currentMillis, _lastUpdateMillis;
void setup() {
pinMode(PIN_LED, OUTPUT);
}
void loop() {
_currentMillis = millis();
// Once every 250 milliseconds, change the led brightness.
// It starts at 0 and goes up to 15, then back down to 0, and repeats.
if (_currentMillis - _lastUpdateMillis > 249) {
_lastUpdateMillis = _currentMillis;
if (_ledBrightness == 0) _increaseBrightness = true;
else if (_ledBrightness == 15) _increaseBrightness = false;
if (_increaseBrightness)
_ledBrightness++;
else
_ledBrightness--;
}
bitAngleModulationHandler();
}
void bitAngleModulationHandler() {
_bitMask++;
if (_bitMask > B1111) _bitMask = B0001; // Ensure mask goes from 1 to 15
// if led brightness = 10 (binary B1010)
if (_bitMask == B0001) { updateLeds(); } // cycle 1 led off
else if (_bitMask == B0010) { updateLeds(); } // cycle 2 led on
// B0011 cycle 3 led unchanged, still on
else if (_bitMask == B0100) { updateLeds(); } // cycle 4 led off
// B0101 cycle 5 led unchanged, still off
// B0110 cycle 6 led unchanged, still off
// B0111 cycle 7 led unchanged, still off
else if (_bitMask == B1000) { updateLeds(); } // cycle 8 led on
// B1001 cycle 9 led unchanged, still on
// B1010 cycle 10 led unchanged, still on
// B1011 cycle 11 led unchanged, still on
// B1100 cycle 12 led unchanged, still on
// B1101 cycle 13 led unchanged, still on
// B1110 cycle 14 led unchanged, still on
// B1111 cycle 15 led unchanged, still on
// So out of the available 15 cycles, a brightness of 10 sets the
// led to be on for 10 of them.
}
void updateLeds() {
// if _bitMask = 0001 and _ledBrightness = 1010 (the number 10)
// bitwise and = (0001 & 1010) = (0000) > 0 = false
bool isEnabled = (_bitMask & _ledBrightness) > 0;
digitalWrite(PIN_LED, isEnabled);
}
After finally getting this to work I thought I had it all figured out and extended it to support an array of led's, but the brightness levels no longer smoothly control the leds, instead their brightness hop around a bit as they go up and down the 0-15 brightness scale. I thought maybe it was a refresh rate problem but it's refreshing over 2500 Hz (I serial print this), so it must be something else. I've hit a wall and wondered if a second pair of eyes might see what I'm doing wrong. The circuit to test this is simple, just hooking up leds to pins 5-10. So if you're interested, any help would be appreciated. If you hook it up you should see the brightness start at 0 on all 6 leds, but the brightness will go up a couple notches, then go down 1, then go up a few more, then down 1, and so on. Same on the way back down, it just won't smoothly adjust the brightness up and down anymore.
// Each element in the array follows the format { PIN, BRIGHTNESS, BRIGHTNESS_DIRECTION }
// PIN is the Arduino digital pin number
// BRIGHTNESS is 0-15
// BRIGHTNESS_DIRECTION is 0 or 1, where 1 means we should increase the brightness
byte _ledArray[][3] = {
{ 5, 0, 1 },
{ 6, 0, 1 },
{ 7, 0, 1 },
{ 8, 0, 1 },
{ 9, 0, 1 },
{ 10, 0, 1 }
};
byte _ledCount;
byte _bitMask;
unsigned long _currentMillis, _lastUpdateMillis;
unsigned long _refreshRate;
unsigned long _lastRefreshRateMillis;
void setup() {
Serial.begin(115200);
// Determine number of led's in the array.
_ledCount = sizeof(_ledArray) / 3; // each array element is 3 bytes
// Set all the led pins to be outputs.
for (byte led = 0; led < _ledCount; led++) {
pinMode(_ledArray[led][0], OUTPUT); // Array index 0 = the Arduino pin number.
}
}
void loop() {
_currentMillis = millis();
// Once every 250 milliseconds.
if (_currentMillis - _lastUpdateMillis > 249) {
_lastUpdateMillis = _currentMillis;
// Loop through the led's, either increasing or decreasing
// their brightness setting.
for (byte led = 0; led < _ledCount; led++) {
byte ledBrightness = _ledArray[led][1];
byte ledBrightnessDirection = _ledArray[led][2];
if (ledBrightness == 0) ledBrightnessDirection = 1;
else if (ledBrightness == 15) ledBrightnessDirection = 0;
if (ledBrightnessDirection)
ledBrightness++;
else
ledBrightness--;
_ledArray[led][1] = ledBrightness;
_ledArray[led][2] = ledBrightnessDirection;
}
}
// Switch led's on/off based on their brightness.
bitAngleModulationHandler();
// Keep track of the refresh rate and show it once every second.
// For an array of 6 led's, refresh rate is around 2560 Hz.
_refreshRate++;
if (_currentMillis - _lastRefreshRateMillis > 999) {
_lastRefreshRateMillis = _currentMillis;
Serial.println(_refreshRate / 16); // 16 cycles to perform a full refresh
_refreshRate = 0;
}
}
void bitAngleModulationHandler() {
_bitMask++;
if (_bitMask > B1111) _bitMask = B0001; // Ensure mask goes from 1 to 15
if (_bitMask == B0001) { updateLeds(); }
else if (_bitMask == B0010) { updateLeds(); }
else if (_bitMask == B0100) { updateLeds(); }
else if (_bitMask == B1000) { updateLeds(); }
}
void updateLeds() {
for (byte led = 0; led < _ledCount; led++) {
digitalWrite(_ledArray[led][0], (_bitMask & _ledArray[led][1]) > 0);
}
}