combining I2C class with low level pin access

23 Apr 2011

Hello all,

I am currently porting the Sensirion code for the SHT21 sensor to mbed. I have "No Hold Master" mode working just fine using the mbed I2C class and now I am working on the "Hold Master" mode. For this to work I need access to the sda and scl pins used by the I2C class on mbed so I can perform the following

"In the hold master mode, the SHT2x pulls down the SCL line while measuring to force the master into a wait state. By releasing the SCL line the sensor indicates that internal processing is terminated and that transmission may be continued."

I have tried something like

    //-- wait until hold master is released --
    i2c.start();
    i2c.write(I2C_ADR_R);

    //SCL=HIGH;                     // set SCL I/O port as input
    scl.input();

    for (i=0; i<1000; i++) {      // wait until master hold is released or
        wait_ms(1);    // a timeout (~1s) is reached
        if (scl.read() == 1) break;
    }

    //-- check for timeout --
    if (scl.read() == 0) error |= TIME_OUT_ERROR;
    //-- read two data bytes and one checksum byte --

but no joy, the system hangs, not sure where yet.

Anyone have any suggestions?

Thanks, Serge

23 Apr 2011

According to the manual SCL can be p10 or p27 depending upon which I2C you are using. You can read the pin value for either of these with:-

#define p10_IS_SET      (LPC_GPIO0->FIOPIN & (1UL <<  1)) ? 1 : 0
#define p27_IS_SET      (LPC_GPIO0->FIOPIN & (1UL << 11)) ? 1 : 0

The macros are 1 of the pin is high or 0 if the pin is low.

However, not knowing what the Mbed/LPC17xx i2c library/peripheral is doing, you may be reading the value being driven out if SCL is set as an output by the i2c library/peripheral.

23 Apr 2011

Hello Andy,

Thanks, it probably is an output but the point is I need to force it as an input for a brief period, then read and then I can set it back to an output.

The alternative I guess is to not use the mbed I2C library and do it all myself :-(

Or just not use the "Hold Master" mode of the SHT21.

Thanks, Serge

23 Apr 2011

I have no idea if this will work or not but you could try it rather than make your own I2C lib.

The following creates a new class SHT21_I2C which inherits the standard I2C. This new class adds 3 new methods:-

void sclAsInput(void); Switches SCL to GPIO input

void sclNormal(void); Restores SCL to I2C mode

int sclRead(void); Reads the SCL pin when a GPIO.

Try it and see if it works. Could save you some time :)

SHT21_I2C.h

/*
    Copyright (c) 2010 Andy Kirkham
 
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
 
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
 
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/

#include "mbed.h"

#ifndef SHT21_I2C_H
#define SHT21_I2C_H

class SHT_I2C : public I2C {

protected:
    PinName _scl;
    PinName _sda;
    
public:

    SHT_I2C(PinName sda, PinName scl, const char *name = NULL) : I2C(sda, scl, name) {
        _sda = sda;
        _scl = scl;
    }
    
    void sclAsInput(void) {
        switch(_scl) {
            case p10:
                LPC_PINCON->PINSEL0 &= ~(3UL << 2); // p10, P0.1 as GPIO
                LPC_GPIO0->FIODIR &= ~(1UL << 1);   // p10, P0.1 as Input
                break;
            case p27:
                LPC_PINCON->PINSEL0&=~(3UL << 22);  // p27, P0.11 as GPIO
                LPC_GPIO0->FIODIR &= ~(1UL << 11);  // p27, P0.11 as input
                break;                
        }
    }
    
    int sclRead(void) {
        switch(_scl) {
            case p10: return (LPC_GPIO0->FIOPIN & (1UL <<  1)) ? 1 : 0;
            case p27: return (LPC_GPIO0->FIOPIN & (1UL << 11)) ? 1 : 0;                
        }
    }
    
    void sclNormal(void) {
        switch(_scl) {
            case p10:
                LPC_PINCON->PINSEL0 &= ~(3UL << 2); // p10, P0.1 as I2C SCL1
                LPC_PINCON->PINSEL0 |=  (3UL << 2);
                break;
            case p27:
                LPC_PINCON->PINSEL0 &= ~(3UL << 22); // p27, P0.11 as I2C SCL2
                LPC_PINCON->PINSEL0 |=  (2UL << 22);            
                break;                
        }
    }

};

#endif
23 Apr 2011

Coming from a Java background I should have though of that ;-)

Thanks, the code runs but the value is wrong, I need to stick a logic analyzer to this now and see if I can spot something.

Serge

23 Apr 2011

OK, well, at least it may point you in the direction of a simpler solution. I couldn't try that above, it was more psuedo code to be honest. Have a read of the manual esp the section 8.5 PINSELx to see how the pin functions are manipulated. That, combined with the above should get you closer to what you need.

23 Apr 2011

Ok Andy,

Got that working now, I had a printf() statement for debugging purposes badly placed and it threw off the timing.

Many many thanks for this!

Serge

23 Apr 2011