Announcement

Collapse
No announcement yet.

Migrating I2C from Picaso to Diablo...

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Migrating I2C from Picaso to Diablo...

    When I updated our hardware to also support Diablo I added the option to use PA14/15 for I2C, as well as the 'original positions' PA13 and PA12, as I wanted to option of slew rate control to ensure good EMC behaviour.

    I haven't been able to get any I2C output from the PA14 and PA15 pins. Any idea why I2C1_Open(I2C_MED,PA14,PA15); didn't get PA14 and PA15 wiggling?

    So I went back to PA13 and PA12 and eventually got it working fine although I found that all of my ACK code that worked on Picaso didn't work on Diablo (i.e. my code thought that it was never getting an ACK from any of my slave devices).

    My code uses an abstraction layer to run on both Picaso and Diablo at present...

    func HW_WriteI2CWithAckStatus(var a)
    #IF USING_PICASO
    I2C_Write(a);
    return I2C_AckStatus();
    #ELSE
    if (I2C1_Write(a)==1) return 1; else return 0;
    #ENDIF
    endfunc


    Whether I was doing the right thing on Picaso is a mute point (though it always worked.... and must have come from some reference code _somewhere_) the documentation for Diablo says that I2Cx_Write() returns a 1 for NACK and a 2 for ACK whereas my practical experiments determined that it is infact the other way around (1=ACK, 2=NACK)

    Simon

  • #2
    Hmm, quite a few things here.
    1. I2C_Write returns the ACK Status, you don't really need a call to I2C_AckStatus
    2. The Diablo documentation for I2Cx_Write documents the returned value incorectly, it is the same as Picaso
    3. Diablo I2C_AckStatus is 'inverted' with respect to Picaso when using Pins PA0-PA13. This will be corrected in a future PmmC

    I can't fault PA14 and PA15 Pins, what PmmC and Display are you using? Could you have 'lost' the pullup resistors when testing PA14/15?

    Here's an I2C detect program that demonstrates the 'equivalency' of Picaso/Diablo I2C. It doesn't need code changes between Picaso and Diablo, only when switching between PA pins on Diablo

    Code:
    #platform "uLCD-32PTU"
    
    #inherit "4DGL_16bitColours.fnc"
    
    func main()
        var addr, resp, acks ;
    #IF EXISTS DIABLO
        I2C1_Close();
        pause(100);
        //                 SCL SDA
    //  I2C1_Open(I2C_SLOW,PA13,PA12) ;     // flip these manually. This
        I2C1_Open(I2C_MED,PA14,PA15) ;      // or This
    #ELSE
        I2C_Close();
        pause(100);
        I2C_Open(I2C_SLOW);
    #ENDIF
        pause(100);
        for (addr := 0; addr < 0x100; addr += 2)
    #IF EXISTS DIABLO
            I2C1_Idle();                             // Wait for bus Idle
            I2C1_Start();                            // Generate Start condition
            resp := I2C1_Write(addr);                // send slave address
            acks := I2C1_AckStatus() ;
    #ELSE
            I2C_Idle();                             // Wait for bus Idle
            I2C_Start();                            // Generate Start condition
            resp := I2C_Write(addr);                // send slave address
            acks := I2C_AckStatus() ;               // This is 'reversed' on Diablo when using PA0-PA13 with respect to Picaso and Diablo PA14/15
    #ENDIF
            if (resp == 1)
                txt_FGcolour(LIME);
            else
                txt_FGcolour(RED);
            endif
            print([HEX2] addr, " ", resp, " ") ;
            print(acks, " ") ;
            txt_FGcolour(LIME);
    #IF EXISTS DIABLO
            I2C1_Nack();                         // send Nak for bad response
            I2C1_Stop();                            // Generate Start condition
    #ELSE
            I2C_Nack();                         // send Nak for bad response
            I2C_Stop();                            // Generate Start condition
    #ENDIF
        next
        print("Done") ;
        repeat                      // maybe replace
        forever                     // this as well
    
    endfunc
    Mark

    Comment


    • #3
      Hi Mark,

      1. Agreed and already corrected for Diablo. The original code worked so it only got dug into when I had 'the issues' with Diablo. I'll tidy it up as we still have a few Picaso prototypes out and about.

      2. As i'd used AckStatus() for the return value i'd not come across this 'feature' so the documentation error for both was new to me !

      3. I'm now using the I2C_Write return value so this correction later shouldn't bite me.

      Lack of pull ups is probably it for PA14/15. We've never historically needed external ones. All my Diablo systems are at the test house but as soon as I retrieve them early next week i'll take a look. but, we passed European automotive EMC and FCC using the non slew controlled pins, so i'll be sticking with them now. I'll be able to test the new Sleep pmmc functionality we've been discussing over on the other thread then too.

      Many thanks,

      Simon

      Comment


      • #4
        I am trying to get this example to work with the Diablo, but I am missing something here, I sure could use a little help. I have the SCL and the SDA pins connected correctly and have a clock display but no movement and nothing but zeros. I know there are some differences between Diablo and Picasso, and I am using the most recent workshop and have updated to PmmC16.

        #platform "uLCD-35DT"


        // 4DGL Demo Application
        // PICASO Platform --
        // This prog is saved as I2Cclock.4XE
        // This program reads a Dallas DS1307 clock chip
        // connected to the I2C port. The time is shown as an
        // analogue representation of a clock face.


        #inherit "4DGL_16bitColours.fnc"
        #inherit "FONT4.fnt"


        #constant FACECOLOUR DARKGREEN
        #constant SECONDSCOLOUR RED
        #constant MINUTESCOLOUR LIME
        #constant HOURSCOLOUR YELLOW

        #constant SECONDS 0
        #constant MINUTES 1
        #constant HOURS 2
        #constant DS1307 0xD0
        #constant WR 1




        // global variables
        var seconds, minutes, hours;
        var targetX, targetY;
        var screenwidth, screenheight;
        var xc, yc, r;
        var SCLpin := PA13;
        var SDApin := PA12;

        // draw the line for a clock hand
        func DrawHand(var length, var angle, var colour)
        gfx_MoveTo(xc, yc); // MUST RE_ESTABLISH THE CENTRE POINT!!!
        gfx_Set(OBJECT_COLOUR, colour);
        gfx_Orbit(angle-90, length);
        gfx_LineTo(targetX, targetY); // but it should be gfx_LineTo, this is now correct
        endfunc

        // redraw hands, return true if an update occured
        func DrawHands()
        var private first, oldsecs, oldmins, oldhours;
        if(first)
        DrawHand(r-20, oldsecs*6, FACECOLOUR); // undraw the old second hand
        gfx_Circle(targetX, targetY, 3, FACECOLOUR);
        DrawHand(r-35, oldmins*6+oldsecs/10, FACECOLOUR); // undraw the old minute hand
        gfx_Circle(targetX, targetY, 5, FACECOLOUR);
        DrawHand(r-50, oldhours*30+oldmins>>1, FACECOLOUR); // undraw old the hour hand
        gfx_Circle(targetX, targetY, 7, FACECOLOUR);
        endif
        first := 1;
        DrawHand(r-20, seconds*6, SECONDSCOLOUR); // redraw the second hand
        gfx_Circle(targetX, targetY, 3, SECONDSCOLOUR);
        DrawHand(r-35, minutes*6+seconds/10, MINUTESCOLOUR); // redraw the minute hand
        gfx_Circle(targetX, targetY, 5, MINUTESCOLOUR);
        DrawHand(r-50, hours*30+minutes>>1, HOURSCOLOUR); // redraw the hour hand
        gfx_Circle(targetX, targetY, 7, HOURSCOLOUR);

        gfx_Circle( xc, yc, 5, ORANGE );

        oldsecs:=seconds;
        oldmins:=minutes;
        oldhours:=hours;

        endfunc

        // the clock chip register format is BCD.
        // This converts the register value to a binary value
        // so we can do normal math operations with it if required.
        func bin2bcd(var binValue)
        return (binValue>>4)*10 + (binValue&15);
        endfunc


        // read a single byte from the required register
        func readbyte(var address)

        var b;
        I2C1_Start(); // Generate Start condition
        I2C1_Write(DS1307); // send slave address
        I2C1_Write(LObyte(address)); // Send register address
        I2C1_Restart(); // Generate Restart
        I2C1_Write(DS1307+WR); // send control byte for Read
        b := I2C1_Read(); // read the byte

        I2C1_Nack(); // finished reading, send Not Ack
        I2C1_Stop(); // Send Stop Condition
        return b; // return the register value

        endfunc

        // write a single byte to the required register
        func writebyte(var register, var value)
        I2C1_Start(); // Generate Start condition
        I2C1_Write(DS1307); // send slave address
        I2C1_Write(register); // select the register
        I2C1_Write(value); // save the value in selected register
        I2C1_Stop(); // finished with bus
        endfunc

        func main()
        var n, k, colr, exit;

        I2C1_Open(I2C_SLOW, SCLpin, SDApin); // 100khz
        //I2C_Open(I2C_MED); // 400khz
        //I2C_Open(I2C_FAST); // 1mhz NB DS1307 may not run at 1mhz!!!

        pause(10);

        n:=readbyte(0) & 0xD0; // ensure CH bit is clear else clock wont run
        writebyte(0, n);

        // draw logo
        txt_FontID(FONT4);
        txt_MoveCursor(18,3);
        txt_FGcolour(WHITE);
        print("4D Systems ");
        txt_FGcolour(RED);
        print("4");
        txt_FGcolour(CYAN);
        print("D");
        txt_FGcolour(YELLOW);
        print("G");
        txt_FGcolour(LIME);
        print("L");

        // get the screen resolution and centrepoint
        screenwidth := gfx_Get( X_MAX );
        screenheight := gfx_Get( Y_MAX );
        xc := screenwidth >> 1;
        yc := screenheight >> 1;
        yc -= 10;

        // calculate a radius suitable for landscape or portrate mode
        r := MIN(screenwidth, screenheight) >> 1;

        // draw the clock face
        gfx_Set(PEN_SIZE, SOLID);
        gfx_Circle( xc, yc, r-16, FACECOLOUR );


        // draw the clocks outer dress ring
        gfx_Set(PEN_SIZE, OUTLINE);
        n := -8;
        while (n++ < 8)
        colr := ABS(n)+12^31;
        gfx_Circle( xc, yc, r+n-8, colr );
        wend

        // set up the centre point
        gfx_MoveTo(xc, yc);

        // a target variable for the orbit command
        gfx_OrbitInit(&targetX, &targetY);

        // mark the hours round the clockface
        gfx_Set(PEN_SIZE, SOLID);
        gfx_MoveTo(xc, yc);
        n := -90; // 12 o'clock position
        while (n<270)
        gfx_Orbit(n, r-7);
        k := 2;
        if (!(n % 90)) k := 4;
        gfx_Circle(targetX, targetY, k, BLUE);
        n := n + 30; // each 30 degreees
        wend

        touch_Set(TOUCH_ENABLE); // enable the touch screen

        // each second, redraw the clock face
        repeat

        n:=readbyte(SECONDS); // wait til seconds rolls over
        while(n==readbyte(SECONDS))
        if (touch_Get(TOUCH_STATUS) == TOUCH_PRESSED)
        exit := 1;
        break; // if there's a press exit
        endif
        wend

        seconds := bin2bcd(readbyte(SECONDS)); // read xloxk xhip, convert to
        minutes := bin2bcd(readbyte(MINUTES));
        hours := bin2bcd(readbyte(HOURS));

        DrawHands(); // update the clock
        txt_FGcolour(LIGHTGREY);
        txt_FontID(FONT3);
        txt_MoveCursor(1, 5);
        print ("The Time is ", [DEC2Z] hours, ":", [DEC2Z] minutes, ":", [DEC2Z] seconds);

        until(exit == 1);

        gfx_Cls(); // clear screen
        I2C1_Close(); // close the I2C port.

        while (touch_Get(TOUCH_STATUS) != TOUCH_RELEASED); // wait for touch release before returning







        endfunc

        Comment


        • #5
          Have you put 4k7 pullups on PA12 and 13?
          Mark

          Comment


          • #6
            Hi Mark,
            I have the pins connected to the I2C bus with a 1307 clock module and an EEPROM and yes I am pulling the lines up using 4.7 K Ohm resistors. I was using pins 14 and 15 but was getting nothing. I read here that pins12 and 13 are the ones to use. So I switched. I am still getting nothing, but when I upload the program, it stops the clock from running. I am verifying that the clock is working and connected using an arduino also connected to the same I2C bus. Can you think of anything else I can try? I have tried running the 35DT without the arduino connected but it doesn't seem to matter. Thanks for your help.

            Mark

            Comment


            • #7
              Don't know, I haven't got a DS1307 handy so I can't really test out the same setting.

              Have you got a logic probe you can check things out with?

              Maybe swap SCL and SDA, just for the heck of it?
              Mark

              Comment


              • #8
                Mark,

                I swapped my uLCD35DT for a Picasso unit and ran the same program and it worked. But when I change back and modify the program to run with the changes for the Diablo, nothing happens. I get nothing. Is there anything that has to be done when you modify the program to run on the Diablo besides change the Open statement, specify which pins to use for the SDA and SCL, and change the I2C to I2C1, I couldn't find anything in the manual. Can you think of anything? , I could use some help.

                Thank you,

                Mark

                Comment


                • #9
                  Sorry, can't think of anything.

                  I've ordered a DS1307, so hopefully I will have an answer tomorrow
                  Mark

                  Comment


                  • #10
                    Thank you, for your help. That DS1307 will come in handy. I think I figured out what I was doing wrong. I hadn't hooked up my ground correctly. It's working now.
                    Also in one of the programs I had declared SCLpin and SDApin equal to 11 and 10 instead of:
                    var SCLpin := PA11;
                    var SDApin := PA10;

                    everything seems to be working now.

                    Thank you,

                    Mark
                    Last edited by markl; 8 April 2015, 04:17 PM.

                    Comment


                    • #11
                      I am still having an issue with reading an I2C EEPROM. It is operating so slowly that it is locking up my program. What used to take millsec to read and display now takes a 30 sec. I have gotten the examples to work and they are as slow as mine. I noticed from the code segment above that these lines are not in my code.

                      resp := I2C_Write(addr); // send slave address
                      acks := I2C_AckStatus() ; // This is 'reversed' on Diablo when using PA0-PA13 with respect to Picaso and Diablo PA14/15
                      I am using pins 10, and 11, can you tell me what these statement do, and if they might make a difference.

                      Thanks,

                      Mark

                      Comment


                      • #12
                        I'm confused. Without the write of the addr the code cannot possibly work as the I2C device will not be addressed.

                        Do you have some cut down code that demonstrates this 30 second issue? Are you sure the connections are all present and correct?
                        Mark

                        Comment


                        • #13
                          Hi, yes the connections are correct as I can verify what is written to the addresses by hooking up my arduino and reading to the serial monitor. Here's the code, straight from examples gathered here:

                          func Read_EEPROM(var MKey)
                          var a, b, n, i, p_index, posn, t, address;
                          a := 0;


                          for(n:= 0; n<55; n++)


                          I2C1_Idle();
                          I2C1_Start();
                          I2C1_Write(EEPROM); // slave address appended with 0 R/W bit
                          // I2C1_Idle(); I am experimenting with some of the statements here, trying to figure out what is going on

                          I2C1_Write(HIbyte(n)); //Send High Address
                          // I2C1_Idle();
                          I2C1_Write(LObyte(n));
                          I2C1_Idle();

                          I2C1_Restart();
                          I2C1_Write(EEPROM+1); //slave address appended with R/W bit (set to 1)

                          I2C1_Idle();
                          value := I2C1_Read();
                          print(value);

                          // I2C1_Nack();
                          I2C1_Stop();
                          I2C1_Close();
                          a ++;
                          next
                          txt_FontID(FONT_4);
                          endfunc


                          I open the I2C connection in the main like this:
                          I2C1_Open(I2C_FAST, SCLpin, SDApin);

                          I call the above function each time a new page is displayed. With the Picasso display it can fill the screen with numbers as fast as you can change the page. With Diablo, there must be something that I am missing because each loop takes about a half second. so if I run through this 40 times per page it is taking about 20 seconds to display the data. I know that the address 0xA0 is right because the arduino is correctly reading the eeprom addresses and giving me the same numbers. Does the Diablo require something else?

                          Your help is appreciated,

                          Mark
                          Last edited by markl; 6 June 2015, 01:55 PM.

                          Comment


                          • #14
                            Hmm, there's a few things going on here.

                            You have an I2C close inside the loop, surely that means that the device cannot work with Picaso???

                            You have sent a low and high byte address, I only have a 24C02 so I only need to write a 1 byte address, writing 2 bytes the way you do causes my 24C02 to respond with incorrect data. IF you are using a larger device please check that you are using the correct address order (The datasheet only shows a single address byte for all sizes).

                            You do not need the I2C idle (it is done inside the writes and read for you) and this is causing your issue, for some reason the 24C02 seems to indicate 'busy' to the I2C bus shortly after the write is complete. Picaso just returns the IDLE status, Diablo waits until IDLE and/or times out. With the 24C02 it times out and this is what is making the code so slow.

                            So your function becomes
                            Code:
                            func Read_EEPROM(var MKey)
                                var a, b, n, i, p_index, posn, t, address;
                                a := 0;
                            
                                for(n:= 0; n<55; n++)
                            
                            //        I2C1_Idle();      not needed
                                    I2C1_Start();
                                    I2C1_Write(EEPROM); // slave address appended with 0 R/W bit
                            
                            //        I2C1_Write(HIbyte(n)); //Send High Address        not needed for 24c02 and causes errors when used
                                    I2C1_Write(LObyte(n));
                                    I2C1_Idle();
                            
                                    I2C1_Restart();
                                    I2C1_Write(EEPROM+1); //slave address appended with R/W bit (set to 1)
                            
                            //        I2C1_Idle();      above write does an idle, 24c02 seems to go busy immediately after write, need to investigate
                                    value := I2C1_Read();
                                    print(value);
                            
                                    I2C1_Stop();
                            //        I2C1_Close(); this causes an error
                                    a ++;
                                next
                                txt_FontID(FONT_4);
                            endfunc
                            Mark

                            Comment


                            • #15
                              Mark,

                              Thank you for helping me here. Your changes made the reading of the EEPROM as fast as I need. I am using a Microchip 24AA1025 and for me this seems to work fine:

                              I2C1_Start();
                              I2C1_Write(EEPROM); // slave address appended with 0 R/W bit
                              I2C1_Write(HIbyte(n)); //Send High Address
                              I2C1_Write(LObyte(n));
                              I2C1_Idle();

                              but, the placement of all the Idle statements was driving me crazy. I tried it every different way I could think of, so thank you again for your time.

                              Mark

                              Comment

                              Working...
                              X