PIC -> BlinkM* I2C Communication Question

Discussion in 'Embedded Systems and Microcontrollers' started by agroom, May 10, 2012.

  1. agroom

    Thread Starter Active Member

    Oct 15, 2010
    I'm working on a project using an open-source BlinkM firmware alternative called cyz_rgb. As far as I know, it duplicates the BlinkM in every aspect except for a few minor details. Otherwise, for all practical measures, it can be used as if I had an actual BlinkM. I'm guessing most have not worked with this before (though possibly with BlinkMs), so my goal here is basically to show my code and seeing if there's any obvious errors that I'm making.

    I have the ATTiny45 chip flashed and hooked to a project board with a PIC 18F4520 and using PCW PIC-C's compiler.

    So far, I can get all the built-in functions to work using the general call address (LED = 0x00), but I'm having issues with assigning an address and referencing the pixel by that address (though I think the former would cure the latter).

    The project board has an LCD screen and I have it display both variables LED and getLED. LED properly displays the address, as it should, but I keep getting 0xFF for the returned getLED value. There is only one pixel attached, so both the Set_Address() and Get_Address() functions should work using the general broadcast channel.

    The PIC chip uses a 7-bit address (0-127), with the LSB being a R/W instruction. I'm not sure if the pixel interprets Set_Address(0x02) as address 2 or 1 (assumes it was already bit-shifted). However, I used 0x02 since it's an even number and should avoid the slave thinking it a write command.

    I've also tried changing the parameters from 'int' to 'unsigned char', but I still get the same getLED result of 0xFF.

    I don't have a schematic of the setup, but I have pull-up resistors on both SCL and SDA and everything has a common ground. I assume the circuitry is correct though since it works fine other than addressing? I can make one if necessary though.

    One other note: as I said, I can get the following code to work using LED = 0x00; however, before it will start, I have to cycle the power to the chip, i.e. unplug the power, then plug it back in. Not sure why this is happening.

    Main C code:
    Code ( (Unknown Language)):
    1. #include <18F452.h>
    2. #use delay (clock = 20000000)
    3. #fuses HS, NOWDT, NOLVP
    4. #use I2C (MASTER, SDA=PIN_C4, SCL=PIN_C3)
    6. #include "..\Library\modifiedlcd.h"
    7. #include "..\Library\functions.h"
    9. void main(){
    11.    int LED = 0x02, getLED;
    12.    lcd_init();
    14.    Set_Address(LED); // Set pixel address
    16.    getLED = Get_Address();
    17.    printf(lcd_putc,"\f LED: %X \n getLED: %X", LED, getLED);
    19.    while(1){ // Blink light on/off every second
    21.       Goto_RGB_Now(LED,255,255,255);
    22.       delay_ms(1000);
    23.       Goto_RGB_Now(LED,0,0,0);
    24.       delay_ms(1000);
    25.    }
    26. }
    Library Functions:
    Code ( (Unknown Language)):
    1. /**************************
    2.    Go to RGB Color Now
    3. **************************/
    4. void Goto_RGB_Now(int LED, int red, int green, int blue){
    5.    i2c_start(); // i2c start condition
    6.    i2c_write(LED); // Device address
    7.    i2c_write(0x6E); // Goto RGB color now
    8.    i2c_write(red); // Red value
    9.    i2c_write(green); // Green value
    10.    i2c_write(blue); // Blue value
    11.    i2c_stop();
    12. }
    13. /**************************
    14.    Set Address
    15. **************************/
    16. void Set_Address(int LED){
    17.    i2c_start();
    18.    i2c_write(0x00);
    19.    i2c_write(0x41);
    20.    i2c_write(LED);
    21.    i2c_write(0xD0);
    22.    i2c_write(0xD0);
    23.    i2c_write(LED);
    24.    i2c_stop();
    25.    delay_ms(20); // Wait for EEPROM to write
    26. }
    28. /**************************
    29.    Get Address
    30. **************************/
    31. int Get_Address(){
    32.    int address;
    33.    i2c_start();
    34.    i2c_write(0x00);
    35.    i2c_write(0x61);
    36.    address = i2c_read();
    37.    i2c_stop();
    38.    return address;
    39. }
    Last edited: May 10, 2012
  2. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    Since you get a response using the general call address your hardware should all be OK. <sigh of relief>

    I2C can be a major PITA to get working. The hardware is beautiful as you only need 2 lines and 2 resistors to get a multitude of devices to work together, but it isn't the easiest of protocols to work with. I too stumble over what the "address" really means.

    I would imagine you need to reset power to get "LED = 0x00" to work as the BlinkM starts off at address zero until told otherwise. So you need the reset to go back to zero and try it again.

    I think I may see something. Your code uses "LED" as the set address, but uses "getLED" as the returned address. Try returning it to just "LED" and see what happens.

    If you have a debugger see what "getLED" really is. If you don't, make a if statement to blink different rates if LED and getLED are different values.
  3. agroom

    Thread Starter Active Member

    Oct 15, 2010
    Thanks for the input. I agree, the simple elegance of I2C is amazing :)

    I did that because LED is the address I want the BlinkM to be, and used a second variable getLED to see what the Get_Address function returned as its actual address. This way I could compare that LED actually holds the address I want and getLED returned the same address.

    Making LED = getLED would defeat the purpose of manually assigning an address...in hopes of later being able to assign addresses to several different BlinkMs and used them together.

    I don't have a debugger, but I do have the LCD panel on my project board. I'm having it print the values of both LED and getLED. Per the code below, I get "LED = 02, getLED = FF".

    Per this, I'm not sure then if the Set_Address is working properly but Get_Address is not, or if Set_Address() is just not working to begin with. Since there's also not much support for the cyz_rgb firmware, and it's technically not a BlinkM, I don't know what's going on back-end on the ATtiny45. The firmware is open-source and I have d/l and reviewed it, but it's a bit over my head atm :(

    [EDIT] btw, I have have tried changing the Goto_RGB_Now() address to 0xFF, but that had no effect.
    Last edited: May 10, 2012
  4. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    Curious. 0xFF is I2C's way of saying "WTF," meaning no slave responded so the resistor pulled SDA high while the master clocked SCL and read all highs in. Perhaps BlinkM only respondes to addr 00 until you tell it otherwise, then no more. Or the Get_Address routine isn't working. You can check if Get_Address is working by reading for address zero before you set address zero.

    BTW, I2C has the ACK/NAK bit in the data stream so the Master can tell if a Slave is there listening. Many routines ignore this check, it seems so does your code.

    Before I had an in circuit debugger I was forced to use an LCD display such as you are doing. You can get lots done that way if you don't mind constantly recompiling to try different things.
  5. agroom

    Thread Starter Active Member

    Oct 15, 2010
    Maybe...the 00 address is intended as a general call address, so if you have say 10 BlinkMs and want to issue a command to all of them you would use this address. Once given an address though, it's supposed to maintain it even after removing the power. This makes sense, you can only set the address of a single pixel at a time, then you'll have to remove it to set another. And these are typically used in long strings of pixels, so each would have to retain it's address once set, or having more than 1 on a string would be rather pointless.

    I think there's a default address of 0x09 when you first flash the firmeware, but I've tried calling that address as well as 0x12 (the bit-shifted address) with no luck. I'll try using the get_address first though, never thought of that! I highly suspect though that either my get_address or set_address functions are not setup correctly. The syntax might be right when compared to other write functions like goto_RGB_now(), but it's possible there's a step I'm missing.

    THE compilers i2c_read() function has an ack parameter. Default is to ack, but I've tried both with no luck. However, I'm going to try this again. If there is an ack, do you think that's something I need to build into the code or would you think the compilers function handles that?

    ha! Well, I think I've compiled and ran this program at least 50x, and presumably over 100 :) I think my next step is to try and manually read from it using the chips registers. I'm fairly familiar with register manipulation, but not with the MSSP module yet.

    Again, I appreciate the help!
  6. agroom

    Thread Starter Active Member

    Oct 15, 2010
    HA! Well I figured out the problem with my Set_Address() function. It's times like this you wonder how you even survived this long...lol. The problem was, to set an address you need to send the data in this format {new address, 0xd0, 0x0d, new address}, this is to prevent any accidental address setting. Well, at first glance...and many subsequent, I thought the middle two commands were both 0xd0. Somehow I just caught that, changed it, and it works perfectly!

    So if I call the Set_Address() using the actual address I want (not bit shifted), then call the goto_RGB_now() function using the bit shifted address, it works great! I've even changed up the LED = X to several addresses and they all work fine.

    I suppose at this point I'm well enough to move forward, but at some point I'll need to figure out the issue. There's a few other functions like getting the current RGB state and reading script lines that I'll want to use.
  7. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    Excellent, congratulations for getting it to work.

    The address thing is weird, I went back to scan the source and making that change makes no sense unless the Blinkie thing has the bug.
  8. agroom

    Thread Starter Active Member

    Oct 15, 2010
    There's one other function that reads from the BlinkM to get the current RGB value. I'm gonna try that one and see if I get the same results. However, I doubt I'll still be able to figure out if I'm just not using the compilers i2c_read() function right or if it's an issue with the blinkM.

    However, I think I'm gonna just work on making my own read/write functions instead of the compilers. I can only use this compiler at school, so I can't run/use this at home. If I make my own then it shouldn't matter which compiler I use.
  9. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    Microchip gives away compilers including one (or more?) for the 18F series.

    It's the one I use.
  10. agroom

    Thread Starter Active Member

    Oct 15, 2010
    Could you post a link to it? Do they have one for chips other than 18F?

    I'm using the 18F chip since it's on my project board, but for this application I plan to eventually use a smaller 8-14 pin chip once I get all the bugs out. So likely something in the 12F or 16F family.
  11. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    Microchip Compilers

    I've been using the C18 compiler for years (and currently) now and just love it.

    They are currently just finishing up a new single compiler to work with all their devices, may be out now as it's been due any day for a month now.