/*
atmega_i2c_pwm_expander.pde
21.11.2009
Jan Krause
Based on PWMallPins by Paul Badger.
This library turns an ATmega8, ATmega168 or ATmega328 (and probably many more) into a PWM IC, that can be controlled by the famous I²C bus.
The ATmega8 is the cheapest so I used this one.
With this code loaded up to your chip you can drive up to 18 leds in 255 brightnes steps for every led.
I compiled the code with the Arduino software(avr-gcc), but it might also work with an other compiler.
Wiring:
_______
(RESET) =| |_| |= SCL
LED 0 =| |= SDA
LED 1 =| |= LED 17
LED 2 =| |= LED 16
LED 3 =| A |= LED 15
LED 4 =| T |= LED 14
+5V =| m |=
GND =| e |=
OSC1 =| g |= +5V
OSC2 =| a |= LED 13 (SCL)
LED 5 =| 8 |= LED 12 (MISO)
LED 6 =| |= LED 11 (MOSI)
LED 7 =| |= LED 10
LED 8 =|_____|= LED 9
To use the chip run a code, looking like the following, on the I²C master:
void writeLEDs()
{
Wire.beginTransmission(DEVICE_ADDR); //DEVICE_ADDR is the address you set in the PWM IC code (in this example its B00000100 = 4)
Wire.send(0); //No. of the led you want to start with setting
for (int i = 0; i < 18; i++) {
Wire.send(LEDs[i]); //LEDs[i] is an array holding the values of brigthnes
}
Wire.endTransmission();
}
*/
#include <Wire.h>
byte DEVICE_ADDR = B00000100;
int pwmVal[18];
int i, x;
byte PORTD_first_state = B00000000;
byte PORTB_first_state = B00000000;
byte PORTC_first_state = B00000000;
void setup()
{
Wire.begin(DEVICE_ADDR); //join i2c bus as slave
Wire.onReceive(receiveEvent); //register event
DDRD = 0xFF; //set port 0 to 7 as output
DDRB = 0xFF; //set port 8 to 13 as output
DDRC = 0x0F; //set port 14 to 17 as output (ADC ports)
for (i = 0; i<18; i++) {
pwmVal[i] = 0;
}
}
void loop()
{
PORTD = PORTD_first_state; //set pin, witch have a pwmVal > 0 to HIGH
PORTB = PORTB_first_state;
PORTC = PORTC_first_state;
for (x=0; x<256; x++) {
for (i=0; i<18; i++) {
if (x == pwmVal[i]) {
if (i < 8) { // corresponds to PORTD
// bitshift a one into the proper bit then reverse the whole byte
// equivalent to the line below but around 4 times faster
// digitalWrite(i, LOW);
PORTD = PORTD & (~(1 << i));
}
else if (i < 14) {
PORTB = PORTB & (~(1 << (i-8))); // corresponds to PORTB - same as digitalWrite(pin, LOW); - on Port B pins
}
else {
PORTC = PORTC & (~(1 << (i-14)));
}
}
}
}
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
//set all leds low, so they don't flash, caused by the short stop of the PWM procedure
PORTD = 0x00;
PORTB = 0x00;
PORTC = 0x00;
byte LED = 0;
byte data = 0;
byte Register = Wire.receive();
//if first byte is no register byte -> return
if (Register > 17)
return;
LED = Register;
while (0 < Wire.available()) {
data = Wire.receive(); // receive byte as an integer
pwmVal[LED] = data;
//make the led light up at the beginning of the pwm procedure
//if the led has at least a value > 0
if (pwmVal[LED] > 0) {
if (LED < 8) {
PORTD_first_state = PORTD_first_state | (1 << LED);
}
else if (LED < 14) {
PORTB_first_state = PORTB_first_state | (1 << (LED-8));
}
else {
PORTC_first_state = PORTC_first_state | (1 << (LED-14));
}
}
else { //else set the first state of the led at the beginning of pwm procedure to low
if (LED < 8) {
PORTD_first_state = PORTD_first_state & (~(1 << LED));
}
else if (LED < 14) {
PORTB_first_state = PORTB_first_state & (~(1 << (LED-8)));
}
else {
PORTC_first_state = PORTC_first_state & (~(1 << (LED-14)));
}
}
LED++;
if (LED > 17)
LED = 0;
}
}