/* 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 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; } }