I2C Library Problems with ATtiny817

Go To Last Post
64 posts / 0 new

Pages

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

Regular international mail is fine. I was surprised to hear you say DHL when El Tangas's photos show regular mail.
.
It will be interesting to see how you get on with the UPDI and mEDBG chip.
And of course the new features of the Tiny817.
.
David.

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

I was surprised to hear you say DHL

I'm getting something else trough DHL so I ASS_U_MEd that the board was in the same package.

 

So the UPDI clock defaults to 100KHz and the main clock at 20MHz. I'm not messing around with those until I understand a lot more.

 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Hello, 

 

I try to implement I2C routine on Attiny 817 Xplained mini for reading T° on LM75A device and I would like to share it once it will be fully functional!

First I looked at I2C blocks available on atmel start, but they are so complex and twisted!

So here is my code.

The problem is that the read with Ack or Nack doesn't seem to work. I receive twice the same value (never the second byte from LM75A).

Perhaps it misses a test to check that the previous operation is done?

Any idea and suggestion is welcome!

 

 

#define F_CPU 20000000UL

#include <util/delay.h>

#include <stdlib.h>

 

/* -------------------------------------------------------*/

 

#define TWI0_BAUD(F_SCL)      ((((float)F_CPU / (float)F_SCL)) - 10 )

#define LM75A_ADDRESS_W 0b10010000

#define LM75A_ADDRESS_R 0b10010001

#define LM75A_TEMP_REGISTER 0x00

 

void TWI_init()

{

TWI0.MBAUD = (uint8_t)TWI0_BAUD(100000);                          // set MBAUD register for 100kHz

TWI0.MSTATUS |= (0x1);                                                         //Force TWI state machine into IDLE state

TWI0.MCTRLA = 1 << TWI_ENABLE_bp                                    /* Enable TWI Master: enabled */

| 1 << TWI_QCEN_bp                                                             /* Quick Command Enable: enabled */

| 0 << TWI_RIEN_bp                                                              /* Read Interrupt Enable: disabled */

| 0 << TWI_SMEN_bp                                                             /* Smart Mode Enable: disabled */

| TWI_TIMEOUT_DISABLED_gc                                                /* Bus Timeout Disabled */

| 0 << TWI_WIEN_bp;                                                            /* Write Interrupt Enable: disabled */

}

 

void TWI_start(uint8_t slaveAddr)

{

TWI0.MADDR = slaveAddr;                                                    //Create TWI start condition by loading slave address into MADDR register

}

 

uint8_t TWI_read(uint8_t ACK)                                             // ACK=1 send ACK ; ACK=0 send NACK 

{

if (ACK) TWI0.MCTRLB &= ~(1<<TWI_ACKACT_bp);              //  si ACK=1 mise à 0 ACKACT => send ACK

else  TWI0.MCTRLB |= (1<<TWI_ACKACT_bp);                     //  sinon (ACK=0) => mise à 1 ACKACT => send NACK 

return TWI0.MDATA ;                                                          // lecture du registre MDATA et envoi de l'action définie par ACKACT

}

 

void TWI_WRITE(uint8_t write_data)

{

TWI0.MDATA = write_data;                                                 //Transfer write_data into MDATA register

}

 

void TWI_STOP(void)

{

TWI0.MCTRLB |= (1<<TWI_ACKACT_bp);                           // Set acknowledge action to NACK

TWI0.MCTRLB |= (TWI_MCMD_gm);                                   //Triggers Master to execute acknowledge action (NACK), succeeded by issuing STOP condition

}

 

TWI_init();

uint8_t tempHigh;

uint8_t tempLow;

 

while (1)

{

_delay_ms(200);

PORTC_OUTTGL = (1<<PIN0_bp);

TWI_start(LM75A_ADDRESS_R);

tempHigh = TWI_read(1);                    // read avec ack

tempLow = TWI_read(0);                    // read avec nack

TWI_STOP();

 

utoa (tempHigh, buffer, 10);

printString(buffer);

printString("/");

utoa (tempLow, buffer, 10);

printString(buffer);

printString(" ");

}

Attachment(s): 

 main.c

 terminal out.jpg

 IMG_0543.JPG

Attachment(s): 

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

The nice thing about I2C protocol is, it will tell you when something goes wrong, but you wrote all of your I2C functions with void return values!!!  So it throws away any chance to do that!

Re-write your I2C functions to return the twi status register value and test it for correctness (expected value) and you will do much better.

Take some time to study what others have successfully done, ask questions if you need help understanding why they did it the way they did.

 

Jim

 

Edit: Please use the "<>" button to post code to preserve correct spacing!

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

Last Edited: Thu. Oct 12, 2017 - 07:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Jim,

 

here is the list of functions :

 

void TWI_init();
void TWI_start(uint8_t slaveAddr);
uint8_t TWI_read(uint8_t ACK);
void TWI_WRITE(uint8_t write_data);
void TWI_STOP(void);

 

the read function returns an uint8_t value read from MDATA

I don't need values back from other function (init, start; write, stop).

 

So I don't think it's the problem.

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

Really, how do you know you got an ACK to your start(address)?  A very common failure seen here by Freaks!

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

Hello,

 

I worked again recently on I2C/TWI simple routines on my Attiny817Xplained mini board connected to an HDC1080 module.

The code is working nice, so I just post it to share with Avrfreak community.

 

I get inspired from JRAB post :

https://www.avrfreaks.net/comment/2390341#comment-2390341 

but I added the Read part, which is somehow capital for Twi/i2c complete working.

 

#define TWI0_BAUD(F_SCL)      ((((float)F_CPU / (float)F_SCL)) - 10 )
#define HDC1080_ADDRESS_W		0b10000000
#define HDC1080_ADDRESS_R		0b10000001
#define HDC1080_TEMP_REGISTER	0x00
#define HDC1080_CONFIG_REGISTER	0x02

void TWI_init()
{
	TWI0.MBAUD = (uint8_t)TWI0_BAUD(100000);	        // set MBAUD register for 100kHz
	TWI0.MCTRLA = 1 << TWI_ENABLE_bp			/* Enable TWI Master: enabled */
	| 0 << TWI_QCEN_bp					/* Quick Command Enable: disabled */
	| 0 << TWI_RIEN_bp					/* Read Interrupt Enable: disabled */
	| 1 << TWI_SMEN_bp					/* Smart Mode Enable: enabled */
	| TWI_TIMEOUT_DISABLED_gc				/* Bus Timeout Disabled */
	| 0 << TWI_WIEN_bp;					/* Write Interrupt Enable: disabled */

	TWI0.MCTRLB |= TWI_FLUSH_bm ;				/* Purge MADDR and MDATA */
	TWI0.MSTATUS |= TWI_BUSSTATE_IDLE_gc ;		        //Force TWI state machine into IDLE state
	TWI0.MSTATUS |= (TWI_RIF_bm | TWI_WIF_bm) ;
}

uint8_t TWI_start(uint8_t deviceAddr)
{
	if ((TWI0.MSTATUS & TWI_BUSSTATE_gm) != TWI_BUSSTATE_BUSY_gc)		//Verify Bus is not busy
	{
		TWI0.MCTRLB &= ~(1 << TWI_ACKACT_bp);
		TWI0.MADDR = deviceAddr ;
		if (deviceAddr&0x01)	{while(!(TWI0.MSTATUS & TWI_RIF_bm));}  //si addressRead
		else			{while(!(TWI0.MSTATUS & TWI_WIF_bm));}  //si addressWrite
		return 0;
	}
	else return 1;	                                                        //Bus is busy
}

uint8_t TWI_read(uint8_t ACK)							// ACK=1 send ACK ; ACK=0 send NACK
{

	if ((TWI0.MSTATUS & TWI_BUSSTATE_gm) == TWI_BUSSTATE_OWNER_gc)		//Verify Master owns the bus
	{
		while(!(TWI0.MSTATUS & TWI_RIF_bm));				// Wait until RIF set
		uint8_t data=TWI0.MDATA;
		if	(ACK==1)	{TWI0.MCTRLB &= ~(1<<TWI_ACKACT_bp);}		// si ACK=1 mise à 0 ACKACT => action send ack
		else			{TWI0.MCTRLB |= (1<<TWI_ACKACT_bp);	}	// sinon (ACK=0) => mise à 1 ACKACT => nack préparé pour actionstop

		return data ;
	}
	else
	return 1;	//Master does not own the bus

}
uint8_t TWI_WRITE(uint8_t write_data)
{
	if ((TWI0.MSTATUS&TWI_BUSSTATE_gm) == TWI_BUSSTATE_OWNER_gc)			                //Verify Master owns the bus
	{
		TWI0.MDATA = write_data;
		while (!((TWI0.MSTATUS & TWI_WIF_bm) | (TWI0.MSTATUS & TWI_RXACK_bm))) ;		//Wait until WIF set and RXACK cleared
		return 0;
	}
	else
	return 1;	//Master does not own the bus
}

void TWI_STOP(void)
{
	TWI0.MCTRLB |= TWI_ACKACT_NACK_gc;
	TWI0.MCTRLB |= TWI_MCMD_STOP_gc;
}

 

now the use of these routines :

 

        TWI_init();
	TWI_start(HDC1080_ADDRESS_W);
	TWI_WRITE(HDC1080_CONFIG_REGISTER);
	TWI_WRITE(0x10);			// 0b0011 0000		0 : RST clear, 0 : reserved, 1 : heater enabled, 1 : Temp+Humid acquisition mode, 0 : battery status,
	                                        //       		0 : Temp 14 bits, 00 : humid 14 bits,
	TWI_WRITE(0x00);			// 0b0000 0000		[7:0] reserved, must be 0
	TWI_STOP();

	TWI_start(HDC1080_ADDRESS_W);
	TWI_WRITE(HDC1080_TEMP_REGISTER);	// séquence pour démarrer une conversion
	_delay_ms(20);				// temps de conversion en 14 bits = 6,5ms (T°) + 6,5ms (HR)

	while (1)
	{
	TWI_start(HDC1080_ADDRESS_R);
	temp_H = TWI_read(1);	// read avec ack
	temp_L = TWI_read(1);	// read avec ack
	humid_H = TWI_read(1);  // read avec ack
	humid_L = TWI_read(0);  // read avec nack
	TWI_STOP();

	/*
        code for treatment data and conversion
	code for results display
	*/

	}	

I hope it will be useful, because up-to-now I didn't find any simple code for I2C/TWI for the wonderful new Attiny AVR-1 cores (the code proposed in Atmel.start is very complex).

 

Let me know your comments!

Fabrice.

Last Edited: Mon. Oct 15, 2018 - 03:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Fabrice,

 

I have tested your TWI code on an ATTiny402 talking to an I2C ADC. It works okay, but at the end of the transaction it doesn't send the stop sequence. I've scoped the signals to confirm this and it also appears that the bus is latching up and not returning to "idle". Can you please confirm that your code above works for more than one transaction. In other words, have you put it in a loop to get data from the HDC1080 more that once?

 

Regards,

Trevor.

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

When the master is done receiving data from the slave, it must NAK the last data byte, this informs the slave to release the bus so the master can send the stop!

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

Fabrice, Can you please confirm that the code above works for more than one transaction?

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

meolsen wrote:

It's only 1 entry, top line is the friendly name that people wants, the bottom line is the internal/symbolic name that other people wants. Makes more sense on other fuses, I grant you...

 

Or its a bug.

Last Edited: Wed. Feb 6, 2019 - 08:09 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

sorry for me late reply. I have to check how to receive an email when the thread is updated!

I use my code (posted #58) successfully on an Attiny817 Xplained Mini, with an Oled I2C screen and a HDC1080 breakout sensor on the same bus.

The sequence is working fine, even if I never checked the bus with an oscilloscope.

 

 here are the routines for HDC1080 : 

define HDC1080_ADDRESS_W        0b10000000
#define HDC1080_ADDRESS_R        0b10000001
#define HDC1080_TEMP_REGISTER    0x00
#define HDC1080_CONFIG_REGISTER    0x02

static float temperature ;
static float humidity ;

void hdc1080_init() {
    TWI_start(HDC1080_ADDRESS_W);
    TWI_write(HDC1080_CONFIG_REGISTER);
    TWI_write(0x10);                    // 0b0011 0000        0 : RST clear, 0 : reserved, 1 : heater enabled, 1 : Temp+Humid acquisition mode, 0 : battery status,
                                        //                    0 : Temp 14 bits, 00 : humid 14 bits,
    TWI_write(0x00);                    // 0b0000 0000        [7:0] reserved, must be 0
    TWI_STOP();
}

void hdc1080_read(){
    TWI_start(HDC1080_ADDRESS_W);
    TWI_write(HDC1080_TEMP_REGISTER);    // séquence pour démarrer une conversion
    _delay_ms(20);

    TWI_start(HDC1080_ADDRESS_R);
    uint8_t temp_H = TWI_read(1);                    //  read avec ACK
    uint8_t temp_L = TWI_read(1);                    //  read avec ACK
    uint8_t humid_H = TWI_read(1);                    //  read avec ACK
    uint8_t humid_L = TWI_read(0);                    //  read avec NACK
    TWI_STOP();

    uint16_t temp_14b = 0 ;
    temp_14b = (uint16_t)(temp_H<<8 | temp_L);
    temperature = (   ( (float) (temp_14b) * 165 ) ) / 65536 - 40 ;

    uint16_t humid_14b = 0 ;
    humid_14b = (uint16_t)(humid_H<<8 | humid_L);
    humidity =  (  (float) (humid_14b) * 100.0 ) / 65536 ;
    }

The key point for several reads, is to use the last read  with with Nack : TWI_read(0) .

Then you can issue a Stop.

 

I hope this will work for you too.

Best regards

Fabrice.

 

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

I know this is a bit to the party, but I was struggling with STMICRO LIS2DH12 accelerometer (and attiny817 xplained pro)and stumbled on here. I attempted using the above mentioned code, but the old Fleury library made it happen for me. I changed the following from the i2cmaster.S:

;***** Adapt these SCA and SCL port and pin definition to your target !!
;
; .kbv  use VPORTs on Tiny817 or Xmega.
; .kbv  I use delay loop instead on NOPs if F_CPU > 4MHz
#define SDA     1		// SDA PB1 (alternate func PA1 on tiny817)
#define SCL	2		// SCL PB0 (alternate func PA2)
#define SDA_PORT        VPORTA_OUT          //
#define SCL_PORT        VPORTA_OUT          //

;******

In the example main:

#include <avr/io.h>
#define F_CPU 5000000UL
#include <util/delay.h>
#include "i2cmaster.h"

#define Slave			 0x33
#define LIS2DH12_W		 0x32
#define LIS2DH12_R		 0x33
#define WHOAMI_REG		 0x0f
#define TEMP_CFG_REG     0x1f
#define TEMP_SEN_EN		 0xc0
#define CTRL_REG1		 0x20
#define ACCEL_EN_LP		 0x47
#define CTRL_REG4		 0x23
#define BDU_HR_EN		 0x88
#define X_ACCEL_REG		 0xa8	//OR'd with 0x80 for multiple return data bytes /* SEE PAGE 25 FOR EXPLANATION*/
#define Y_ACCEL_REG		 0xaa	//OR'd with 0x80 for multiple return data bytes
#define Z_ACCEL_REG		 0xac	//OR'd with 0x80 for multiple return data bytes 

void initAccel(void){
	uint16_t ret;
	/* SPECIFIC ORDER FOR READING VALUE OF REGISTER (VERIFY DEVICE ID)*/
	i2c_start(LIS2DH12_W);  //.kbv
	i2c_write(WHOAMI_REG);
	i2c_rep_start(LIS2DH12_R);
	ret = i2c_readNak();
	i2c_stop();
	(ret == 0x33) ? PORTB.OUTSET : PORTB.OUTCLR;		//if ret!=33 there was a problem
	i2c_stop();
	/* CONFIG TEMPERATURE SETTINGS */
	i2c_start(LIS2DH12_W);
	i2c_write(TEMP_CFG_REG);
	i2c_write(TEMP_SEN_EN);
	i2c_stop();
	/* CONFIG LOW POWER AND ACCELEROMATER X-Y-Z */
	i2c_start(LIS2DH12_W);
	i2c_write(CTRL_REG1);
	i2c_write(ACCEL_EN_LP);
	i2c_stop();
	/* CONFIG BLOCK UPDATE/HIGH RESOLUTION MODE */
	i2c_start(LIS2DH12_W);
	i2c_write(CTRL_REG4);
	i2c_write(BDU_HR_EN);
	i2c_stop();
}

int main(void)
{
	/* SETTING UP LED */
	PORTB.DIRSET = PIN4_bm;
	PORTB.OUTCLR = PIN4_bm;
	/* BUTTON SET */
	PORTB.DIRCLR = PIN5_bm;			   // SET PIN5 AS INPUT
	PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // SET PULL UP

	/* INITIALIZE I2C */
	i2c_init();
	/* INITIALIZE ACCELEROMETER */
	initAccel();

    while (1)
    {
		if(PORTB.IN & PIN5_bm){
			/* APPEARS TO BE WORKING USING THIS SPECIFIC READ SEQUENCE */
				i2c_start(LIS2DH12_W);  //.kbv
				i2c_write(X_ACCEL_REG);
				i2c_rep_start(LIS2DH12_R);
				i2c_readAck();
				i2c_readNak();
				i2c_stop();
		}
		_delay_ms(500);
    }
}

I set up the button so I could use my logic analyzer to trigger on the packets. This should also work for the LIS3DH but this is the general read write functionality with this device. Sorry if this is obvious to most I'm still a newb and figured I'd share.

Cheers,
Jesse_G an EE writing firmware...what could go wrong...

Pages