UIMENU and I2C respectively SPI

edited March 2016 in Questions & Answers
Hi all,

after playing around with UI functions  i have some questions i couldn´t figure out on my own.

for example the UI_MATRIX_ACTIONS.
seems to be needed only with matrix keyboard , am i correct?


I2C buttons , is it possible to add a second/third I2C peripheral using the integrated I2C driver?

if yes , maybe a short example would be helpful (no need to get the job done by somebody i just hope for a starting point)

in UIconfig there is the possibility to connect buttons via MCP23017  is it possible to switch to the SPI pendant MCP23S17?
would be much faster.May be somebody did that, Software SPI would be nice.

Maybe there is someone in the forum to give me a guide.


Thank´s in advance,

RAyWB

Comments

  • UI_MATRIX_ACTIONS is only needed for matrix keyboards, yes.

    Integrated is only one I2C chip. As you know for keys you have to define







    void uiInitKeys()

    and









    void uiCheckKeys(uint16_t &action)

    so it is possible to add any logic you want for extra key functions. The I2C solution implemented is just mixing display and free pins for buttons to get them more or less for free.

  • edited March 2016
    Thank you for the quick response,
    what i´m thinking about is implementing user specific hardware , the idea came up as you  implemented the analog input
    to read buttons.
    That´s what i described in my post "manual positioning/jogwheel". I added now some buttons for homing , setting positions to zero, single probing  and so on   just functions i want t control without menu and without computer connected. and therefore i use a hacked gamepad.
    so now my thoughts are drifting to adding an external A/D converter and/or additional Portextender like 23017.
    i want to insert the code to uiconfig  similar to your :

    #if UI_DISPLAY_I2C_CHIPTYPE==1  // will change this to check one pin if gamepad is connected
        HAL::i2cStartWait(UI_DISPLAY_I2C_ADDRESS+I2C_WRITE); // have to change to address of my hardware
        HAL::i2cWrite(0x12); // GIOA
        HAL::i2cStop();
        HAL::i2cStartWait(UI_DISPLAY_I2C_ADDRESS+I2C_READ);
        uint16_t keymask = HAL::i2cReadAck();
        keymask = keymask + (HAL::i2cReadNak()<<8);
    #endif


    i know i can´t use same address and variables so i have to change the names, my basic intention is reducing wires to my
    hacked gamepad just placing the A/D converter to the gamepad side. from Hardware view i know how to handle the I2C for longer distances , that will not be an issue.

    Thanks for your support and by the way thank you for your absolute great Job and support !!

    One more question please , regarding the GITHUB repository ,  are there some special rules to create a fork of your software?
    I don´t have an account yet , but in order to share my modifications i´m thinking about that.

    best regards,

    RAyWB

  • No, anyone can make a fork if he is registered, that is no problem.

    You can also use the I2C routines. As you see they just set address and read or write. In your case only read I guess. Just us ethe address of your chip. There are pins to change the address.
  • OK,
    i tried to use the existing code as it is but could not succeed i used MCP23017 but it seems there is some mismatch when using NON-I2C Display.  there seem to be multiple I2C Routines (found some in HAL  , some in U8Glib) so i´m pretty confused now.
    So basically the UI_Display I2C routines come from u8glib?

    I also cannot follow how the    UI_KEYS_I2C_BUTTON_LOW(_BV(1),UI_ACTION_OK) works.

    so there is the I2C read , returning a value to variable keymask.

    _BV(1)  is a bit shift operation defined in HAL , pinnumber means value of shift

    but i cannot find a function UI_KEYS_I2C_BUTTON_LOW...


    Can you clarify please?
  • #define UI_KEYS_I2C_BUTTON_LOW(pin,action_) if((keymask & pin)==0) action=action_;

    in ui.h

    u8glib is a independent display solution for graphic displays, which is why it has it's own I2C implementation. Normally you use the HAL-Version.

    For MCP23017 you have to configure which pins are in and out pins otherwise read will not show what you expect.

  • tried harder , but no success with the HAL version...
    if i use arduino wire library it works, may be there is a trap i couldn´t see
    can you tell me what´s wrong?
    // CODE:
    #include <Wire.h> //for arduino lib


    #define MCP_IN_ADDRESS 0x20
    #define MCP_OUT_ADDRESS 0x24


    #define MCP23017_IODIRA 0x00
    #define MCP23017_GPIOA 0x12


    #define MCP23017_IODIRB 0x01
    #define MCP23017_GPIOB 0x13

    #define MCP23017_KEYS_I2C_BUTTON_LOW(pin,action_) if((keymask1 & pin)==0) action=action_;

    // cannot get the following running . seems like watchdog timeout happens

    #define COMPILE_I2C_DRIVER


    HAL::i2cInit(100000);
    HAL::i2cStart(MCP_IN_ADDRESS<<1 + I2C_WRITE);
    HAL::i2cWrite(MCP23017_IODIRA); // GPIOA
    HAL::i2cWrite(0xFF); // all INPUTS
    HAL::i2cStop();
    HAL::i2cStart(MCP_IN_ADDRESS<<1 + I2C_WRITE);
    HAL::i2cWrite(MCP23017_IODIRB); // GPIOB
    HAL::i2cWrite(0xFF); // all INPUTS
    HAL::i2cStop();

    HAL::i2cStart(MCP_IN_ADDRESS<<1 + I2C_READ);

    uint16_t keymask1 = HAL::i2cReadAck();
    keymask1 = keymask1 + (HAL::i2cReadNak()<<8);

    HAL::i2cStop();
    */

    //###########################################################################
    //following code using arduino wire library is running well



    Wire.begin();
    Wire.beginTransmission(MCP_IN_ADDRESS);
    Wire.write(MCP23017_IODIRA);
    Wire.write(0xFF);
    Wire.endTransmission();
    Wire.beginTransmission(MCP_IN_ADDRESS);
    Wire.write(MCP23017_IODIRB);
    Wire.write(0xff);
    Wire.endTransmission();


    Wire.beginTransmission(MCP_IN_ADDRESS); //
    Wire.write(MCP23017_GPIOA);
    Wire.endTransmission();
    Wire.requestFrom(MCP_IN_ADDRESS, 2);

    uint16_t keymask1 = Wire.read();
    keymask1 = keymask1 + (Wire.read()<<8);


    //following witch 2nd MCP23017 just for testing

    //OUTPUTS
    Wire.beginTransmission(MCP_OUT_ADDRESS);
    Wire.write(MCP23017_IODIRA);
    Wire.write(0x00);
    Wire.endTransmission();
    Wire.beginTransmission(MCP_OUT_ADDRESS);
    Wire.write(MCP23017_IODIRB);
    Wire.write(0x00);
    Wire.endTransmission();


    Wire.beginTransmission(MCP_OUT_ADDRESS);
    Wire.write(MCP23017_GPIOA);
    Wire.write(keymask1>>8); // write something just to see that something happens
    Wire.endTransmission();
    //#############################################################################



    MCP23017_KEYS_I2C_BUTTON_LOW(_BV(1),UI_ACTION_OK); // push button, connects gnd to pin
    MCP23017_KEYS_I2C_BUTTON_LOW(_BV(3),UI_ACTION_BACK); // push button, connects gnd to pin
    MCP23017_KEYS_I2C_BUTTON_LOW(_BV(8),UI_ACTION_MENU_QUICKSETTINGS+UI_ACTION_TOPMENU); // push button, connects gnd to pin
    MCP23017_KEYS_I2C_BUTTON_LOW(_BV(15),UI_ACTION_MENU_EXTRUDER+UI_ACTION_TOPMENU); // push button, connects gnd to pin
    MCP23017_KEYS_I2C_BUTTON_LOW(_BV(6),UI_ACTION_MENU_POSITIONS+UI_ACTION_TOPMENU); // push button, connects gnd to pin


  • sorry, i forgot to copy some lines in not working code, now it´s complete

    #define COMPILE_I2C_DRIVER


    HAL::i2cInit(100000);
    HAL::i2cStart(MCP_IN_ADDRESS<<1 + I2C_WRITE);
    HAL::i2cWrite(MCP23017_IODIRA); // GPIOA
    HAL::i2cWrite(0xFF); // all INPUTS
    HAL::i2cStop();
    HAL::i2cStart(MCP_IN_ADDRESS<<1 + I2C_WRITE);
    HAL::i2cWrite(MCP23017_IODIRB); // GPIOB
    HAL::i2cWrite(0xFF); // all INPUTS
    HAL::i2cStop();

    HAL::i2cStart(MCP_IN_ADDRESS<<1 + I2C_WRITE);
    HAL::i2cWrite(MCP23017_GPIOA ); // GPIOA
    HAL::i2cStop();

    HAL::i2cStart(MCP_IN_ADDRESS<<1 + I2C_READ);

    uint16_t keymask1 = HAL::i2cReadAck();
    keymask1 = keymask1 + (HAL::i2cReadNak()<<8);

    HAL::i2cStop();

  • Don't write MCP_IN_ADDRESS<<1 but (MCP_IN_ADDRESS<<1) as it has lower priority then plus operator. You are computing wrong address this way.
  • edited March 2016
    sorry, no change. even if i write 0x40 it doesn´t work.
    i have due board + st7920 spi display+ encoder via direct(non i2c) pins.
    even if i just try to add i2c beeper without i2c buttons and without i2c encoder it doesn´t work.
    just pulled new files from github yesterday and only set beeper to i2c
    just one thing happens.. if i move the encoder  the firmware hangs and reboots after a few seconds

    any ideas?
  • Not really, the address issue was the only thing I saw. But it is ok to use wire library if that works. Did not know it was a due board and I'm quite sure I never had a I2C device on due, so maybe implementation is wrong for due. That part comes from a different developer.

    Reset will be watchdog as everything is blocked I think.

  • edited April 2016
    ok, thank you.
    as my coding knowledge is limited i´ll continue the due project with the wire library but
    I´ll try the code on a mega2560 and inform you the result
  • did some more testing now with just i2c beeper

    on mega2560 :

    MCP23017 doesn´t work , found a mismatch in ui.cpp line 72 :   HAL::i2cWrite( 0x14);
    changed to   HAL::i2cWrite( 0x12);

     beeper works now once, but it takes a few seconds without any action to get triggered again.

    switched over to PCF8574:
    beeper works as expected.

    seems there is something wrong with MCP23017 implementation



    switched over to DUE and PCF8574 :

    no beeper function , but no hanging firmware....

    anyway , I2C EEPROM works

    I´m totally confused now
  • I have to revide my statement as we use I2C eeprom and that works with HAL i2c implementation.

    PCF8574 is much easier to handle as it has no input/output register or anything else to configure.

    0x12 and ox14 is the same for writing. 0x14 is OLAT register and 0x12 GPIO = input, but wriring to input register puts values also in OLAT register.

    Also make sure you initialized all registers correctly before first use. Here what we do







        // set direction of pins

        HAL::i2cStart(UI_DISPLAY_I2C_ADDRESS + I2C_WRITE);

        HAL::i2cWrite(0); // IODIRA

        HAL::i2cWrite(~(UI_DISPLAY_I2C_OUTPUT_PINS & 255));

        HAL::i2cWrite(~(UI_DISPLAY_I2C_OUTPUT_PINS >> 8));

        HAL::i2cStop();

        // Set pullups according to  UI_DISPLAY_I2C_PULLUP

        HAL::i2cStart(UI_DISPLAY_I2C_ADDRESS+I2C_WRITE);

        HAL::i2cWrite(0x0C); // GPPUA

        HAL::i2cWrite(UI_DISPLAY_I2C_PULLUP & 255);

        HAL::i2cWrite(UI_DISPLAY_I2C_PULLUP >> 8);

        HAL::i2cStop();




  • edited April 2016
    ok, i´ll try,
    but why doesn´t work pcf8574 on due?
    i use same uiconfiguration on mega and due

    only difference i found :

    eeprom uses void HAL::i2cStartAddr(unsigned char address_and_direction, unsigned int pos)

    other i2c stuff uses : void HAL::i2cStartWait(unsigned char address_and_direction)
     respectively          : unsigned char HAL::i2cStart(unsigned char address_and_direction)

    these functions differ , but that´s out of my knowledge

  • How do you handle the voltage difference. PCF8574 wants 5V I2C and I think 100K baudrate. The eeprom wants 3.3V and 400K. For I2C you need special level shifter not the normal one - has something to do with the pullups to different voltages or something like that.
  • edited April 2016
    update: got pcf8574 running for beeper now ,  so hal seems to work but one question:
    firmware  hangs when i use #define UI_HAS_I2C_KEYS.

    so if i have non i2c buttons and i2c buttons ,  doesn´t that work?

    i´ll continue searching the next days , need a little break now
  • PCF8574 is specified from 2.5V to 6V ,Baudrate 100 K
    i know about the special level shifters , i use them in another application
    driving a Display on a 10m long wire through noisy environment (here i had to shift up to 12V and back to  5 after the wire).

    but to your question:

    settings for  EEProm (written in userpins.h:)

    #define SDA_PIN         20  
    #define SCL_PIN         21   

    #define TWI_CLOCK_FREQ          100000

    #define EEPROM_SERIAL_ADDR      0x50   // 7 bit i2c address (without R/W bit)
    #define EEPROM_PAGE_SIZE        64     // page write buffer size
    #define EEPROM_PAGE_WRITE_TIME  7      // page write time in milliseconds (docs say 5ms but that is too short)
    // specify size of eeprom address register
    // TWI_MMR_IADRSZ_1_BYTE for 1 byte, or TWI_MMR_IADRSZ_2_BYTE for 2 byte
    #define EEPROM_ADDRSZ_BYTES     TWI_MMR_IADRSZ_2_BYTE
    #define EEPROM_AVAILABLE 1



    so as i wrote above:  PCF8574 is running for beeper , just if i enable  #define UI_HAS_I2C_KEYS. firmware hangs ,
    but anyway i didn´t get the MCP23017 running .

    so my current status :

    I2c EEProm : running
    PCF8574 :running for Beeper

    MCP23017 not running via HAL routines  if i use Wire library its ok

    But using Wire seems to cause conflict to HALroutines , PCF8574 Beeper(HAL) gets not reliable.
    So I´m still searching using the try and error method
  • With UI_HAS_I2C_KEYS you need BEEPER_ADDRESS to be the same.

    I2C keys are handled in uiCheckSlowKeys and non i2c keys in uiCheckKeys and you can combine them. For the MCP keys and display are handled in the same address using the leftover pins for keys.

    Combining serveral I2c chips on one line is sometimes difficult as it seems, also it should be no problem with the different addresses. But I have not much experience with this. Normally only have one I2C chip for display.
















  • edited April 2016
    ok as i wrote before pcf8574 works for beeper , if i want to use it for i2c buttons firmware hangs.
    (beeper address is same as key address)

    if i replace the  HAL code in uiCheckSlowKeys by Wire code
    as follows beeperworks with HAL,Buttons with Wire code... Strange

    void uiCheckSlowKeys(uint16_t &action) {
    #if defined(UI_HAS_I2C_KEYS) && UI_HAS_KEYS!=0
    #if UI_DISPLAY_I2C_CHIPTYPE==0
     
         Wire.requestFrom(0x27, 1);
      uint8_t keymask = Wire.read();
        Wire.endTransmission();
     
       // HAL::i2cStartWait(UI_I2C_KEY_ADDRESS+I2C_READ);//key address is 0x4e
       // uint8_t keymask = HAL::i2cReadNak(); // Read current key mask
    #endif



    after that i changed code for beep , for a better overview i sorted code by beeper type
    and added routine for MCP23017 to PCF8574 beep routine.

    for my understanding the wire stuff should do exactly the same as Hal stuff,
    but for mcp 23017 only the wire version works

    see  code:

    #if BEEPER_TYPE==2

    #if UI_DISPLAY_I2C_CHIPTYPE==0

    //
    for(uint8_t i=0; i < count; i++)
    {
    HAL::i2cStartWait(BEEPER_ADDRESS +I2C_WRITE);

    #if BEEPER_ADDRESS == UI_DISPLAY_I2C_ADDRESS
    HAL::i2cWrite(uid.outputMask & ~BEEPER_PIN);
    #else
    HAL::i2cWrite(~BEEPER_PIN);
    #endif
    HAL::i2cStop();

    /*
    //######## compare #######
    //initialize MCP23017

    Wire.beginTransmission(0x24);
    Wire.write(0x00);//IODIRA
    Wire.write(0x00);//all OUTPUT
    Wire.write(0x00);//all OUTPUT
    Wire.endTransmission();

    //write to GPIOAB
    Wire.beginTransmission(0x24);
    Wire.write(0x12);//GPIOA
    Wire.write(255);// all on,Reg counter automatically to 0x13
    Wire.write(255);//all on
    Wire.endTransmission();

    //##### wire code runs the 23017 ######
    */


    //##### HAL CODE NOT #####

    // initialize MCP23017
    HAL::i2cStartWait(0x48 + I2C_WRITE);
    HAL::i2cWriting(0x00);//IODIRA
    HAL::i2cWriting(0x00);
    HAL::i2cWrite(0x00);
    HAL::i2cStop();

    //write to GPIOAB
    HAL::i2cStartWait(0x48 + I2C_WRITE);
    HAL::i2cWriting(0x12); // Start at port a
    HAL::i2cWriting(255);//(BEEPER_PIN) | uid.outputMask);
    HAL::i2cWrite(255);//((BEEPER_PIN) | uid.outputMask)>>8);
    HAL::i2cStop();

    //#######################

    HAL::delayMilliseconds(duration);

    HAL::i2cStartWait(BEEPER_ADDRESS +I2C_WRITE);

    #if BEEPER_ADDRESS == UI_DISPLAY_I2C_ADDRESS
    HAL::i2cWrite((BEEPER_PIN) | uid.outputMask);
    #else
    HAL::i2cWrite(255);
    #endif

    HAL::i2cStop();

    //######
    HAL::i2cStartWait(0x48 + I2C_WRITE);
    HAL::i2cWriting( 0x12); // Start at port a
    HAL::i2cWriting(0);
    HAL::i2cWrite(0);
    HAL::i2cStop();
    //######
    /*
    Wire.beginTransmission(0x24);
    Wire.write(0x12);
    Wire.write(0);
    Wire.write(0);
    Wire.endTransmission();

    */

    HAL::delayMilliseconds(duration);

    }

    #endif


    so for me now it seems  problem located in Hal... but i have no idea where. i tried to compare arduino wire.cpp with the Hal stuff but that is absolutely out of my coding limits.

    Do you have some ideas? or the guy who implemented the i2c for due?

    kind regards
  • for test i went back to mega2560 and got running PCF8574 beeper and buttons also communication to MCP23017 via HAL routines .

    On Due no way via HAL , just EEProm and Beeper onPCF8574
  • edited April 2016
    after learning to understand how the I2C implementation should work , found the problems in HAL routines now .
    there are a few changes necessary , to meet the specs in ATMEL Datasheet.

    The I2C EEprom works because of different/additional use of the existing HAL routines.

    I´ll send a pull request in the next days so you can decide to implement or not.

    I changed the routines to meet ATMEL Datasheet but unfortunately it will affect a couple of files which are
    momentarily not Hardware specific.

    To avoid that i think it´should possible to implement Arduinos Wire Library to keep code as it is,by just implementing
    the Wire stuff in HAL.


    Anyway , MCP23017 based controllers/Displays will not work with the actual I2C implementation, also reading PCF8574 is not possible.

    Kind regards,

    RAyWB
  • edited April 2016
    changed the code , major problem was missing start bit for reading i2c chips.(that´s why beeper worked on
    PCF8574, 1 Byte writing was possible)
    some errors in other i2c routines , removed for example writes to read-only registers.
    modified i2cwrite (removed return in i2cwrite as it is not used and blocked 2nd byte reading for mcp23017)
     removed i2cwriting,i2cfinished,i2ccompleted routines(added their code to the other routines,
    so code is now working 1:1 to the AVR stuff,differences only in HAL no further modifications necessary.
    (depends on changing the i2cwrite and removing i2cwriting).

    test setup 2*MCP23017,1*PCF8574,1*24LC256 , working together, no conflicts.
    (i use arduino version 1.67 and 1.68 to compile , no problems).

    changes commented in code, see pull request #515

    kind regards,

    RAyWB
  • Merged in latest master version.
Sign In or Register to comment.