1. /*
  2. atmega_i2c_pwm_expander.pde
  3.  
  4. 21.11.2009
  5. Jan Krause
  6.  
  7. Based on PWMallPins by Paul Badger.
  8.  
  9. 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.
  10. The ATmega8 is the cheapest so I used this one.
  11. With this code loaded up to your chip you can drive up to 18 leds in 255 brightnes steps for every led.
  12. I compiled the code with the Arduino software(avr-gcc), but it might also work with an other compiler.
  13.  
  14.  
  15. Wiring:
  16.               _______
  17.     (RESET)  =| |_| |=  SCL
  18.       LED 0  =|     |=  SDA
  19.       LED 1  =|     |=  LED 17
  20.       LED 2  =|     |=  LED 16
  21.       LED 3  =|  A  |=  LED 15
  22.       LED 4  =|  T  |=  LED 14
  23.         +5V  =|  m  |=  
  24.         GND  =|  e  |=  
  25.        OSC1  =|  g  |=  +5V
  26.        OSC2  =|  a  |=  LED 13 (SCL)
  27.       LED 5  =|  8  |=  LED 12 (MISO)
  28.       LED 6  =|     |=  LED 11 (MOSI)
  29.       LED 7  =|     |=  LED 10
  30.       LED 8  =|_____|=  LED 9
  31.  
  32.  
  33. To use the chip run a code, looking like the following, on the I²C master:
  34.  
  35. void writeLEDs()
  36. {  
  37.   Wire.beginTransmission(DEVICE_ADDR);  //DEVICE_ADDR is the address you set in the PWM IC code (in this example its B00000100 = 4)
  38.   Wire.send(0);                         //No. of the led you want to start with setting
  39.  
  40.   for (int i = 0; i < 18; i++) {
  41.     Wire.send(LEDs[i]);                 //LEDs[i] is an array holding the values of brigthnes
  42.   }
  43.  
  44.   Wire.endTransmission();
  45. }
  46.  
  47. */
  48.  
  49. #include <Wire.h>
  50.  
  51. byte DEVICE_ADDR = B00000100;
  52.  
  53. int pwmVal[18];
  54. int i, x;
  55. byte PORTD_first_state = B00000000;
  56. byte PORTB_first_state = B00000000;
  57. byte PORTC_first_state = B00000000;
  58.  
  59.  
  60.  
  61. void setup()
  62. {
  63.   Wire.begin(DEVICE_ADDR);      //join i2c bus as slave
  64.   Wire.onReceive(receiveEvent); //register event
  65.  
  66.   DDRD = 0xFF;  //set port  0 to  7 as output
  67.   DDRB = 0xFF;  //set port  8 to 13 as output
  68.   DDRC = 0x0F;  //set port 14 to 17 as output (ADC ports)
  69.  
  70.   for (i = 0; i<18; i++) {
  71.     pwmVal[i] = 0;
  72.   }
  73.  
  74. }
  75.  
  76.  
  77. void loop()
  78. {
  79.   PORTD = PORTD_first_state;  //set pin, witch have a pwmVal > 0 to HIGH
  80.   PORTB = PORTB_first_state;
  81.   PORTC = PORTC_first_state;
  82.  
  83.  
  84.   for (x=0; x<256; x++) {
  85.     for (i=0; i<18; i++) {
  86.       if (x == pwmVal[i]) {
  87.         if (i < 8) {    // corresponds to PORTD
  88.           // bitshift a one into the proper bit then reverse the whole byte
  89.           // equivalent to the line below but around 4 times faster
  90.           // digitalWrite(i, LOW);
  91.           PORTD = PORTD & (~(1 << i));
  92.         }
  93.         else if (i < 14) {
  94.           PORTB = PORTB & (~(1 << (i-8)));         // corresponds to PORTB - same as digitalWrite(pin, LOW); - on Port B pins
  95.         }
  96.         else {
  97.           PORTC = PORTC & (~(1 << (i-14)));
  98.         }
  99.       }
  100.     }
  101.   }
  102.  
  103. }
  104.  
  105.  
  106. // function that executes whenever data is received from master
  107. // this function is registered as an event, see setup()
  108. void receiveEvent(int howMany)
  109. {
  110.   //set all leds low, so they don't flash, caused by the short stop of the PWM procedure
  111.   PORTD = 0x00;
  112.   PORTB = 0x00;
  113.   PORTC = 0x00;
  114.  
  115.   byte LED = 0;
  116.   byte data = 0;
  117.   byte Register = Wire.receive();
  118.  
  119.   //if first byte is no register byte -> return
  120.   if (Register > 17)
  121.     return;
  122.  
  123.   LED = Register;
  124.  
  125.   while (0 < Wire.available()) {
  126.     data = Wire.receive();    // receive byte as an integer
  127.     pwmVal[LED] = data;
  128.  
  129.     //make the led light up at the beginning of the pwm procedure
  130.     //if the led has at least a value > 0
  131.     if (pwmVal[LED] > 0) {
  132.       if (LED < 8) {
  133.         PORTD_first_state = PORTD_first_state | (1 << LED);
  134.       }
  135.       else if (LED < 14) {
  136.         PORTB_first_state = PORTB_first_state | (1 << (LED-8));
  137.       }
  138.       else {
  139.         PORTC_first_state = PORTC_first_state | (1 << (LED-14));
  140.       }
  141.     }
  142.     else {  //else set the first state of the led at the beginning of pwm procedure to low
  143.       if (LED < 8) {
  144.         PORTD_first_state = PORTD_first_state & (~(1 << LED));
  145.       }
  146.       else if (LED < 14) {
  147.         PORTB_first_state = PORTB_first_state & (~(1 << (LED-8)));
  148.       }
  149.       else {
  150.         PORTC_first_state = PORTC_first_state & (~(1 << (LED-14)));
  151.       }
  152.     }
  153.  
  154.  
  155.     LED++;
  156.     if (LED > 17)
  157.       LED = 0;
  158.   }
  159.  
  160. }

Share