TWI for n00bs!

Go To Last Post
46 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have been wanting to learn the TWI stuff for some time now and now the time has come, the thing is the tutorials I read don't really cut it for me so I thought I would start a thread and maybe some nice freak will be able to hold my hand!

 

I am reading this

 

http://www.embedds.com/programmi...

 

I have a sensor like this one

 

http://www.farnell.com/datasheet...

 

Its address is 0x28 and I will operate it at 100kHz

 

I am using an ATMega328 @8MHz (RC oscillator)

 

prescale of 1 and TWBR 0f 32

 

So my init function is
 

void TWIInit(void)
{
	//set SCL to 100kHz
	TWSR = 0x00; //Prescale=1;
	TWBR = 0x20; //decimal 32, 8MHz clock=100kHz TWI
	//enable TWI
	TWCR = (1<<TWEN);
}

My start and stop functions are unaltered

 

void TWIStart(void)
{
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
    while ((TWCR & (1<<TWINT)) == 0);
}
//send stop signal
void TWIStop(void)
{
    TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

 

 

Then theres three other functions that I need to use

 


uint8_t TWIReadACK(void)
{
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
	while ((TWCR & (1<<TWINT)) == 0);
	return TWDR;
}
//read byte with NACK
uint8_t TWIReadNACK(void)
{
	TWCR = (1<<TWINT)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);
	return TWDR;
}

uint8_t TWIGetStatus(void)
{
	uint8_t status;
	//mask status
	status = TWSR & 0xF8;
	return status;
}

One is a read with acknowledge the other is a read without acknowledge

 

Finally theres a get status function

 

I am unsure how to piece this all together, let me try now......

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is there some reason for not simply using Fleury? Or is this just a TWI learning experience where you want to understand how he honed that perfectly circular wheel?

 

If nothing else compare what you are doing with what Peter Fleury does to see where you are going wrong.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As usual I am just wanting to learn but I am up for whatever is easiest to do that so Fleury sounds good

 

Theres so many articles out there and I think I just need to read data so I am wanting simple

 

I probably could read and do this but I like a discussion and to see what the freaks think

 

Edit

 

Fleurys code is spread over different libraries, I hate this, I am such a noob I write my entire programs on one page and I know I should get into the swing with headers etc its just I struggle to follow programs that are spread over multiple files

 

 

Last Edited: Wed. Mar 11, 2015 - 10:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fleurys code is spread over different libraries,

No it isn't. His TWI code is in one .zip here:

 

http://homepage.hispeed.ch/peter...

 

In that you get a choice as there are two implementations. One for when you have a TWI and can use it (the "easy" one?) and one for when I2C must be bit-banged on port pins.

As you have TWI hardware in your AVR you would use just two files:  twimaster.c and the accompanying i2cmaster.h

 

The documentation is here:

 

http://homepage.hispeed.ch/peter...

 

(you'll see that most of that comes from i2cmaster.h in fact).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry for being thick here Cliff, theres a bit to take in and its unclear what I need the fleury stuff is confusing me

 

I think the easiest way is for me to adapt this

 

#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#include <avr/io.h>
#include <util/twi.h>
#include "I2C_master.h"
#define F_SCL 100000UL // SCL frequency
#define Prescaler 1
#define TWBR_val ((((F_CPU / F_SCL) / Prescaler) - 16 ) / 2)
void I2C_init(void){
TWBR = TWBR_val;
}
uint8_t I2C_start(uint8_t address){
// reset TWI control register
TWCR = 0;
// transmit START condition
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
// wait for end of transmission
while( !(TWCR & (1<<TWINT)) );
// check if the start condition was successfully transmitted
if((TWSR & 0xF8) != TW_START){ return 1; }
// load slave address into data register
TWDR = address;
// start transmission of address
TWCR = (1<<TWINT) | (1<<TWEN);
// wait for end of transmission
while( !(TWCR & (1<<TWINT)) );
// check if the device has acknowledged the READ / WRITE mode
uint8_t twst = TW_STATUS & 0xF8;
if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;
return 0;
}
void I2C_write(uint8_t data){
// load data into data register
TWDR = data;
// start transmission of data
TWCR = (1<<TWINT) | (1<<TWEN);
// wait for end of transmission
while( !(TWCR & (1<<TWINT)) );
if( (TWSR & 0xF8) != TW_MT_DATA_ACK ){ return 1; }
return 0;
}
uint8_t I2C_read_ack(void){
// start TWI module and acknowledge data after reception
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
// wait for end of transmission
while( !(TWCR & (1<<TWINT)) );
// return received data from TWDR
return TWDR;
}
uint8_t I2C_read_nack(void){
// start receiving without acknowledging reception
TWCR = (1<<TWINT) | (1<<TWEN);
// wait for end of transmission
while( !(TWCR & (1<<TWINT)) );
// return received data from TWDR
return TWDR;
}
void I2C_stop(void){
// transmit STOP condition
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);

The above makes more sense than anything else right now

 

OMG!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd suggest getting acquainted with the i2c spec. This one's an older one as the latest has a lot more gumf in it that probably isn't relevant for most apps.

http://www.cs.unc.edu/Research/s...

 

If you want a learning experience, write a bit-bashed version -  that way you get down and dirty with the required sequences. It's not difficult at all.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

// load data into data register
TWDR = data;
// start transmission of data
TWCR = (1<<TWINT) | (1<<TWEN);
// wait for end of transmission
while( !(TWCR & (1<<TWINT)) );
if( (TWSR & 0xF8) != TW_MT_DATA_ACK ){ return 1; }
return 0;

  return' with a value, in function returning void   

Obviously I am declaring the function void then trying to return a value, well I could fix it by changing the function declaration but what the hell, why is the code written like this?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The above makes more sense than anything else right now

What on earth happened to the indentation?

 

Anyway despite staring at that intently I don't see main(). Like Fleury says a simple I2C using main looks like:

int main(void)
{
  unsigned char ret;
  i2c_init(); // initialize I2C library

  // write 0x75 to EEPROM address 5 (Byte Write)
  i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode
  i2c_write(0x05); // write address = 5
  i2c_write(0x75); // write value 0x75 to EEPROM
  i2c_stop(); // set stop conditon = release bus

  // read previously written value back from EEPROM address 5
  i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode

  i2c_write(0x05); // write address = 5
  i2c_rep_start(Dev24C02+I2C_READ); // set device address and read mode

  ret = i2c_readNak(); // read one byte from EEPROM
  i2c_stop();

  for(;;);
}

I imagine you have something similar? What I'm suggesting is that you replace your own library I2C_init(), I2C_start() and so on with calls to the Fleury code instead and see how you get on. The difference is that this is know working code so it removes that "unknown" from the jig-saw.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you want a learning experience

Lol

 

Well I do but I have this sensor on my desk right now, if I could get this going then I can prove the feasibility and really open up the gates to fun at work

 

So I want to learn but at the minute I would accept it just working and then if this sensor is feasible I then get much more time to get into it later

 

I feel like that guy who posts a one line thread basically wanting the freaks to do the work, I am him right now because I have so much on its literally unbelievable!

 

 

I imagine you have something similar

Cliff sorry to dissapoint but I have nothing, I have whats posted in this thread the idea was to learn as I go but right now I think this will take me way longer than I have

 

Sorry for being a lazy git, its not lazy its over worked!

Last Edited: Wed. Mar 11, 2015 - 11:15 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The official I2C specification is here: http://www.nxp.com/documents/use...

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you just want to get something to 'work', then the likes of Arduino and MBED are my choice. Pretested i2c libraries and most likely some sample code. You get things working in a couple of minutes. Then you can fiddle to your heart's content. 

 

I can recommend the Salae logic analyser if you're fiddling with i2c, it is invaluable. You can get Chinese copies for very little$$$ but the real one is fairly priced and the software is excellent. It decodes i2c,spi and async.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman, I have the Salae its really good, I have all the gear but no idea!

 

I have some really good equipment, I have equipment that is awesome just not what you expect to find in a hobbyists collection

 

Look I am on this now, I will make it work

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I suggest that you use the Fleury library.   Note that it expects an 8-bit Slave address.

Or you use the Arduino <Wire.h> library.    This uses a 7-bit address.

 

With any library,   always use the return values from a function.

 

In practice,   any I2C transaction is just writing X bytes to the SLAVE_W address and reading Y bytes from the SLAVE_R address.

You can achieve this with a single universal function.     (polled or interrupt)

You can construct this 'universal Master' from the primitives in Fleury.

 

Note that the Arduino <Wire.h> defaults to fairly small buffers for any 'block read or write'.

 

Achieve familiarity with proven libraries before you start creating your own 'home-grown' version.    You will have something to campare with.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Cliff sorry to dissapoint but I have nothing,

Then surely that fleury example main() provides a perfect starting place to learn how to use the API?

 

But I'm a bit confused. If the code you showed previously was your own code how were you testing it as you implemented it? Or were you just writing it all "theoretically" and hoping it would all work immediately on the day you put a main() together to call the functions? I'd suggest that perhaps isn't the best of development techniques.

 

Personally I always start on any micro by getting some form of rudimentary UART communication working then I can implement a menu:

1 test i2c_init()
2 test i2c_start()
3  etc...

and then as I implement each function I can test it as I go along (well OK, some routines may be inter-dependent so I may implement a group then test them all together). With a logic analyser you could be observing the wires and ensuring that the AVR is doing what you expect as you try menu option 1, 2, 3...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

David I was hoping you would drop in here, yopur never far away from a TWI thread!

 

lets go for Fleury

 

Now I am confused because all the examples I can find use EEPROMS etc where they are reading and writing, I have a sensor which I just want to read so thats master receiver mode

 

So I look at Fleury

 

int main(void)
{
  unsigned char ret;
  i2c_init(); // initialize I2C library

  // write 0x75 to EEPROM address 5 (Byte Write)
  i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode
  i2c_write(0x05); // write address = 5
  i2c_write(0x75); // write value 0x75 to EEPROM
  i2c_stop(); // set stop conditon = release bus

  // read previously written value back from EEPROM address 5
  i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode

  i2c_write(0x05); // write address = 5
  i2c_rep_start(Dev24C02+I2C_READ); // set device address and read mode

  ret = i2c_readNak(); // read one byte from EEPROM
  i2c_stop();

  for(;;);
}

I change the names but I am confused big style

 

So I have this so far

 

#define F_CPU          8000000
#define TWI_FREQ       100000
#define DEVICE_ADDRESS 0x28

#define I2C_READ 1
#define I2C_WRITE 0

int main(void)
{
	unsigned char ret;
	TWIInit();                     // initialize I2C library
	TWIStart_wait(DEVICE_ADDRESS+I2C_WRITE); // set device address and write mode
	TWIWrite(0x05);               // write address = 5
	TWIWrite(0x75);               // write value 0x75 to EEPROM
	TWIStop();                    // set stop conditon = release bus

	// read previously written value back from EEPROM address 5
	TWIStart_wait(Dev24C02+I2C_WRITE); // set device address and write mode

	TWIWrite(0x05); // write address = 5
	i2c_rep_start(Dev24C02+I2C_READ); // set device address and read mode

	ret = i2c_readNak(); // read one byte from EEPROM
	i2c_stop();

	for(;;);
}





void TWIInit(void)
{
	
	TWSR = 0x00; //Prescale=1;
	TWBR = ((F_CPU/TWI_FREQ)-16)/2;;  //set TWI Freq to 100kHz
	TWCR = (1<<TWEN);                 //enable TWI
}
void TWIStart(void)
{
	TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);
}


void TWIStop(void)
{
	TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

void TWIWrite(uint8_t u8data)
{
	TWDR = u8data;
	TWCR = (1<<TWINT)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);
}

uint8_t TWIReadACK(void)
{
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
	while ((TWCR & (1<<TWINT)) == 0);
	return TWDR;
}

uint8_t TWIReadNACK(void)
{
	TWCR = (1<<TWINT)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);
	return TWDR;
}

uint8_t TWIGetStatus(void)
{
	uint8_t status;
	//mask status
	status = TWSR & 0xF8;
	return status;
}

void TWIStart_wait(unsigned char address)
{
	uint8_t   twst;
	while ( 1 )
	{
		TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);	// send START condition
		while(!(TWCR & (1<<TWINT)));	           // wait until transmission completed 
		twst = TW_STATUS & 0xF8;                   // check value of TWI Status Register. Mask prescaler bits.
		
		if ( (twst != TW_START) && (twst != TW_REP_START)) continue;
		TWDR = address;                        // send device address
		TWCR = (1<<TWINT) | (1<<TWEN);
		while(!(TWCR & (1<<TWINT)));           // wail until transmission completed
		twst = TW_STATUS & 0xF8;               // check value of TWI Status Register. Mask prescaler bits.
		if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) )
		{
			TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);  //* device busy, send stop condition to terminate write operation */
			while(TWCR & (1<<TWSTO));                    // wait until stop condition is executed and bus released
			
			continue;
		}
		
		break;
	}

}

I understand the initialisation its really simple

 

TWIStart_wait(DEVICE_ADDRESS+I2C_WRITE); // set device address and write mode

I dont understand this part, I start the TWI then write the device adress followed by a 0 for write mode I think the difference between the other start function is just it looks for an ACK bit

 

        TWIWrite(0x05);               // write address = 5
	TWIWrite(0x75);               // write value 0x75 to EEPROM

These are write so I dont think I need them

 

So I have this piece of crap so far

 

#include <util/twi.h>


#define F_CPU          8000000
#define TWI_FREQ       100000
#define DEVICE_ADDRESS 0x28

#define I2C_READ 1
#define I2C_WRITE 0

void TWIInit(void);
unsigned char TWIStart(void);
void TWIStop(void);
void TWIWrite(uint8_t u8data);
uint8_t TWIReadACK(void);
uint8_t TWIReadNACK(void);
uint8_t TWIGetStatus(void);
void TWIStart_wait(unsigned char address);
unsigned char TWIRep_start(unsigned char address);


int main(void)
{
	unsigned char ret;
	TWIInit();                     // initialize I2C library
	TWIStart_wait(DEVICE_ADDRESS+I2C_WRITE); // set device address and write mode
	TWIWrite(0x05);               // write address = 5
	TWIWrite(0x75);               // write value 0x75 to EEPROM
	TWIStop();                    // set stop conditon = release bus

	// read previously written value back from EEPROM address 5
	TWIStart_wait(DEVICE_ADDRESS+I2C_WRITE); // set device address and write mode

	TWIWrite(0x05); // write address = 5
	TWIRep_start(DEVICE_ADDRESS+I2C_READ); // set device address and read mode

	ret = TWIReadNACK(); // read one byte from EEPROM
	TWIStop();

	for(;;);
}





void TWIInit(void)
{
	
	TWSR = 0x00; //Prescale=1;
	TWBR = ((F_CPU/TWI_FREQ)-16)/2;;  //set TWI Freq to 100kHz
	TWCR = (1<<TWEN);                 //enable TWI
}
unsigned char TWIStart(void)
{
	TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);
}


void TWIStop(void)
{
	TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

void TWIWrite(uint8_t u8data)
{
	TWDR = u8data;
	TWCR = (1<<TWINT)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);
}

uint8_t TWIReadACK(void)
{
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
	while ((TWCR & (1<<TWINT)) == 0);
	return TWDR;
}

uint8_t TWIReadNACK(void)
{
	TWCR = (1<<TWINT)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);
	return TWDR;
}

uint8_t TWIGetStatus(void)
{
	uint8_t status;
	//mask status
	status = TWSR & 0xF8;
	return status;
}

void TWIStart_wait(unsigned char address)
{
	uint8_t   twst;
	while ( 1 )
	{
		TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);	// send START condition
		while(!(TWCR & (1<<TWINT)));	           // wait until transmission completed 
		twst = TW_STATUS & 0xF8;                   // check value of TWI Status Register. Mask prescaler bits.
		
		if ( (twst != TW_START) && (twst != TW_REP_START)) continue;
		TWDR = address;                        // send device address
		TWCR = (1<<TWINT) | (1<<TWEN);
		while(!(TWCR & (1<<TWINT)));           // wail until transmission completed
		twst = TW_STATUS & 0xF8;               // check value of TWI Status Register. Mask prescaler bits.
		if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) )
		{
			TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);  //* device busy, send stop condition to terminate write operation */
			while(TWCR & (1<<TWSTO));                    // wait until stop condition is executed and bus released
			
			continue;
		}
		
		break;
	}

}

unsigned char TWIRep_start(unsigned char address)
{
	return TWIStart(address);

}

And I am going to start again, I was trying to change the names around but I don't know enough

 

Back to the very start

 

 

Last Edited: Wed. Mar 11, 2015 - 02:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I have this so far

If it is a read-only sensor I cannot help thinking there may be something wrong here:

	TWIStart_wait(Dev24C02+I2C_WRITE); // set device address and write mode

In I2C devices have two addresses - one for read and one for write. The example code may:

#define DEVICE_ADDRESS 0x28

but there's also:

#define I2C_READ 1
#define I2C_WRITE 0

so it's using 0x28 for write and 0x29 for read.

 

EDIT: BTW I went looking for a "proper" datasheet for that sensor to find out what commands/data it uses and so on but even on the manufacturer website:

 

http://www.hygrochip.com/index.p...

 

It seems they only have that same 3 page summary sheet you linked to at Farnell.

 

I can't help thinking it's going to be a challenge to learn TWI/I2C using a device that is so poorly documented. There are a lot of heavily documented devices (DS1307 alarms, 24LCxx EEPROMs, etc) that might be a much better place to start learning I2C then pass that knowledge on to this humidity sensor when you know all about I2C

Last Edited: Wed. Mar 11, 2015 - 02:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff I really appreciate your patience here

 

I am looking at the datasheet for my sensor 

 

http://www.ist-ag.com/eh/ist-ag/...$FILE/AHHYTM_E2.2.1.compressed.pdf

 

it talks about measurement requests and data fetch

 

For measurement request it looks like I need to

 

Start I2C

Device slave address

Write

Wait for acknowledge

 

For data fetch

 

Start I2C

Slave address

Read

Wait for acknowledge

 

So I need to get my head around this and I have started again with fleury

 

I have this

 

#include <util/twi.h>
#define F_CPU          8000000
#define TWI_FREQ       100000
#define DEVICE_ADDRESS 0x28
#define I2C_READ 1
#define I2C_WRITE 0
 
void i2c_init(void);
void i2c_stop(void);
unsigned char i2c_start(unsigned char addr);
unsigned char i2c_rep_start(unsigned char addr);
void i2c_start_wait(unsigned char addr);
unsigned char i2c_write(unsigned char data);
unsigned char i2c_readAck(void);
unsigned char i2c_readNak(void);
unsigned char i2c_read(unsigned char ack);



int main(void)
{
	//unsigned char ret;
	i2c_init(); // initialize I2C library

	// write 0x75 to EEPROM address 5 (Byte Write)
	i2c_start_wait(DEVICE_ADDRESS+I2C_WRITE); // set device address and write mode
	i2c_write(0x05); // write address = 5
	i2c_write(0x75); // write value 0x75 to EEPROM
	i2c_stop(); // set stop conditon = release bus

	// read previously written value back from EEPROM address 5
	i2c_start_wait(DEVICE_ADDRESS+I2C_WRITE); // set device address and write mode

	i2c_write(0x05); // write address = 5
	i2c_rep_start(DEVICE_ADDRESS+I2C_READ); // set device address and read mode

	//unsigned char ret = i2c_readNak(); // read one byte from EEPROM
	i2c_stop();

	for(;;);
}

/*************************************************************************
 Initialization of the I2C bus interface. Need to be called only once
*************************************************************************/
void i2c_init(void)
{
  /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
  
  TWSR = 0;                         /* no prescaler */
  TWBR = ((F_CPU/TWI_FREQ)-16)/2;  /* must be > 10 for stable operation */

}/* i2c_init */


/*************************************************************************	
  Issues a start condition and sends address and transfer direction.
  return 0 = device accessible, 1= failed to access device
*************************************************************************/
unsigned char i2c_start(unsigned char address)
{
    uint8_t   twst;

	// send START condition
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

	// wait until transmission completed
	while(!(TWCR & (1<<TWINT)));

	// check value of TWI Status Register. Mask prescaler bits.
	twst = TW_STATUS & 0xF8;
	if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;

	// send device address
	TWDR = address;
	TWCR = (1<<TWINT) | (1<<TWEN);

	// wail until transmission completed and ACK/NACK has been received
	while(!(TWCR & (1<<TWINT)));

	// check value of TWI Status Register. Mask prescaler bits.
	twst = TW_STATUS & 0xF8;
	if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;

	return 0;

}/* i2c_start */


/*************************************************************************
 Issues a start condition and sends address and transfer direction.
 If device is busy, use ack polling to wait until device is ready
 
 Input:   address and transfer direction of I2C device
*************************************************************************/
void i2c_start_wait(unsigned char address)
{
    uint8_t   twst;


    while ( 1 )
    {
	    // send START condition
	    TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
    
    	// wait until transmission completed
    	while(!(TWCR & (1<<TWINT)));
    
    	// check value of TWI Status Register. Mask prescaler bits.
    	twst = TW_STATUS & 0xF8;
    	if ( (twst != TW_START) && (twst != TW_REP_START)) continue;
    
    	// send device address
    	TWDR = address;
    	TWCR = (1<<TWINT) | (1<<TWEN);
    
    	// wail until transmission completed
    	while(!(TWCR & (1<<TWINT)));
    
    	// check value of TWI Status Register. Mask prescaler bits.
    	twst = TW_STATUS & 0xF8;
    	if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) ) 
    	{    	    
    	    /* device busy, send stop condition to terminate write operation */
	        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
	        
	        // wait until stop condition is executed and bus released
	        while(TWCR & (1<<TWSTO));
	        
    	    continue;
    	}
    	//if( twst != TW_MT_SLA_ACK) return 1;
    	break;
     }

}/* i2c_start_wait */


/*************************************************************************
 Issues a repeated start condition and sends address and transfer direction 

 Input:   address and transfer direction of I2C device
 
 Return:  0 device accessible
          1 failed to access device
*************************************************************************/
unsigned char i2c_rep_start(unsigned char address)
{
    return i2c_start( address );

}/* i2c_rep_start */


/*************************************************************************
 Terminates the data transfer and releases the I2C bus
*************************************************************************/
void i2c_stop(void)
{
    /* send stop condition */
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
	
	// wait until stop condition is executed and bus released
	while(TWCR & (1<<TWSTO));

}/* i2c_stop */


/*************************************************************************
  Send one byte to I2C device
  
  Input:    byte to be transfered
  Return:   0 write successful 
            1 write failed
*************************************************************************/
unsigned char i2c_write( unsigned char data )
{	
    uint8_t   twst;
    
	// send data to the previously addressed device
	TWDR = data;
	TWCR = (1<<TWINT) | (1<<TWEN);

	// wait until transmission completed
	while(!(TWCR & (1<<TWINT)));

	// check value of TWI Status Register. Mask prescaler bits
	twst = TW_STATUS & 0xF8;
	if( twst != TW_MT_DATA_ACK) return 1;
	return 0;

}/* i2c_write */


/*************************************************************************
 Read one byte from the I2C device, request more data from device 
 
 Return:  byte read from I2C device
*************************************************************************/
unsigned char i2c_readAck(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
	while(!(TWCR & (1<<TWINT)));    

    return TWDR;

}/* i2c_readAck */


/*************************************************************************
 Read one byte from the I2C device, read is followed by a stop condition 
 
 Return:  byte read from I2C device
*************************************************************************/
unsigned char i2c_readNak(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN);
	while(!(TWCR & (1<<TWINT)));
	
    return TWDR;

}/* i2c_readNak */

Let me go through  it again

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I foolishly installed Acrobat-XI on this Vista-32 Laptop.    It is appallingly slow.    Every time that you open a PDF,   you have to wait for about 30 secs before the CPU has got any time for your input.

 

Anyway,   the HY-221 data sheet is not impossible.   There is an Arduino sketch on the makers website.

So to start with.   The 7-bit address is 0x28.   This means the 8-bit SLAVE_W is 0x50 and SLAVE_R is 0x51.

 

Any self-respecting library will return an 'error' for using 0x28/0x29 with i2c_start().

 

You simply do:

       ret = i2c_start(0x51);
       if (ret != 0) printf("oh sh1t,  we have wrong slave address");
       capH = i2c_read(1);
       capL = i2c_read(1);
       tempH = i2c_read(1);
       tempL = i2c_read(0);
       i2c_stop();

There is a C file too.   I did not read it.    So my 'example' is pure guesswork and untested.

 

But I seriously advise you to stick with a respected library.   

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm glad I'm not using that chip. I don't see any link on any of the tabs here:

 

http://www.hygrochip.com/index.p...

 

that link to app note you now showed (though admittedly that does look pretty good!).

 

Anyway I had another thought. Almost every peripheral chip in the world has been added to Arduino so I tried a Google for "HYT-221 Arduino".

 

That got me here:

 

http://forum.arduino.cc/index.ph...

 

and in turn here:

 

http://www.hygrochip.com/index.p...

 

That page has both an Arduino sketch (PDE) and some C code.

 

now the C code seems almost entirely hopeless - it's incomplete and shows nothing about the I2C dialog with the sensor. But the Arduino code is much more useful:

#include <Wire.h>

//set the adress of sensor:
int adress=0x28;

void setup()
{ //set arduino as master:
  Wire.begin();
  //set baudrate:
  Serial.begin(38400);
  /*this code sets
 databits:8 ; parity:ODD ; stopbits:2*/
  UCSR0C = B00111110;
}
void loop()
{ //declaration of variables:
  unsigned int valuetempH=0;
  unsigned int valuetempL=0;
  unsigned int valuetemp=0;
  unsigned int valuecapH=0;
  unsigned int valuecapL=0;
  unsigned int valuecap=0;
  double endvaluecap=0;
  double endvaluetemp=0;
  
  //DataFetch(4 byte):
  Wire.requestFrom(adress,4);
  //wait for 4 bytes available:   
  if(Wire.available()>3)
     //receive the databytes:
     {valuecapH=Wire.receive();
      valuecapL=Wire.receive();
      valuetempH=Wire.receive();
      valuetempL=Wire.receive();   
      Wire.endTransmission();
      
      //mask the statusbits
      valuecapH = (valuecapH&B00111111);
      valuetempL= (valuetempL&B11111100);
      
      /*put together the highbytes and lowbytes
      For this, the highbyte gets moved 8 bits higher*/ 
      valuecap  = (valuecapH*256 + valuecapL);
      /*the temperature highbyte gets moved 6 bits
      and the lowbyte 2 bits lower*/ 
      valuetemp = ((valuetempH)*64 + valuetempL/4);
      
      //formate the values
      endvaluecap=(double(valuecap))/163.83;
      endvaluetemp=((double(valuetemp))/163.83)/2;
    
    //output the values compatible with PCLog:
    Serial.print(endvaluecap);
    Serial.print("\t");
    Serial.println(endvaluetemp);}
    
    //in case of no receiving possible:
   else 
     {Serial.println("no data");}
  
  
  delay(100);
}

So that seems to suggest you need to read 4 bytes from address 0x28 (so I guess 0x28 *is* the read address as there is no write support). The four bytes are:

      valuecapH=Wire.receive();
      valuecapL=Wire.receive();
      valuetempH=Wire.receive();
      valuetempL=Wire.receive();   

in that order. There's the equations showing how to manipulate these H/L values. Of course the code gives no idea of what the significance of 163.83 is but maybe that app note says?

 

EDIT: David beat me to it ;-)

Last Edited: Wed. Mar 11, 2015 - 03:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Searching for 'hygrochip', the 3rd or 4th entry was the I2C PDF from them. Got that?

 

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The 7-bit address is 0x28.   This means the 8-bit SLAVE_W is 0x50 and SLAVE_R is 0x51

Now I am confused!

 

the 7bit address is definitely 0x28, I thought to read I add 1 and to write I add 0

 

So surely the Slave write address is 0x28 and the read address is 0x28, I don't understand how you got 0x50

 

ret = i2c_start(0x51);
       if (ret != 0) printf("oh sh1t,  we have wrong slave address");
       capH = i2c_read(1);
       capL = i2c_read(1);
       tempH = i2c_read(1);
       tempL = i2c_read(0);
       i2c_stop();

Your making it look easy!, I really don't know how to make my code do the above

 

But I seriously advise you to stick with a respected library.   

I will use this Fleury until its clear

 

I'm glad I'm not using that chip

Damn it, I thought I was just being thick!

 

 

That page has both an Arduino sketch

I hate the Arduino stuff, its just garbage admittedly it does make things easier but I just hate it

 

So that seems to suggest you need to read 4 bytes from address 0x28

yeah, its just like another SPI sensor I use (reme,ber the thread?!) so I have no issues converting the numbers etc, I just need to get the data into an array or whatever, in for a penny in for a pound I am not giving up

 

Let me reshape it up a bit

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Got that

I didnt but what I did have was something very similar

 

Guys I really appreciate all your input here, I am getting somewhere

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I hate the Arduino stuff, its just garbage admittedly it does make things easier but I just hate it

Then I fear you have completely misunderstood Arduino!

 

What Arduino does is hide all the low level nonsense. I have no idea what Wire.begin(), Wire.requestFrom(), Wire.available(), Wire.receive() are doing but they have simple interfaces and it means that the code in loop() is just the few high level steps required to interact with this chip. I didn't need to read any datasheet or app note or anything like that for this Arduino sketch to tell me that you star ta dialog with the chip, read 4 bytes from it and you are done.

 

in that sense Arduino is actually very powerful because (as C+= has a tendency to do) it "layers" the code so that at one level you just concern yourself with the very most highlevel operation and don't mess things up with a load of "while(!(TWCR & (1<<TWINT)));" or similar nonsense - that's all hidden in the implementation.

 

The one stick you can beat the Arduino library code with is that a few parts of the implementation of some of the low level access (and I'm thinking pinMode and digitalWrite) are sub-optimal and it rankles with "professionals" to think they might have been able to implement it more efficiently. But even when you consider pinMode() and digitaWrite() you are looking at an abstraction. No longer are you doing PORTB |= (1 << 5) but simply digitalWrite(13, ON) or whatever which makes the higher level code more readable and hence more maintainable.

 

So don't discount Arduino as a research source. If I wanted to know how to operate a DS1307 or an AT45DB161 or something there's a strong chance I'd take a peek at some Arduino code to see what the high level steps to success were then worry about the low level implementation of  how I get those steps to the chip later.

 

For the same reason an Arduino is great for prototyping. If you wanted to play with that HYT-221 and concern yourself more with the high level maths used to decode the readings rather than the nitty-gritty of getting the bytes I bet you could have something up and running on an Arduino in 10 minutes (basically just copy that supplied sketch).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff I agree with your post for me though I need to learn the nitty gritty of the programming all this nonesense is what I need to make sense of and looking back I have learned so much since joining here, going to an Arduino feels like a massive backwards step

 

read 4 bytes from it and you are done

Yes, well after the measurement request is done IO read four bytes 

 

So I am thinking

 

#include <util/twi.h>
#define F_CPU          8000000
#define TWI_FREQ       100000
#define DEVICE_ADDRESS 0x28
#define I2C_READ 1
#define I2C_WRITE 0
 
void i2c_init(void);
void i2c_stop(void);
unsigned char i2c_start(unsigned char addr);
unsigned char i2c_rep_start(unsigned char addr);
void i2c_start_wait(unsigned char addr);
unsigned char i2c_write(unsigned char data);
unsigned char i2c_readAck(void);
unsigned char i2c_readNak(void);
unsigned char i2c_read(unsigned char ack);

uint8_t result_array[8]={0};

int main(void)
{
	
	i2c_init(); // initialize I2C library
	i2c_start_wait(DEVICE_ADDRESS+I2C_WRITE); // set device address and write mode
	i2c_stop(); 

	
	result_array[0]=i2c_readAck(DEVICE_ADDRESS+I2C_READ); // set device address and read mode
	result_array[1]=i2c_readAck(DEVICE_ADDRESS+I2C_READ);
	result_array[2]=i2c_readNak(DEVICE_ADDRESS+I2C_READ);
	i2c_stop();

	for(;;);
}

/*************************************************************************
 Initialization of the I2C bus interface. Need to be called only once
*************************************************************************/
void i2c_init(void)
{
  /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
  
  TWSR = 0;                         /* no prescaler */
  TWBR = ((F_CPU/TWI_FREQ)-16)/2;  /* must be > 10 for stable operation */

}/* i2c_init */


/*************************************************************************	
  Issues a start condition and sends address and transfer direction.
  return 0 = device accessible, 1= failed to access device
*************************************************************************/
unsigned char i2c_start(unsigned char address)
{
    uint8_t   twst;

	// send START condition
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

	// wait until transmission completed
	while(!(TWCR & (1<<TWINT)));

	// check value of TWI Status Register. Mask prescaler bits.
	twst = TW_STATUS & 0xF8;
	if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;

	// send device address
	TWDR = address;
	TWCR = (1<<TWINT) | (1<<TWEN);

	// wail until transmission completed and ACK/NACK has been received
	while(!(TWCR & (1<<TWINT)));

	// check value of TWI Status Register. Mask prescaler bits.
	twst = TW_STATUS & 0xF8;
	if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;

	return 0;

}/* i2c_start */


/*************************************************************************
 Issues a start condition and sends address and transfer direction.
 If device is busy, use ack polling to wait until device is ready
 
 Input:   address and transfer direction of I2C device
*************************************************************************/
void i2c_start_wait(unsigned char address)
{
    uint8_t   twst;


    while ( 1 )
    {
	    // send START condition
	    TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
    
    	// wait until transmission completed
    	while(!(TWCR & (1<<TWINT)));
    
    	// check value of TWI Status Register. Mask prescaler bits.
    	twst = TW_STATUS & 0xF8;
    	if ( (twst != TW_START) && (twst != TW_REP_START)) continue;
    
    	// send device address
    	TWDR = address;
    	TWCR = (1<<TWINT) | (1<<TWEN);
    
    	// wail until transmission completed
    	while(!(TWCR & (1<<TWINT)));
    
    	// check value of TWI Status Register. Mask prescaler bits.
    	twst = TW_STATUS & 0xF8;
    	if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) ) 
    	{    	    
    	    /* device busy, send stop condition to terminate write operation */
	        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
	        
	        // wait until stop condition is executed and bus released
	        while(TWCR & (1<<TWSTO));
	        
    	    continue;
    	}
    	//if( twst != TW_MT_SLA_ACK) return 1;
    	break;
     }

}/* i2c_start_wait */


/*************************************************************************
 Issues a repeated start condition and sends address and transfer direction 

 Input:   address and transfer direction of I2C device
 
 Return:  0 device accessible
          1 failed to access device
*************************************************************************/
unsigned char i2c_rep_start(unsigned char address)
{
    return i2c_start(address);

}/* i2c_rep_start */


/*************************************************************************
 Terminates the data transfer and releases the I2C bus
*************************************************************************/
void i2c_stop(void)
{
    /* send stop condition */
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
	
	// wait until stop condition is executed and bus released
	while(TWCR & (1<<TWSTO));

}/* i2c_stop */


/*************************************************************************
  Send one byte to I2C device
  
  Input:    byte to be transfered
  Return:   0 write successful 
            1 write failed
*************************************************************************/
unsigned char i2c_write( unsigned char data )
{	
    uint8_t   twst;
    
	// send data to the previously addressed device
	TWDR = data;
	TWCR = (1<<TWINT) | (1<<TWEN);

	// wait until transmission completed
	while(!(TWCR & (1<<TWINT)));

	// check value of TWI Status Register. Mask prescaler bits
	twst = TW_STATUS & 0xF8;
	if( twst != TW_MT_DATA_ACK) return 1;
	return 0;

}/* i2c_write */


/*************************************************************************
 Read one byte from the I2C device, request more data from device 
 
 Return:  byte read from I2C device
*************************************************************************/
unsigned char i2c_readAck(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
	while(!(TWCR & (1<<TWINT)));    

    return TWDR;

}/* i2c_readAck */


/*************************************************************************
 Read one byte from the I2C device, read is followed by a stop condition 
 
 Return:  byte read from I2C device
*************************************************************************/
unsigned char i2c_readNak(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN);
	while(!(TWCR & (1<<TWINT)));
	
    return TWDR;

}/* i2c_readNak */

 

Look here

 

http://www.google.co.uk/url?sa=t...

 

 

the measurement request is

 

Start mode

address+write

Wait for ack

Stop

 

So thats how I got

 

i2c_init(); // initialize I2C library
	i2c_start_wait(DEVICE_ADDRESS+I2C_WRITE); // set device address and write mode
	i2c_stop(); 

The datafetch (for three bytes)

 

Start mode

address+read

wait for ack

read and wait for ack

read and wait for ack

read and wait for NACK

 

So I (stupidly)tried

    result_array[0]=i2c_readAck(DEVICE_ADDRESS+I2C_READ); // set device address and read mode
	result_array[1]=i2c_readAck(DEVICE_ADDRESS+I2C_READ);
	result_array[2]=i2c_readNak(DEVICE_ADDRESS+I2C_READ);
	i2c_stop();

But i2c_readAck  is a void function, it doesn't accept any arguments so I am obviously way off here

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why do you think you would need to issue the device address for each read()? You start(address) then you read();read();read();... and finally you stop().

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff one day I will buy you a pint, you are a true gentleman and the patience of a saint

 

 

Does this look right?

    i2c_init(); // initialize I2C library
    i2c_start_wait(DEVICE_ADDRESS+I2C_WRITE); // set device address and write mode
    i2c_stop();

    
    i2c_start_wait(DEVICE_ADDRESS+I2C_READ); // set device address and read mode
    result_array[0]=i2c_readAck();
    result_array[1]=i2c_readAck();
    result_array[2]=i2c_readAck();
    result_array[3]=i2c_readNak();
    i2c_stop();

Once I get it going, I will go through it and really understand it, I just need it to go so I can investigate!

 

I have to leave work now, I will probably do some tonight but it will probably be tomorrow

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry but what's the point of the first start_wait() then stop() there? You don't plan to write to this thing do you? (indeed I'm not sure it even has a write address)

 

Beyond that what you then have is pretty much identical to what the Arduino sketch was doing isn't it? (apart from names changed to protect the innocent)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff I am stuck in traffic at the angel of the North!

The first write and wait for ack is the measurement request that is in the app note

I think this is correct but I'm not 100 percent, looking at the app note what do you think?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff I am stuck in traffic at the angel of the North!

The first write and wait for ack is the measurement request that is in the app note

I think this is correct but I'm not 100 percent, looking at the app note what do you think?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can't help but keep coming back to that Arduino sketch. I don't see them doing any such thing there.

 

But on the whole I'd generally ignore anything I say. I don't do I2C. I avoid it like the plague - always buy the SPI device when there is an I2C vs SPI choice available so I'd put much more store in any advice David can give rather than me ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the 7bit address is definitely 0x28, I thought to read I add 1 and to write I add 0

You really ought to read Wikipedia.

 

If you are determined to use the 7-bit address with Fleury,  you would say:

#define DEVICE_ADDRESS 0x28                    // use the wierd 7-bit address
ret = i2c_start((DEVICE_ADDRESS<<1)+I2C_READ); // set device address and read mode

And stragely enough ((0x28<<1) + 1) is 0x51

 

And it is very unwise (tm) ever using i2c_start_wait().     If you use the wrong slave address,   the whole program hangs.  

If you use i2c_start(),   you get a diagnostic return.

 

DAvid.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

David thanks

 

It makes sense how we get 0x50/0x51 being a noob I wasn't thinking about the 7 bits being the MSB thats asorted now

 

And it is very unwise (tm) ever using i2c_start_wait().

So I just use start and I can see if theres an error if the function returns a 1, very useful the start wait is basically no use

 

I need to try and make sense of all these while(TWCR) stuff, it looks like these could be a source of hanging

 

I will try and make some progress

 

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In practice,   you can just test i2c_start(SLAVE_X)

If it returns OK,   it is likely that every subsequent i2c_xxxx() function will be OK too.

If it returns NOT_OK,   it means that either SLAVE_X is not present or the bus is not free.

 

Just treat the library as a Black Box.      Obey the documentation and it will do exactly what it says.

 

Yes,   it is worth making several attempts at i2c_start().     For example,   an AT24Cxxx eeprom can take 3ms to write a page.   During this 'busy' period,   i2c_start() will return NOT_OK.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

David thanks

 

I had a good go at it today for a couple of hours, back to basics and I am putting more effort in because its got hold of me

 

I have a load of electronics stuff to do, an absolute bewilderring amount and it is very important so work will have to wait until next week, I will be back to this

 

Thanks and have a good weekend

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got this working yesterday, I still need to master it properley but sure enough it worked

 

However there is a lot for me to learn with the error reporting stuff

 

Every TWI app needs to use init

 

void i2c_init(void)
{
  /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
  
  TWSR = 0;                         /* no prescaler */
  TWBR = ((F_CPU/TWI_FREQ)-16)/2;  /* must be > 10 for stable operation */

}

It simply sets the prescaler and TWBR register which sets the frequency, no error reporting, just call it once and its done

 

After the bus is initialised then we send the start command

 

unsigned char i2c_start(unsigned char address)
{
    uint8_t   twst;

	// send START condition
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

	// wait until transmission completed
	while(!(TWCR & (1<<TWINT)));

	// check value of TWI Status Register. Mask prescaler bits.
	twst = TW_STATUS & 0xF8;
	if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;

	// send device address
	TWDR = address;
	TWCR = (1<<TWINT) | (1<<TWEN);

	// wail until transmission completed and ACK/NACK has been received
	while(!(TWCR & (1<<TWINT)));

	// check value of TWI Status Register. Mask prescaler bits.
	twst = TW_STATUS & 0xF8;
	if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;

	return 0;

}

This function returns 0 if there are no errors and 1 if an error occured

 

so I can use

 

err=i2c_start;

 

if(err!=0)

{

i2c_init;

err=i2c_start;

}

However I really dont know what the implications of

 

while(!(TWCR & (1<<TWINT)));

I mean what happens if the sensor goes faulty and its in the middle of that?, doesn't it make the code hang in some cases? I need to learn the exact ins and outs of this

 

Next up is

 

unsigned char i2c_readAck(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
	while(!(TWCR & (1<<TWINT)));    

    return TWDR;

}

Its read and acknowledge, fleury tells me it reads a byte and requests more?

I don't see where it requests more data but I am sure it does!

 

Next is stop, looks easy enough

 

void i2c_stop(void)
{
    /* send stop condition */
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
	
	// wait until stop condition is executed and bus released
	while(TWCR & (1<<TWSTO));

}

Sends a stop condition and waits for it to happen, again what if my sensor breaks or whatever?, will the program just hang in some cases?

 

The final function I use is

 

unsigned char i2c_readNak(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN);
	while(!(TWCR & (1<<TWINT)));
	
    return TWDR;

}

Which is the same as before but with no Ack, and I can see the difference here is the TWEA

 

So after all that the only function that I use which reports an error is the start

 

So how do I use this error reporting

 

What do I do if I get an error?

 

err=i2c_start;

 

if(err!=0)

{

i2c_init;

err=i2c_start;

if(err!=0)

{

printf("Its broken!")

}

}

I am interested to see what error handling the freaks have, not just David but anyone

 

If you use TWI please show the world how you handle errors

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I mean what happens if the sensor goes faulty and its in the middle of that?

But that isn't the error you are likely to encounter. It's not things failing half way through a conversation (on the whole if that happens your whole AVR design is FUBAR anyway) it's simply when you star trying to talk to an I2C device that isn't there. Because it is a floating bus it lends itself to having "plug in modules". Think of a chain of "intelligent" christmas tree lights for example. Someone may have only bough 20 or they may have added the extension and have 40 lights on the bus. When the controller tries to talk to light number 21 in the case where there are only 20 it will be met with silence.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff I don't understand

 

I know the chances of it failing in the middle are extremely slim it was just me thinking about it

 

But its entirely possible the sensor goes dodgy, the AVR end is just fine but it could just hang, now this applies to many things not just TWI so as the best programmer in the world have you ever defended against this stuff? or am I talking crap again?

 

I wouldn't know how to even start

 

I wrote some code to try and reinitialise the TWI if things failed, I  tried unplugging my sensor yesterday and asometimes it came back on others it hung which is why I asked the question, I think I need to power it down sometimes but I dunno, more playing is required

 

Edit

 

Cliff

 

if it fails half way through surely we need to protect against FUBAR?

 

So we diagnose the faulty sensor and we then assume worst case conditions or whatever is required to keep the syatem running safely

 

 

Last Edited: Mon. Mar 16, 2015 - 09:24 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As Cliff says,    a missing sensor is detected easily enough.    i.e. there is no reply.

 

An existing sensor that has died could have two modes.    Either silence / open-circuit or a short-ciruit low.

 

If your sensor is holding SDA line or the SCL line permanently low,   you have lost the whole bus.

 

From a software point of view,   you can timeout after a certain period.    The SMBus allows for a timeout but the I2C bus has no maximum time.    In practice,    it is worth using some sort of safety recovery.

 

Bear in mind that I2C means inter-integrated-circuit.    i.e. chips on one pcb.   all i.c.s are powered from the single pcb.

It is not designed for remote boards.    Other buses are more suitable.

It is certainly not designed for connecting un-powered boards.     Unpowered electronics generally kills buses.

 

David.

 

Edit.   You certainly should never call i2c_start() before i2c_init()

Last Edited: Mon. Mar 16, 2015 - 09:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If your sensor is holding SDA line or the SCL line permanently low,   you have lost the whole bus.

I completely understand

 

The SMBus

Never heard of that one, it must of slipped me by

 

In practice,    it is worth using some sort of safety recovery.

 So my thinking is right, yes this is good!

 

Its how to implemet it that I am confused about

 

Looking at it its simply

 

while(!(TWCR & (1<<TWINT)));

its waiting for the TWINT  in the TWCr register to be 0

 

I can think of a way to use if()

 

But I am really interested in hearing how Cliff or David would tackle this seemingly simple C problem

 

Bear in mind that I2C means inter-integrated-circuit.    i.e. chips on one pcb.   all i.c.s are powered from the single pcb.

It is not designed for remote boards.    Other buses are more suitable.

It is certainly not designed for connecting un-powered boards.     Unpowered electronics generally kills buses.

I understand, electronics stuff I always find easier, I studied hardware afterall so I am a hardware man so all the PCB design, filtering, conditioning and environmental considerations come easier to me, reading documentation is a breeze and I really enjoy it

 

I am aware of the maximum cable length limitations, it all seems to be mostly down to parasitics creating lowpass filters which I 100% understand, I won't try and be a maverick and I will use them as intended however this sensor uses uA, so I can power it from a pin and should there be trouble perhaps cycling the power will revive the sensor, I have witnessed what seemed to be a locked sensor but my code wasn't written to handle it, surely having a power cycle feature makes for a much better system, even when the chip is on the same PCB, obviously when its powered down the bus will be dead but it will be reinitialised and the whole thing redone

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

if it fails half way through surely we need to protect against FUBAR?

Do you own a torch? If the bulb goes what use is it as a torch?

 

Electronic circuits are like this. If one of the key components in the design fails then the whole device is FU'd.

 

Would you propose that the torch had a "back up" so that if it could not generate light perhaps it would play tunes or something to keep you entertained instead?

 

I fail to see why code should go out of it's way to try and compensate for a catastrophic component failure unless you are talking about something health/safety critical like a heart monitor or an autopilot or an ABS system in a car but I think you'll find that in such devices they maybe have 2 or 3 of each I2C sensor (or whatever) and if one fails they revert to a back up. But torches aren't built like this ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff, I love you man, you always make me smile

 

Its the way you think, always thinking logically like a proper programmer should

 

However lots of electronics sytems are complicated and a sensor isn't the be all and end all now I don't work on safety critical systems so thats not the thing here but I do think a safety critical attitude is always good

 

A sensor might not be the be all and end all to a system, especially when applied to mechanical/chemical processes it might be there to monitor something so that a decision can be made if the sensor breaks then it can just switch to default in other cases it might revert to a mathematical model which uses other parameters to estimate aka as an observer

 

Seriously the sensor going down and taking the rest with it might seem like safety critical but its an absolute requirement for me, I have to allow for it to break so that default action can be taken

 

I can think of many new sensors that never used to exist, which I could take, develop a system and apply to an old product where it improves that product massively, if the sensor breaks I just go back to a product that never had the sensor

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've got an alarm clock like that. It's an alarm clock but it also tells me the temperature in my bedroom. If the temperature sensor failed I'd probably still use it as an alarm clock.

 

I imagine that if the temp sensor failed it would probably show something like --.-°C

 

I further guess that if the temp sensor failed at the very instant it was talking to it that there's a possibility it might get stuck in an infinite loop.

 

As a user if I had an unresponsive alarm clock I guess the first thing I'd try is to turn it off and on again. On the second startup I imagine it would get to the place where it talks to the temperature sensor and determine "curious, he doesn't appear to be there - perhaps I'll just show --.-°C instead?". At this point all is well in the world (except the temp function in my alarm clock is FUBAR!)

 

If you *really* believe your temp sensor is going to fail at the very moment of the:

while(!(TWCR & (1<<TWINT)));

then consider something like:

int timeout = 5000;
while(!(TWCR & (1<<TWINT)) && --timeout > 0);

Or words along those line (bet I got the logic wrong!). Anyway the point being that if you have an "infinite wait loop" in code and you think that for some reason you may get stuck inside it then operate a timeout alongside and if the timeout reaches 0 then abandon the infinite loop and report an error.

 

Another approach to the same thing is "turn on the watchdog". It will "kick" the AVR out of an inifinite loop after N milliseconds and either interrupt or pull the AVR into reset. When the AVR resets in this way the chances are that the first attempt to talk to the temp sensor will fail even before it gets near an infinite loop and it will already have made the "show --.-°C " decision.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Excellent post Cliff, thank you

 

I don't think the fault will ever happen in my life but I have big plans mate, if its one in a million chance and I can write a line extra that defends then for me there is no excuse for not doing it

 

I was such a n00b I wasn't thinking about the watchdog which will always be active blush

 

Anyway your alarm clock analogy is good but Cliff I have AVR's controlling some pretty expensive equipment and for the most part theres all the right protection in place, fuses, stats PRV's etc the thing is if something goes wrong its not the expense but its ball ache to get it stripped down and set back up, an alarm clock doesn't compare

 

i am just trying to be careful and taking extra measures for reliability, hence me looking into this more

 

thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well ultimately do what aircraft do - make three AVR circuits and have them all monitor/control on some kind of "voting principle". As long as at least two agree do what they say and ignore the third that has had a sensor fail or whatever. In aircraft the three computer are programmed by 3 teams so if there's a bug in one it's unlikely to have been replicated into the others and they win the vote.

 

(and yes there is an issue if your vote coordinator hits a problem!).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

laugh

 

Cliff, that would be three times the work! well probably more like four

 

I can't do the things I have on now!, theres only me so its just as well I don't do safety critical stuff

 

seriously I would refuse to work on anything safety critical, I am a n00b it would be irresponsible and being a bleeding heart liberal it would really affect me to think someone had got hurt but if only I had......

 

theres no fun in that but I think if its relatively cheap but it improves reliability/safety then as long as the effort isn't too much then theres no excuse

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Having done a bit of i2c over the years, things do fail, so timeouts are a good idea. At least you can detect the failure and make an attempt to recover or fail gracefully by logging the error and/or indicating the failure.
Last year i did a project with two micros linked by i2c. That was a challenge! The existing code was unreliable and would frequently lock up - not a good thing to happen! Try to find out who's holding the bus! The salae logic was invaluable. I implemented timeouts and retries. I would reset the peripheral block to free the bus (chip was a kinetis cm4).
Now i can short the bus and things recover cleanly.

Last Edited: Mon. Mar 16, 2015 - 10:07 PM