Settings meaning for TWI device

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

Hi all...

 

I'm trying to learn how to program my device to use TWI interface to read from my board temperature sensor and there are some settings I can't decide which options to set on their registers, so I need some help clarifying these settings meaning and what they are used for!

 

I'm using this AVR IoT WG board which uses an AtMega4808 and the temperature sensor is an MCP9808. AtMega4808 User Manual I'm using is here and MCP9808 datasheet I'm using is here.

I'm using MPLAB X IDE v5.15 and AVR Toolchain v5.4.0. I'm on Debian 9.8 (Stretch) 64 bit, if this matters!

The goal is to setup TWI interface to read temperature from this sensor.

 

The first thing I did was start reading Mega4808 User Manual at the TWI section, starting at page 334. Then a bit further they talk about the interface initialisation where is said that properties SDASETUP and SDAHOLD must be set before initialising the device.

 

I didn't know what these 2 settings meant so I went to search about them. The problem is that even after I read about them I'm still not sure what to set for these 2 settings.

I have searched both documents and found timings tables of both devices and that these tables might hold the answers but still I'm not sure what to choose.

 

In MCP9808 I see:

 

 

and in Mega4808 User Manual I see:

 

 

Is it reasonable to assume that if I choose Mega4808 option 3, 0x2 (300ns nominal - 180-630ns) I'll be ok for this setting? Or is it better to choose option 4, 0x3 (500ns nominal - 300-1050ns) ???

 

And also for SDASETUP I have no clue what for is it used for! I also need clarification for that setting, please!

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

Well, regardless of this, I need to try to continue! I chose here the 4th option. I'll change it if needed.

 

Now, I keep reading and they say that I should set the Baud rate in TWIn_MBAUD register. So I assumed a random value of 100KHz which is inside the range of the Slave device (0Hz up to 400KHz in its Datasheet).

When I search for this register, I see this is an 8 bit register, so I can't write 100KHz there. Then I read there that "For more information on how to calculate the frequency, see the section on Clock Generation". When I search the User Manual for this section and start looking for TWI Baud Rate frequency calculation I can't find anything but USART related stuff and Synch Modes and Asynch modes, etc, etc! So, I'm clueless about what to do with this MBAUD register I need to set!

 

Any help here?

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

There is more then one datasheet (manual) for this part, a generic family DS, and a specific part DS (28, 32 or 48 pin part), You need both (all) of them.

While your at MC getting those, also get the app notes as well, you will need the TWI one specifically, but might as well get the rest.

 

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...

 

 

 

 

Last Edited: Mon. Mar 18, 2019 - 07:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

There is more then one datasheet (manual) for this part, a generic family DS, and a specific part DS (28, 32 or 48 pin part), You need both (all) of them.

While your at MC getting those, also get the app notes as well, you will need the TWI one specifically, but might as well get the rest.

 

Jim

 

 

I have the links in my 1st post to the documents I'm using! I'm using the datasheet and the user manual of AtMega4808 and MCP9808 temp sensor!

 

I found what they mean about "Clock Generator" section. It's in page 344 and the formula I need is there but now I need to know about tRise and I can't find clear info about this value that I need to evaluate the BAUD value I need to set TWI0.MBAUD register!

 

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

That MCP9808 table can be pretty much ignored unless you try to run your serial at the very fastest possible speed. YOU have no control over those parameters, and you have scant choice on the master.  Simply, set the master hold time long enough.

 

Compare the timing diagrams of the master and the slave to figure out what you really need.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Mar 18, 2019 - 10:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I have some questions I can't answer by myself regarding this TWI operation, specifically when it comes to this chip Mega4808.

 

I know there is a sequence of events during a transaction of a packet. But I'm finding hard to understand and confirm my reasoning is correct or not when I try to write my functions to deal with Start Bits, Stop Bits, Hold Times, etc.

 

So, before the questions about the code, I need to ask the following:

 

When the User Manual says Master Operation and Slave Operation, I'm not sure what this means. I know that usually, in my case, the Mega4808 is the Master Device and the temperature sensor is the slave device and in my mind, Master Device will need to read values from the temperature sensor. So I cannot decide if this is a Master Operation or a Slave operation because both devices are working together. Master is asking for data and Slave is providing that data!

The question is in what context this Master and Slave operation comes in? Why there are both operations and when one happens and when the other happens?

 

Then comes some more specific questions.

 

I know that there is a specific time that SDL and SDA lines needs to hold to be able to some specific situations to be detected and confirmed. And at the beginning there was this setting I set named SDAHOLD which was 500ns (option 0x03 below).

 

 

My questions here are 2:

 

1 - Is this SDAHOLD setting the one needed, for instance, for detecting a START Condition and/or STOP Condition? I mean, does the SDA Line needs to be at level 0 (after the transition from level 1 to level 0) for 500ns while SCL is also level 0 for a Start Condition to be confirmed? Or is this setting unrelated to this?

 

2 - If answer to the above question is "yes, it's related and 500ns are needed to a Start condition is detected and confirmed", how do I make it wait 500ns? Do I need to write my own delay function or can I use the _delay_xx() functions?

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

Not sure why your worrying about this, the TWI hardware will handle this, I would just take the default value and go on with the project.

Are you trying to trouble shoot some issue with the I2C comms or just trying to understand the details of how it works?

 

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

I think that a big part of the issue is that the new Mega0/1 devices have more control over TWI timing than we are used to with the "old" Mega/Tiny family. Note reference to SDASETUP & SDAHOLD in TWI.CTRLA in the quote in the initial post. Nothing like that in a standard Mega!

 

Frankly, I don't think any of us have direct experience with this. Yet. It is going to involve direct analysis of the timing diagram of the peripheral device(s), the timing diagram for the TWI port, and the timing table in the TWI documentation. I suspect that this will mostly effect operation at the maximum possible speeds. Someday, in the not too distant future, I'll have to go through this exercise, myself. Until then, I plead ignorance of the facts.

 

Jim

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

ki0bk wrote:

Not sure why your worrying about this, the TWI hardware will handle this, I would just take the default value and go on with the project.

Are you trying to trouble shoot some issue with the I2C comms or just trying to understand the details of how it works?

 

Jim

 

 

 

I think I understand the general concept of how TWI works but the details are killing me. As you can notice I'm not experienced and therefore I'm struggling to understand the settings and what they do!

 

For instance, I know that SDAHOLD is a some setting that defines the amount of time that one line needs to hold a certain level before it can change to detect and confirm some specific action. But my struggle is that I don't know what for is this setting needed and if this setting is to automatically avoid the user to be worrying about timings.

 

For instance, to send a Star Condition I have this registers from the header file:

TWI_MCMD_REPSTART_gc = (0x01<<0),  /* Issue Repeated Start Condition */

So, I assume that to send a start condition to start sending a packet I would do:

 

TWI0_MCTRLB = TWI_MCMD_REPSTART_gc;

Then I know that the hardware needs to hold the SDA line for some time while SCL is low so that this condition is detected and confirmed as being a Start (or Repeated Start Condition) but I don't know if I need to manually write a delay function for this or if that SDAHOLD setting takes care of it automatically!

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

ka7ehk wrote:

I think that a big part of the issue is that the new Mega0/1 devices have more control over TWI timing than we are used to with the "old" Mega/Tiny family. Note reference to SDASETUP & SDAHOLD in TWI.CTRLA in the quote in the initial post. Nothing like that in a standard Mega!

 

Frankly, I don't think any of us have direct experience with this. Yet. It is going to involve direct analysis of the timing diagram of the peripheral device(s), the timing diagram for the TWI port, and the timing table in the TWI documentation. I suspect that this will mostly effect operation at the maximum possible speeds. Someday, in the not too distant future, I'll have to go through this exercise, myself. Until then, I plead ignorance of the facts.

 

Jim

 

 

Indeed... This header file and the way microchip guys did this is way different from the usual stuff!

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

Ohhh ,man... I hate this...

 

I've been reading the same crap over and over again and now that I'm reading the description of this register MADDR I found myself thinking if I need anything I read before...

 

 

It says there that when this bit field (I would say byte field), a Start condition and an Slave address protocol is initiated.

So does this means I don't have to worry at all with Start Conditions? And then that I only need to check for the ACK response to continue with the code?

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

You might want to get a traditional mega (328 on Arduino would be a good, cheap, easy choice) and do TWI on that first. It is the "minimal subset" and shows you the core basics which are the only stuff you need really understand. It's a bit like doing ports and pins. Those traditional AVR only have 3 registers: DDR, PORT, PIN and that's all you really need for IO. Then look at the Xmega derivatives. The ports have 10 or more registers for things like OUTTGL and so on. This adds all kind of additional stuff but it's really all just eye candy. You can still run a port using only PORT_DIR, PORT_IN and PORT_OUT. Looks like I2C is the same. They've thrown in a ton of eye candy with SDAHOLD and so on but you don't have to understand/use that (except that the one that says "backward compatibility" probably sounds good!).
.
In short you are confusing yourself with stuff you don't need. Having said that, TWI/I2C is always going to need some kind if "addr" register. You'll find that in the simple 328 just as in this massively complex chip.

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

Clawson, I have already did this in AtMega328 in the past. I did it for learning about TWI by storing and reading simple values from an external memory chip.
Here the problem I think it is the fact that I don't know that much about TWI and Mega4808 that allow me to understand the User Manual instructions. And adding to that, seems to exists many new features that I don't know if I need them.

I'm ok with the eye candy way of microchip doing things and writing the header file because it's been easy ti find the register's names and by what is said in User Manual. That is the least of thr problems.

I gave the example of SDAHOLD that I read about what it is but I have no clue how to reason the option to choose in my case.

And yet about Mega328, because it is a more simple chip, datasheet ends up more easy ro understand. They ezplain things in a more linear way. I mean, chronologically speaking, is way easier to understand. They start in the beginning and go until the end without the need of explaining other things in the middle. Here, for Mega4808 they explain each feature but not like they organize explaining for TWI frim beginning to end. They explain featur 1, then they explain feature 2 and after they explain feature 3, but not that things needs to happen in the code by this order. That is a problem to me. I'll posr the code I have. Maybe you guys can help me from there.

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

This is what I have to initialise TWI:

 

void TWI_Init(void){
    TWI0_CTRLA |=  TWI_SDASETUP_4CYC_gc  // SDASETUP time of 4 clock cycles
                 | TWI_SDAHOLD_500NS_gc; // SDA Hold time - Meets SMBus specifications across all corners (0x03) ???
    TWI0_CTRLA &= ~TWI_FMPEN_bm;         // Fast Mode Plus disabled

    // Set MBAUD to get 100KHz for SCL from fSCL = CLK_PER / (10 + 2*BAUD + CLK_PER*tRise)
    // fSCL = 100KHz, CLK_PER = 20MHz, tRise = 2.5ns (datasheet page 17)
    TWI0_MBAUD = 0x5f;
    TWI0_CTRLA &= ~TWI_TIMEOUT0_bm;      // Disable Timeout for TWI operation
    TWI0_MSTATUS = TWI_BUSSTATE_IDLE_gc; // Manually set the Bus state to IDLE for TWI operation
}

 

 

Then I also have functions for Star and Stop conditions but these ones might not be needed:

void Start_Bit(void){
    // Wait until BUS is IDLE
    while( (TWI0_MSTATUS & TWI_BUSSTATE_gm) == 0x01)
        /*wait*/;

    // Send Start Bit
    TWI0_MCTRLB = TWI_MCMD_REPSTART_gc;
}

void Stop_Bit(void){
// Wait until BUS is IDLE
    while( (TWI0_MSTATUS & TWI_BUSSTATE_gm) == 0x01)
        /*wait*/;

    TWI0_MCTRLB = TWI_MCMD_STOP_gc;
}

 

Then I have this for reading packets of data

uint8_t TWI_Read(uint8_t d_address, uint8_t* packet){

    // Send Slave address and R/W operation
    TWI0_MADDR = 0x18; // 0x18 + 0x00 for write operation

    // Check for Slave ACK/NACK bit for slave address
    if( !(TWI0_MSTATUS & TWI_RXACK_bm) ){
        // Read data
        *packet = TWI0_MDATA;
    }

    if(TWI0_MSTATUS & TWI_RIF_bm)
        return 0x00;
    if(TWI0_MSTATUS & TWI_WIF_bm)
        return 0x01;
}

 

Last Edited: Sun. Mar 24, 2019 - 01:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I sent a support ticket to Microchip about the TWI interface and if it was possible for them to create a basic example guide on how to configure TWI for AtMega4808.

 

They told me there is a document I could use to try to figure it out because registers were written in the same way as for AtMega4808.

This document purpose is a little different from what I need it for but still is something. This document describes, if I'm not mistaken, a bootloader operation using TWI but in Slave mode!

So I started looking into the project files and I see that even if that is a basic and simple example, I think it's still a little complex to start with because they handle collisions and other stuff I was not interested in yet! And they are also using structs and enums to create data types that maks it a bit harder to "decode" so to speak! Ok it may be a bit more intuitive when you get used to it but for now it's not very user friendly yet (at least for me).

 

Anyway, I still tried to see what is happening at least inside the function that initialises the TWI interface and I might not be that far from what it is needed.

 

So their function is as follow:

 

static void init_twi(void)
{
	/* Switch pins to alternate TWI pin location */
	PORTMUX.CTRLB |= PORTMUX_TWI0_ALTERNATE_gc;

	/* Enable TWI driver */
	i2c_slave_init();
}

 

Then i2c_salve_init() is:

 

void i2c_slave_init()
{

	// TWI0.CTRLA = 0 << TWI_FMPEN_bp /* FM Plus Enable: disabled */
	//       | TWI_SDAHOLD_OFF_gc /* SDA hold time off */
	//       | TWI_SDASETUP_4CYC_gc; /* SDA setup time is 4 clock cycles */

	// TWI0.DBGCTRL = 0 << TWI_DBGRUN_bp; /* Debug Run: disabled */

	TWI0.SADDR = I2C_SLAVE_ADDRESS << TWI_ADDRMASK_gp /* Slave Address */
	             | 0 << TWI_ADDREN_bp;  /* General Call Recognition Enable: disabled */

	// TWI0.SADDRMASK = 0 << TWI_ADDREN_bp /* Address Mask Enable: disabled */
	//       | 0x0 << TWI_ADDRMASK_gp; /* Address Mask: 0x0 */

	TWI0.SCTRLA = 0 << TWI_APIEN_bp    /* Address/Stop Interrupt Enable: disabled */
	              | 0 << TWI_DIEN_bp   /* Data Interrupt Enable: disabled */
	              | 1 << TWI_ENABLE_bp /* Enable TWI Slave: enabled */
	              | 1 << TWI_PIEN_bp   /* Stop Interrupt Enable: enabled */
	              | 0 << TWI_PMEN_bp   /* Promiscuous Mode Enable: disabled */
	              | 1 << TWI_SMEN_bp;  /* Smart Mode Enable: enabled */

	TWI0.MCTRLA = 1 << TWI_ENABLE_bp;  /* Enable TWI Master: enabled, for bus error detection */
}

 

So, anyone willing to help me to go through this code and write my own code (an even mor simple version of this code just to make the TWI work)?

 

The code they sent me is here:

https://www.microchip.com/wwwApp...

Last Edited: Thu. Mar 28, 2019 - 08:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is ONE thing that catches most new TWI/I2C users and even some of us who have used it before. That is the matter of address.

 

There are SEVEN true address bits and one read/write bit. Some peripheral vendors give you the 7 bit address, often disguised in hex so that it is not obvious that only 7 bits are involved. Then, YOU have to supply that 8th bit according to the  operation you want to perform. Other vendors give you TWO addresses as read and write addresses. When you look at them, they differ only in the LSB. These are full 8-bit addresses. The full  I2C manual from NXP works in terms of 7 bit addresses.

 

The peripheral spec sheet often is not very explicit about this, leading to confusion. 

 

At the master end, you are simply provided with an 8-bit address register. If you have a 7-bit address for an AVR master, you MUST left shift it by one bit and add in the read or write "address" bit in the LSB. If you have an 8-bit address, just load that address into the address register without any shifting.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Thu. Mar 28, 2019 - 09:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:

There is ONE thing that catches most new TWI/I2C users and even some of us who have used it before. That is the matter of address.

 

There are SEVEN true address bits and one read/write bit. Some peripheral vendors give you the 7 bit address, often disguised in hex so that it is not obvious that only 7 bits are involved. Then, YOU have to supply that 8th bit according to the  operation you want to perform. Other vendors give you TWO addresses as read and write addresses. When you look at them, they differ only in the LSB. These are full 8-bit addresses. The full  I2C manual from NXP works in terms of 7 bit addresses.

 

The peripheral spec sheet often is not very explicit about this, leading to confusion. 

 

At the master end, you are simply provided with an 8-bit address register. If you have a 7-bit address for an AVR master, you MUST left shift it by one bit and add in the read or write "address" bit in the LSB. If you have an 8-bit address, just load that address into the address register without any shifting.

 

Jim

 

Yes, I think I'm aware of that bit. The Slave device address is 0x18 so I'm adding (OR) 1 bit to the LSB of this address for read operation or 0 for Write operation. I have defined 2 macros like:

 

#define READ_OP (0x01 << 0x00)
#define WRITE_OP (0x00 << 0x00)

So, when I want to add/remove this bit I just use it like:

 

REGISTER = address | READ_OP;

or

REGISTER = address | WRITE_OP;

 

Now what I want is to try to use those 2 functions to try to build my own for my device!

Last Edited: Thu. Mar 28, 2019 - 09:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the meantime, someone has succeeded in programming TWI-Master Mode functionally?

Since current datasheet does not seem flawless and there is no "Getting started with TWI" or other helpful code regarding this mode I hope for some practical help here.

 

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

PsyScOrpiOn -

 

If you have a 7 bit address, you have to shift it left by 1 bit before combining the read/write bit with it.

 

I might do something like this

#define DEVADDR 0x18

#define READBIT 0x01
#define WRITEBIT 0x00

#define WRITEADDR ( (DEVADDR << 1) | WRITEBIT )
#define READADDR ( (DEVADDR << 1) | READBIT )

Well, I MIGHT to something like that but probably not. Instead, I would probably just write, straight out,

#define WRITEADDR ( (0x18 << 1) | 0x01 )
#define READADDR  (0x18 << 1)

But, that is just me.

 

Jim

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Sat. Apr 6, 2019 - 11:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

GermanFranz wrote:

In the meantime, someone has succeeded in programming TWI-Master Mode functionally?

Since current datasheet does not seem flawless and there is no "Getting started with TWI" or other helpful code regarding this mode I hope for some practical help here.

 

I got closer today. Bed time now. 3am here.
I'll report here tomorrow if I get it done.

Last Edited: Sun. Apr 7, 2019 - 10:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:

PsyScOrpiOn -

 

If you have a 7 bit address, you have to shift it left by 1 bit before combining the read/write bit with it.

 

I might do something like this

#define DEVADDR 0x18

#define READBIT 0x01
#define WRITEBIT 0x00

#define WRITEADDR ( (DEVADDR << 1) | WRITEBIT )
#define READADDR ( (DEVADDR << 1) | READBIT )

Well, I MIGHT to something like that but probably not. Instead, I would probably just write, straight out,

#define WRITEADDR ( (0x18 << 1) | 0x01 )
#define READADDR  (0x18 << 1)

But, that is just me.

 

Jim

 

Well, that's what I just learnt today from an IRC friend that taught me that. I changed it and i can see it's now correct, at least for the address part. I can see it with my scope I2C decoder.

I'll try to keep going tomorrow and figure out how to read temps from the sensor. I think I'm close.

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

Hello.

 

I'll state here the point where I'm at!

 

I'm trying to make 256 temperature reads, sum them and average the value. Then I send that value to my board USART2 which heads to my laptop terminal (supposedly).

 

I have created several functions for the TWI implementation and for the USART device. The code for the USART device should be working because I've used it before in another example project I made.

 

For the TWI implementation I created a function to initialise the device, another for a Read operation and few others but I'll only discuss about the Read operation function because it's the one I'm working with.

This TWI_Read() function, receives the Slave address and a couple more parameters. I'm still not going to discuss if this function is the best one or not. It's not but the concern here is to make it work. So please do not lose focus trying to improve the function structure or the way it was created because that's not the point for now! I'll improve it when I know the important code is working!

 

I have one version of the code where I can see that the Slave device ACK its address. I see it in scope. I'll post a screen. This version, in my opinion is not the best because I'm sending the address of the slave device in each read operation and I think it's not needed or intended to be like that, so I tried to change the function and everything breaks a part!

 

I'll post now the main() function and the TWI_Read() function that is kind of working but I don't agree with it because I'm sending the Slave address every temperature reading operation. I think this should not be like this!

 

main() function:

int main(int argc, char** argv) {
    uint8_t temperature[256] = {0};
    uint8_t sumtemp = 0;
    float avgtemp = 0.0;
    char message[256];

    CPU_Init();
    USART2_Init();
    TWI_Init();

    while(0x01){
        for(uint8_t i = 0; i < 256; i++){
            TWI_Read(0x18, &temperature[i]);
            sumtemp += temperature[i];
        }
        avgtemp = sumtemp / 256;
        USART2_SendStr(itoa(avgtemp, message, 10));
        USART2_SendChar('\n');
        _delay_ms(1000);
    }

    return (EXIT_SUCCESS);
}

 

TEI_Read() function:

uint8_t TWI_Read(uint8_t sl_addr, uint8_t* packet){

    while(!(TWI0_MSTATUS & TWI_BUSSTATE_BUSY_gc))
        /*wait*/;

    // Send Slave address and R operation onto the bus
    TWI0_MADDR = (sl_addr << 1) | READ_OP;

    // Check Slave device ACK/NACK bit to signal address not/acknowledgment
    if(TWI0_MSTATUS & TWI_RXACK_bm){
        return STATUS_NACK; // Slave Address NOT received
    }

    // Data byte is stored into TWI0_DATA register and passed to *packet variable
    *packet = TWI0_MDATA;
    TWI0_MCTRLB = TWI_ACKACT_ACK_gc;

    // Check Master device ACK/NACK bit to signal data not/acknowledgment
    //if (TWI0_MSTATUS & TWI_RXACK_bm)
        //return STATUS_NACK;

    return STATUS_OK;
}

The code using TWI_Read() function like this allows me to see on my scope the Slave address being ACK:

update your browser

 

 

According to the datasheet, I can issue as many read operations as I want and I don't need to send Slave address prior to each read:

upgarde your browser

So, I tried to move the for loop in main() function into TWIRead() function to avoid sending Slave address for each for loop, like this:

 

main() function:

int main(int argc, char** argv) {
    uint8_t temperature[256] = {0};
    uint8_t sumtemp = 0;
    float avgtemp = 0.0;
    char message[256];

    CPU_Init();
    USART2_Init();
    TWI_Init();

    while(0x01){
        TWI_Read(0x18, temperature, &sumtemp, &avgtemp);
        USART2_SendStr(itoa(avgtemp, message, 10));
        USART2_SendChar('\n');
        _delay_ms(1000);
    }

    return (EXIT_SUCCESS);
}

 

TWI_Read() function

uint8_t TWI_Read(uint8_t sl_addr, uint8_t* packet, uint8_t* psumtemp, float* pavgtemp){
    // Make sure BUS is idle
    while(TWI0_MSTATUS & TWI_BUSSTATE_BUSY_gc)
        /*wait*/;

    // Send Slave address and R operation onto the bus
    TWI0_MADDR = (sl_addr << 1) | READ_OP;

    // Check Slave device ACK/NACK bit to signal address not/acknowledgment
    if(TWI0_MSTATUS & TWI_RXACK_bm){
        return STATUS_NACK; // Slave Address NOT received
    }

    for(uint8_t i = 0; i < 256; i++){
        // Data byte is stored into TWI0_DATA register and passed to *packet variable
        *(packet + i) = TWI0_MDATA;
        // Send ACK from Master to signal Data packed readed
        TWI0_MCTRLB = TWI_ACKACT_ACK_gc;
        // Sum the readed value
        *psumtemp += *(packet + i);
    }
    // Send NACK from Master to signal don't need more data
    TWI0_MCTRLB = TWI_ACKACT_NACK_gc;

    // Send Stop bit to terminate transaction
    TWI0_MCTRLB = TWI_MCMD_STOP_gc;

    // Average readed values
    *pavgtemp = *psumtemp / 256.0;

    // Check Master device ACK/NACK bit to signal data not/acknowledgment
    //if (TWI0_MSTATUS & TWI_RXACK_bm)
        //return STATUS_NACK;

    return STATUS_OK;
}

 

But this last version shows nothing on scope! If anyone can help me figuring out why, it would be awesome!

Last Edited: Sun. Apr 7, 2019 - 05:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your code. Could you add TWI_Init() too?

Sorry I'm not C expert enough to find out your bug quickly but I was now able to create an interrupt-based, working TWI-master implementation in assembler (tomorrow @ projects).

Last Edited: Sun. Apr 7, 2019 - 06:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

GermanFranz wrote:

Thanks for your code. Could you add TWI_Init() too?

Sorry I'm not C expert enough to find out your bug quickly but I was now able to create an interrupt-based, working TWI-master implementation in assembler (tomorrow @ projects).

 

Sure.

 

void TWI_Init(void){
    TWI0_CTRLA |=  TWI_SDASETUP_4CYC_gc     // SDASETUP time of 4 clock cycles
                 | TWI_SDAHOLD_500NS_gc;    // SDA Hold time - Meets SMBus specifications across all corners (0x03) ???
    TWI0_CTRLA &= ~TWI_FMPEN_bm;            // Fast Mode Plus disabled
    
    // Set MBAUD to get 100KHz for SCL from fSCL = CLK_PER / (10 + 2*BAUD + CLK_PER*tRise)
    // fSCL = 100KHz, CLK_PER = 20MHz, tRise = 2.5ns (datasheet page 17)
    TWI0_MBAUD = 0x5f;
    TWI0_CTRLA &= ~TWI_TIMEOUT0_bm;         // Disable Timeout for TWI operation
    TWI0_MSTATUS = TWI_BUSSTATE_IDLE_gc;    // Manually set the Bus state to IDLE for TWI operation
    PORTMUX_TWISPIROUTEA =  PORTMUX_TWI0_DEFAULT_gc;// Set default port and pins for SDA and SCL lines
    PORTA_DIR |= PIN3_bm | PIN2_bm;
    TWI0_MCTRLA = TWI_ENABLE_bm;            // Enable Master Mode of operation
}

 

There are settings that might be not needed due to the fact that their default setting might be the one I'm setting there, but I used them anyway to prevent that I miss any more details!

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


I'm having more issues now with another setting that I decided to change.

 

In my Salve device datasheet there is this setting:

 

The Serial Port Clock Frequency says up to 400Khz so I decided to change the Master Device main clock frequency from 20Mhz to 20Mhz / 64 = 312.5Khz which is within Slave device range.

But the formula that my Master device datasheet says to use to evaluate this MBAUD value and that returned a value of 95 when I did the math for the first time assuming an f_SCL freqeucny of 100Khz is now returning a negative value for 312.5Khz.

 

Master device datasheet says:

So, the first time I used this formula I assumed an f_SCL= 100Khz, f_CLK_PER = 20Mhz (default Master device main clock frequency) and T_RISE = 2.5ns also from my Master device datasheet.

Here my unknown is BAUD and solving this in order to BAUD I get:

 

100Khz = 20Mhz / (10 + 2*BAUD + 20Mhz*2.5ns)

BAUD = 95 approx...

 

But now, I used a prescaler to bring my Master device main clock frequency to a value which would be within Slave device SCL frequency range. So I chose to use a 64 prescalor factor.

 

So, the above formula would become filled with the following parameters:

 

I assumed f_SCL = 20Mhz / 64 = 312.5Khz

 

312.5Khz = (20Mhz/64) / (10 + 2*BAUD + (20Mhz/64)*2.5ns)

BAUD = - 4.5

 

This is not reasonable and at this point I don't know what makes and what doesn't makes sense!

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

PsySc0rpi0n wrote:
PORTA_DIR |= PIN3_bm | PIN2_bm;
This should not be necessary since twi configures pins itself.

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

GermanFranz wrote:

PsySc0rpi0n wrote:

PORTA_DIR |= PIN3_bm | PIN2_bm;

 

This should not be necessary since twi configures pins itself.

 

Ok, thank you for letting me know!

 

Can you also check about the latest problem I stated about the frequency setting?

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

PsySc0rpi0n wrote:
Can you also check about the latest problem I stated about the frequency setting

I remember reading somewhere not every BAUD / fCLK_PER value is allowed... The amount of configuration options is limited.

Last Edited: Sun. Apr 7, 2019 - 09:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

GermanFranz wrote:

PsySc0rpi0n wrote:

Can you also check about the latest problem I stated about the frequency setting

 

I remember reading somewhere not every BAUD / CLK_PER value is allowed... The amount of configuration options is limited.

 

So how can I possibly check that? I don't remember of reading anything like that in the datasheets I'm using.

 

I'm trying another version of my code.

I have worked around the f_SCL frequency and BAUD value and tried to send the address and read a single byte of data, then send NACK from Master and issue a Stop_Bit() and check what shows up on scope.

 

Thinks are not looking pretty for 2 reasons:

1st - I can't activate the Env. Table that shows the decoding results, if applicable.

2nd - I can't understand how to decode it by myself. I was expecting a specific number of bits and that those bits could tell me something more meaningful. But I can't fetch anything from it other than what the following picture shows (if I'm correct):

TWI scope

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

Fclk_per is the period, not the frequency. Eg 20Mhz = 50ns

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

Kartman wrote:
Fclk_per is the period, not the frequency. Eg 20Mhz = 50ns

 

No. It's frequency.

 

MBAUD = (fCLK_PER - 10*fSCL) / 2*fSCL. 

 

This formula is sufficient in practice, fCLK_PER * TRise (<1) mostly not significant.

Last Edited: Mon. Apr 8, 2019 - 05:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

Fclk_per is the period, not the frequency. Eg 20Mhz = 50ns

 

Hello.

 

I was about to believe you but the fact is that the frequency was matching the math on scope so I was wondering and was about to check again the formula and eventually try to change the formula parameters to see if anything would change. Then the next post confirmed that actually I used the correct parameters!

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

GermanFranz wrote:

Kartman wrote:
Fclk_per is the period, not the frequency. Eg 20Mhz = 50ns

 

No. It's frequency.

 

MBAUD = (fCLK_PER - 10*fSCL) / 2*fSCL. 

 

This formula is sufficient in practice, fCLK_PER * TRise (<1) mostly not significant.

 

Thanks for confirming. In fact, the scope was confirming because the frequency is matching the math.

 

But after all, my code is still not working. I would need some help to try to figure out why.

I could post here all my code but to b honest the code tags are not very friendly so I was thinking about to create a Git repo and everyone could see and change the code.

I have one version of my code that is partially working but in my opinion it's not the most logically correct and then I have another version that I think it is logically more correct but that it's not working at all!

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

PsySc0rpi0n wrote:
Then a bit further they talk about the interface initialisation where is said that properties SDASETUP and SDAHOLD must be set before initialising the device.

 

Y feel sure MCP9808 will need this?

 

PsySc0rpi0n wrote:
But this last version shows nothing on scope!

 

Maybe because a '!' in  TWI_Read(), third line? :)

 

 

PsySc0rpi0n wrote:
When I search for this register, I see this is an 8 bit register, so I can't write 100KHz there.

 

In my thoughts I often ask that question to the mcu engineers. Same with UART: chip should know its current frequencies. Just tell the mcu 9600 Baud...

Why mcu's more and more complex constructed as stupid configuration monsters instead of helping the programmer in an intelligent way? surprise

 

How many working hours were wasted worldwide due to a wrong set baud rate?

OK I would like to acknowledge that the new Series-0/1 TWI architecture better automates the processes. yes

But, the good impression is affected by the faulty data sheet no

 

 

Last Edited: Tue. Apr 9, 2019 - 09:59 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

GermanFranz wrote:

PsySc0rpi0n wrote:
Then a bit further they talk about the interface initialisation where is said that properties SDASETUP and SDAHOLD must be set before initialising the device.

 

Y feel sure MCP9808 will need this?

 

I'm not sure so I placed those 2 settings there!

 

GermanFranz wrote:

PsySc0rpi0n wrote:
But this last version shows nothing on scope!

 

Maybe because a '!' in  TWI_Read(), third line? :)

 

In which code?

 

GermanFranz wrote:

PsySc0rpi0n wrote:
When I search for this register, I see this is an 8 bit register, so I can't write 100KHz there.

 

In my thoughts I often ask that question to the mcu engineers. Same with UART: chip should know its current frequencies. Just tell the mcu 9600 Baud...

Why mcu's more and more complex constructed as stupid configuration monsters instead of helping the programmer in an intelligent way? surprise

 

How many working hours were wasted worldwide due to a wrong set baud rate?

OK I would like to acknowledge that the new Series-0/1 TWI architecture better automates the processes. yes

But, the good impression is affected by the faulty data sheet no

 

Well, I have to admit this is also my first time reading a datasheet and trying to do something from scratch so I might also misunderstanding many things in the datasheet. Probably for someone with some more experience than me, would find the datasheet pretty explanatory. But not me!

I need to keep digging!

 

I'm was even trying to split my TWI_Read() function line by line and add each line before the while() loop and use my scope in "Single" mode where it captures the first bits on SCL line to see if anything was going on, but when I place the line of code with the Slave address before the while(1) loop, I can only capture a single transition on SCL line from 0 to 1. Nothing else! I have also increased frequency to 20Mhz/8 and MBAUD to 7. This was supposed to return a frequency of about 104Khz but my scope says about 111Khz. Hope this is not critical!

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

PsySc0rpi0n wrote:
In which code?

 

I quoted "But this last version..." Did you not notice that your two code versions differ in this point?

Did you not wonder if a "!" could make a difference?

 

PsySc0rpi0n wrote:
I'm was even trying to split my TWI_Read() function line by line

use my scope in "Single" mode where it captures

 

You should not develop your code on the scope but with the datasheet.

I had to fight with the data sheet for a few days too (please have look for latest version).

What helped me the most were these two pictures:

 

 

 

and especially

 

 

You have to understand these processes first then the reading in of data is no longer a problem. If details are unclear please ask.

 

PsySc0rpi0n wrote:
I have also increased frequency to 20Mhz/8 and MBAUD to 7. This was supposed to return a frequency of about 104Khz but my scope says about 111Khz. Hope this is not critical!

 

That's all irrelevant now, same with timing parameters in TWI_CTRLA.

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

I need to apologize but as I wait for answers here I'm always trying to change things, read and think about other possible interpretations and therefore, when people replies here and I see the replies, many of the times, my code is already way different than when I posted here my questions.

 

So, about the '!', of course I know it may change the code dramatically but by the time I read your reply, that '!' was already gone and probably the condition being tested was also different.

 

Anyway, about those schemes/pictures you posted, I think I understand the concept and the order things happens but turn it into code is another very different thing. That has been my struggle so far!

 

And Mr. @GermanFranz, you said you're no expert in C and you don't have to be. I'm not an expert in C at all. This is all about having some background on I2C, I mean, to have done this before and to be used reading datasheets! I'm not comfortable with any of them yet.

 

Just as a note and to give you some context on my (in)experience on the matter, I didn't even know that the way of coding the following directive - ADDRESS + R/W - was by means of a left shift and adding a bit to the LSB position. I interpreted this as adding (due to the '+' sign) 1 to the address of the slave device like 0x18 + 0x01 = 0x19. I now know this makes no sense but when I read ADDRESS+R/W, to me this was always a sum operation, 0x18 + 0x01, read operation, 0x18 + 0x00, write operation.

 

Anyway, a part from this, I'm more interested in figuring out how to make this work.

I wanted to understand why one of the versions of the code where I have the TWI_Read() function being called 256 times inside the while(1)  is working and I can see the Slave address being sent to the bus and being ACK by the Slave device and when I change the code so that the for loop with the 256 cycles goes inside the TWI_Read() function itself, I can no longer see the Slave address being sent to the bus and ACK by the Slav device.

 

I'm going to place both code online and paste here the links because it will be more easily readable!

 

 

Edited;

 

Code with 256 reads. This loop with 256 cycles is inside th while(1) loop in main() function. This version of the code is the only one where I can see on my scope the Slave address being sent to the bus and being ACK by the Slav address. However, I think this is not the correct way of doing things because it is not supposed that I send the Slave address in each one of the 256 reads. Datasheet shows I can perform as many reads as I like and Figure 24-7, page 339 here.

 

https://ideone.com/uG2KVT

 

Then I tried to change the code so that the Slave address wouldn't be sent in every one of the 256 loops, by moving this loop into the TWI_Read() function but despite the fact that I think I changed nothing in the way each setting is happening, I can't understand why I can't see a thing on my scope.

 

https://ideone.com/t6att5

 

The only thing I did was to adapt the 2nd version to have the 256 reads inside TWI_Read() function but with this code I can't see anything at all on scope!

 

This is where I need help to try to check where the problem might be.

Last Edited: Wed. Apr 10, 2019 - 08:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

If you have some more questions to "Settings meaning for TWI device" (title of this thread), to twi function or datasheet don't hesitate to ask. Any other code-check its stuff for C-experts here:) The ways of cognition painfully often lead over trial and error, but I think with your patience you will arrive!

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

GermanFranz wrote:

If you have some more questions to "Settings meaning for TWI device" (title of this thread), to twi function or datasheet don't hesitate to ask. Any other code-check its stuff for C-experts here:) The ways of cognition painfully often lead over trial and error, but I think with your patience you will arrive!

 

This is not about C. There's nothing complex in the code. This is in fact all about settings and knowing about I2C/TWI. All C code here is just meaning of settings, knowing when and how to use them. Not anything related to C programming language!

 

Ok, my last post is asking about C but only because I can't spot any big change in the chronology of events and I was hoping someone to help me, but other than that, it's all about settings and I2C/TWI.

Last Edited: Fri. Apr 12, 2019 - 08:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi PsySc0rpi0n,

concrete questions indicate a deeper involvement with the topic. I can not recognize such questions and now I (and probably other people) don't know how can help you. If you want to read several bytes of the slave coherently you must implement the processes according to figure 24-13, otherwise it will not work!

You should use your time because since my code works in a weather station outside I will certainly forget many things again ๐Ÿ˜

Last Edited: Sat. Apr 13, 2019 - 12:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Hi.

 

I can understand the BUS State, State diagram. Translate it into code is another very different matter.

 

About the other picture, it's confusing to me at some points. I'll try to follow the picture and say how I translated it into code.

 

According to the picture description, light grey diamond shape is something that must be done with code. Dark grey is stuff related to bus state. So, having this said, I start looking to the picture and I see M1. I read about M1 and it is said that it's for a case of Arbitration Lost or Bus Error during Address Packet. But I don't understand this. How can this be an error during an Address packet if, at this point, and according to the picture provided, there is no address byte sent yet?

 

Same for M2 which is described as Address Packet Transmit Complete - Address not Acknowledged by Slave. There is no address packet sent yet so how they say that at M2 there is an address packet transmission complete and not ACK by the Slave device. No sense to me here either!

 

Again, for M3 which is described as Address Packet Transmit Complete - Direction Bit Cleared. How can the Address packet transmission to be completed if, according to the picture, the Address rectangle is yet to come? I can't understand this, at least until here.

 

Anyway, I tried to follow the diagram from the beginning, ignoring these 3 first cases. So, the first thing I see is that in my code, I have to wait until the BUS becomes IDLE. I do it with the following line of code inside my TWI_Read() function:

while((TWI0_MSTATUS & TWI_BUSSTATE_gm) != TWI_BUSSTATE_IDLE_gc)
      /*wait*/;

Then the picture says I need to issue a Start Bit/Condition. The datasheet says that when the TWI0_MADDR is written with the Slave address and the R/W bit, the Start Bit/Condition is automatically issued by the hardware.

 

So I do this with the following line of code:

#define READ_OP (0x01 << 0x00)

TWI0_MADDR = (0x18 << 1) | READ_OP;

So, at this point, I can save the ACK/NACK from the Slave device or just ignore it, I guess. I chose to save it. Or better, I'm returning it with the following lines of code:

#define STATUS_NACK 0x01

if(TWI0_MSTATUS & TWI_RXACK_bm)
      return STATUS_NACK; // Slave Address NOT received

TWI_RXACK is 0 if ACK is signaled by the Slave device and 1 if NACK is signaled.

 

After this, and if I understand the thing, the Slave device responded with the ACK/NACK and I can now read data bytes from the Slave device.

 

I assume that at this point I'm here in the picture:

Now I think it's time to read data bytes from the Slave device. Also here I'm not sure how it's done, but for some reason, I assumed that after the Slave device knows it will be performed a Read operation on the BUS that it will send it's data onto the BUS so that the Master device can fetch it from the BUS into it's TWI0_MDATA register, so I try to read with the following line of code:

*packet = TWI0_MDATA;

But I'm not sure this is the way to read data bytes from the Slave device or if I have to access some specific register directly from Slave device. I really don't know!

The, reading both the datasheet and this picture at the same time, I see that a transaction for a Master Read operation should obey the following frame:

My reasoning tells me about this picture, that in the case that I only need to read a single data byte, I must issue a NACK from the Master device to signal I don't want to read more data and after that, I must issue a Stop Bit/Condition.

And by looking to the picture with the 4 cases, M[1-4], I see that if everything goes smoothly, I'll go to either M4 or M2. I exclude M4 because it doesn't match the above picture of the Master Read frame format. I want to read only one byte of data, so I need to have a NACK followed by the Stop Bit/Condition. The only case that matches this situation is M2 but M2 was described as Address Packet Transmit Complete - Address not Acknowledged by Slave. So, what the hell? I'm not at an Address packet transmission right now. I'm at a Read data byte. No sense once more in my opinion. So I can't understand where am I going wrong. Despite all this, I'm issuing a NACK bit and a Stop bit/Condition from the Master device with the following lines:

 

TWI0_MCTRLB = TWI_ACKACT_NACK_gc;
TWI0_MCTRLB = TWI_MCMD_STOP_gc;

So, I acknowledge that I have ignored some aspects from that picture with the M cases and I think I have tried to follow the above frame format for a Master Read operation and still no data bytes can be fetched from the BUS by the Master device!

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

I know Fleury does not support AVR-0/AVR-1 but what it does do is correctly implement the TWI sequence of operation. So I'd study what it does for tiny/mega then simply port/translate that to the AVR-0/1 hardware way of doing things. The higher level algorithm operation will be the same.

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

Hello PsySc0rpi0n,

 

It looks complicated but in fact it needs less code compared to older Mega-TWI. Let answer me step by step, beginning today with first part of your last posting. How you have to talk with your Sensor-Slave and how you finally translate everything into c code its your task, but I can help you with the understanding of Master-Read procedere @ figure 24-13 flow- and datasheet.

 

PsySc0rpi0n wrote:
I read about M1 and it is said that it's for a case of Arbitration Lost or Bus Error during Address Packet. But I don't understand this. How can this be an error during an Address packet if, at this point, and according to the picture provided, there is no address byte sent yet?

 

The IDLE status is start point and the prerequisite for a proper procedure. 

 

If something went wrong with an attempted address command before (because a unexpected electric bus problem, collision with devices) bus becomes BUSY, a Master Write Interrupt ("Error") is released and ARBLOST or BUSERR bits in MSTATUS are set. Then

PsySc0rpi0n wrote:
I have to wait until the BUS becomes IDLE.

maybe not enough, if there is no bus time-out enabled or stop condition detected you have to force IDLE status by writing to the bus state bits. Or you do same me: Switch off TWI in case of any error and start again: Then bus state must be manually set to IDLE too.

 

PsySc0rpi0n wrote:
Same for M2 which is described as Address Packet Transmit Complete - Address not Acknowledged by Slave. There is no address packet sent yet so how they say that at M2 there is an address packet transmission complete and not ACK by the Slave device. No sense to me here either!

 

Same situation: An attempted address command before! A Master Write ("Error") Interrupt is released. If MSTATUS/RXACK flag not reflects what you need (ACK from slave) you can send stop-condition and after that bus becomes wanted IDLE again! In this case there is most time a bigger problem with slave or i2c address is wrong.

 

PsySc0rpi0n wrote:
Again, for M3 which is described as Address Packet Transmit Complete - Direction Bit Cleared. How can the Address packet transmission to be completed if, according to the picture, the Address rectangle is yet to come? I can't understand this, at least until here.

 

This is the case after sending a "Repeated start". Now address have to follow. I not use this feature.

 

Important to understand: Every state machine like TWI has a past and works in a circle. M1-M4 are location points to close this circle only.

 

Last Edited: Mon. Apr 15, 2019 - 07:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

Have look right side Fig.24-13 again: As part of TWI MASTER INTERRUPT there are 2 interrupts you should activate: Master-Write Interrupt (MWI) and Master-Read Interrupt (MRI). All data transfers can be performed inside these Interrupts without the involvement of the main program! Instead of manual polling WIF / RIF this improves the software efficiency dramatically. Main program only starts transfer! Unlike MRI the MWI has three different functions:

 

-> MWI is released after address write command to MADDR

-> MRI is released after address read command to MADDR

 

-> MWI is released if data is written to MDATA

-> MRI is released (most time) when reading data from MDATA

 

-> MWI is released in case of any error: ARBLOST, BUSERR, no Slave-ACK

 

TWI MASTER INTERRUPT- this is either MRI or MWI, only one can be triggered at the same time. MRI, this also means: No error has occurred!

OK, have look this operation in detail. Remember we want to read several bytes from slave continuous:

 

PsySc0rpi0n wrote:
Then the picture says I need to issue a Start Bit/Condition. The datasheet says that when the TWI0_MADDR is written with the Slave address and the R/W bit, the Start Bit/Condition is automatically issued by the hardware.

 

Right! That happens automatically now. If no MWI but first MRI is triggered afterwards: Everything OK! Slave sent his ACK-Bit!

More happens: The first data byte has already been read in MDATA too!

 

NEW MRI: Interrupt software has to decide now: Need more data or not? If need more data then Master must send ACK-Bit, when the transfer is to be ended Master must send NACK+STOP. This Acknowledge action must be set in MCTRLB/ACKACT first, if new required!  A feature called Smartmode (activation in MCTRLA) helps to automate master-slave communication:

 

If Smartmode enabled AND  ACKACT=ACK (need more data) then reading of MDATA causes: ACK will be sent automatically, next data from slave will be read into MDATA, followed by NEW MRI.

 

Independent of Smartmode, if ACKACT=NACK (need no more data) then nothing happens after reading of MDATA (last data): No Acknowledge will be sent, no more data from slave will be read into MDATA, there is no new MRI. NACK+STOP slave-transfer must be sent via MCTRLB command bits manually in this case and

 

PsySc0rpi0n wrote:

TWI0_MCTRLB = TWI_ACKACT_NACK_gc;
TWI0_MCTRLB = TWI_MCMD_STOP_gc;

 

you can summarize this code in 1 line / 1 instruction.

 

That's it! 

Last Edited: Tue. Apr 16, 2019 - 12:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is quite a bit of information to digest. I'll try to see what I would change in my code after reading your post.

 

This is going to be an endless journey of questions.

 

So you say that waiting for the BUS to become IDLE may not be enough and then you talk about BUSERROR or ARBLOST being set in MSTATUS. In such a case you suggest to turn OFF and then turn TWI ON again, right?

So, I'm using this:

while((TWI0_MSTATUS & TWI_BUSSTATE_gm) != TWI_BUSSTATE_IDLE_gc)
        /*wait*/;

to wait for the BUS to become IDLE. And handling BUSERROR and/or ARBLOST should be after or before waiting for the BUS to become IDLE?

Last Edited: Tue. Apr 16, 2019 - 07:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PsySc0rpi0n wrote:
So you say that waiting for the BUS to become IDLE may not be enough and then you talk about BUSERROR or ARBLOST being set in MSTATUS

 

BUSERR & ARBLOST are symptoms of status BUSY. Have look in Fig. 24-12 how you can come back IDLE.

 

PsySc0rpi0n wrote:
In such a case you suggest to turn OFF and then turn TWI ON again, right?

 

Not only in such a case- at any error. This maybe not the best recommendation (with aborted transactions you lose communication speed) but it simplifies and saves code. In practice, I had so far no bus problems and big challenges with only one sensor I ask every second and a short cable.

 

Remember that arbitation lost problems you can get only with more than one master device on the bus.

And "an illegal bus condition (BUSERR) is detected if a protocol violating Start (S), repeated Start (Sr) or Stop (P) is detected on the bus lines" (Datasheet S.365). With only one master and own software such errors should be excludable.

With short connection lines and matching pullups, the right i2c addresses and working sensor(s) its a surefire thing! Don't be too worried. I saw only one time a i2c device is crashed in a way that no more software, only a power-reset helped.

 

Because my error handling means turn OFF/ON TWI only I can avoid such questions:

 

PsySc0rpi0n wrote:
And handling BUSERROR and/or ARBLOST should be after or before waiting for the BUS to become IDLE?

 

Writing 0x1 to BUSSTATE bits forces the bus state logic into its IDLE state, the next software operation of writing the MADDR will clear both flags.

These are statements from the data sheet. But I have no further experience with that because I treat this case as described, if it could happen...

Last Edited: Wed. Apr 17, 2019 - 10:43 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


GermanFranz wrote:

PsySc0rpi0n wrote:
So you say that waiting for the BUS to become IDLE may not be enough and then you talk about BUSERROR or ARBLOST being set in MSTATUS

 

BUSERR & ARBLOST are symptoms of status BUSY. Have look in Fig. 24-12 how you can come back IDLE.

 

Recover from BUSY back to IDLE, according to that State Machine diagram is only possible with a Stop Bit/Condition + Timeout. This is another thing not making sense to me. At the beginning of TWI section of the datasheet, it's said that TIMEOUT must be disabled for TWI operation.

 

 

GermanFranz wrote:

PsySc0rpi0n wrote:
In such a case you suggest to turn OFF and then turn TWI ON again, right?

 

Not only in such a case- at any error. This maybe not the best recommendation (with aborted transactions you lose communication speed) but it simplifies and saves code. In practice, I had so far no bus problems and big challenges with only one sensor I ask every second and a short cable.

 

Ok. I have to understand and re-write the code in such way I can re-Enable TWI device in the correct timing.

 

GermanFranz wrote:

Remember that arbitation lost problems you can get only with more than one master device on the bus.

 

That I know. Thanks

 

GermanFranz wrote:

And "an illegal bus condition (BUSERR) is detected if a protocol violating Start (S), repeated Start (Sr) or Stop (P) is detected on the bus lines" (Datasheet S.365). With only one master and own software such errors should be excludable.

With short connection lines and matching pullups, the right i2c addresses and working sensor(s) its a surefire thing! Don't be too worried. I saw only one time a i2c device is crashed in a way that no more software, only a power-reset helped.

 

That violation you refer to, is related with electrical interference and other phenomena not controlled by us or what kind of violation we might be talking? And btw, I'm using a small dev board so I'm not using wires or cables. So connections are what they are. The shortest possible, I presume!

 

GermanFranz wrote:

Writing 0x1 to BUSSTATE bits forces the bus state logic into its IDLE state, the next software operation of writing the MADDR will clear both flags.

These are statements from the data sheet. But I have no further experience with that because I treat this case as described, if it could happen...

 

Yeah, I have read that info many times also in the datasheet.

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

To try to do what you said about checking how to recover back to IDLE, I added the following code:

 

if( (TWI0_MSTATUS & TWI_BUSERR_bm) || (TWI0_MSTATUS & TWI_ARBLOST_bm) ){
      TWI0_MCTRLA &= ~(TWI_ENABLE_bm);
      _delay_ms(1);
      TWI0_MCTRLA |= TWI_ENABLE_bm;
      TWI0_MCTRLA |= TWI_BUSSTATE_IDLE_gc;
   }

If BUSERR or ARBLOST are detected, I disable TWI, wait a small amount of time (not sure this is needed), re-Enable it and set the BUS state to IDLE manually. But I placed this piece of code after the while loop waiting for the BUS to be come IDLE at the beginning of my TWI_Read() function!

 

But at this point, I'm not sure the while loop waiting for the bus to become IDLE is still necessary or not.

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

PsySc0rpi0n wrote:
At the beginning of TWI section of the datasheet, it's said that TIMEOUT must be disabled for TWI operation.

Right. Usable or not - I do not know either. But no problem, we not need!

PsySc0rpi0n wrote:
But at this point, I'm not sure the while loop waiting for the bus to become IDLE is still necessary or not.

No. Because it's sure you get IDLE if you

PsySc0rpi0n wrote:
re-Enable it and set the BUS state to IDLE manually

if there is no other master, all connections OK and i2c devices not faulty.

PsySc0rpi0n wrote:

That violation you refer to, is related with electrical interference and other phenomena not controlled by us

This is caused from other masters or wrong commands you send to the bus (for example only start+stop).
The question if other phenomena play a role here I can not answer exactly but these are rare enough to prevent you can write working code now ๐Ÿ˜

Or you can use https://www.avrfreaks.net/comment/2678521#comment-2678521 ๐Ÿ‘

Last Edited: Thu. Apr 18, 2019 - 08:10 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

GermanFranz wrote:

This is caused from other masters or wrong commands you send to the bus (for example only start+stop).
The question if other phenomena play a role here I can not answer exactly but these are rare enough to prevent you can write working code now ๐Ÿ˜

Or you can use https://www.avrfreaks.net/comment/2678521#comment-2678521 ๐Ÿ‘

 

How can he use that code ????!! that code does a completly different task.

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

Moe123 wrote:
 How can he use that code ????!! that code does a completly different task.

 

Sure? It's same TWI Mr.Moe123 wink

Last Edited: Thu. Apr 18, 2019 - 09:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

GermanFranz wrote:

Moe123 wrote:

 How can he use that code ????!! that code does a completly different task.

 

 

Sure? It's same TWI Mr.Moe123 wink

 

Diefferent "task", its not just about how to initialize TWI and thats it....

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

Moe123 wrote:
Diefferent "task", its not just about how to initialize TWI and thats it....

 

Well Mr. Moe123,

first, this helpful instructive code does not just show the initialization and second, it would be nice if you could offer specific help on the subject here.

Your objections, however, do not make much hope , or?

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

Herr Franz,

 

Please read again what I wrote.

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

Nahhh... It's not working.

 

I0ve changed a few things but I can't see on scope what I was expecting.

 

I haven't seen that code yet. But to go through it I need also to take a look at that chip.

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

PsySc0rpi0n wrote:
I haven't seen that code yet. But to go through it I need also to take a look at that chip.

Once again: TWI its the same! Completely. If this C-Code is working on ATTiny402 and an ATTiny416 you can use same techniques, same code for read/write access using Mega4808.

Moe123 wrote:
Please read again what I wrote

Please read again what is "task" of this thread.

Last Edited: Sat. Apr 20, 2019 - 03:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

GermanFranz wrote:
PsySc0rpi0n wrote:
I haven't seen that code yet. But to go through it I need also to take a look at that chip.
Once again: TWI its the same! Completely. If this C-Code is working on ATTiny402 and an ATTiny416 you can use same techniques, same code for read/write access using Mega4808.

 

Ok, I guess I'll have to give it a try!

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

Hello.

 

Finally back to my TWI thing.

 

I've been looking to the code in those 2 files.

I have some questions because I'm not sure if things are the way I'm thinking they are!

 

I'll go through each code regarding each question.

 

In the following code:

char TWI_Start(char addr){
	if ((TWI0.MSTATUS & TWI_BUSSTATE_gm) != TWI_BUSSTATE_BUSY_gc){
		TWI0.MCTRLB &= ~(1<<TWI_ACKACT_bp);
		TWI0.MADDR = addr;

		if (addr & 1){
			while (!(TWI0_MSTATUS & TWI_RIF_bm))
				/*wait*/;
		}
		else{
			while (!(TWI0_MSTATUS & TWI_WIF_bm))
				/*wait*/;
		}
		return TWI0.MSTATUS;
	}
	else
		return TWI0.MSTATUS;
}

First question is why there is this TWI_Start() function and then one other for the Read operation and another for the write operation? I had created only the TWI_Init() and then the TWI_Read() functions. Is this "Start" function mandatory?

Then, I don't understand why there is an ACK signal being sent if the BUSSTATE is different from BUSY... Can you please explain me why?

Then, I don't understand why there is this IF with "addr & 1 ". What is the purpose of this? If this is to set a READ/WRITE operation, then, why are there specific functions for the same Read/Write operations?

Also, RIF and WIF, according to the User Manual, says that these bits are set when a Read or Write operations are successfully completed. But this function has no code to read or write anything from/for the BUS. So why is this code inside this function? Or am I getting all wrong?

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

PsySc0rpi0n wrote:
First question is why there is this TWI_Start() function and then one other for the Read operation and another for the write operation? I had created only the TWI_Init() and then the TWI_Read() functions. Is this "Start" function mandatory?
I may have suggested this before but have you looked at Fleury? Not necessarily the nitty gritty details of the implementation but simply the highlevel API he provides - he has a complete/minimal implementation of everything a normal use of TWI will require:

 

http://homepage.hispeed.ch/peterfleury/doxygen/avr-gcc-libraries/group__pfleury__ic2master.html#func-members

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

clawson wrote:

PsySc0rpi0n wrote:
First question is why there is this TWI_Start() function and then one other for the Read operation and another for the write operation? I had created only the TWI_Init() and then the TWI_Read() functions. Is this "Start" function mandatory?
I may have suggested this before but have you looked at Fleury? Not necessarily the nitty gritty details of the implementation but simply the highlevel API he provides - he has a complete/minimal implementation of everything a normal use of TWI will require:

 

http://homepage.hispeed.ch/peterfleury/doxygen/avr-gcc-libraries/group__pfleury__ic2master.html#func-members

 

Yes, you have already suggested to study Fleury's code and try to start from there, but at this point, I want to try the other suggested code! I was just trying to understand how that code works so that I can use it properly!

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

I kind of took some parts of that code and I can now see e few more things on my scope but I still think it's not correct what scope is showing!

 

My actual code:

https://ideone.com/4GVVf4

 

This is what my scope shows now:

 

Looks like the only correct thing is the address which is 0x18. In this code I was trying to read 5 times and average the value. But I'm not even sure if what I see here represents 5 reads and even if it does (I think there are 6 reads instead), why the sensor is telling 01, FF, FF, FF, FF? This doesn't looks correct at all. The 0x1D could be eventually correct but not all the others!

Last Edited: Fri. Apr 26, 2019 - 10:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Seems to me you have too many unknowns.

 

You probably want to start out with well known, totally understood I2C device - perhaps an RTC from the DS13xx range? Maybe an EEPROM? Use that to get your I2C stack to the point where it's fully implemented, tested and understood and does what you want. Then when you connect to this new "unknown device" any problems you hit must be with that device, not with the I2C implementation.

 

Otherwise you just don't know.

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

PsySc0rpi0n wrote:
why the sensor is telling 01, FF, FF, FF, FF? This doesn't looks correct at all

What should the sensor provide with these data?

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

GermanFranz wrote:
PsySc0rpi0n wrote:
why the sensor is telling 01, FF, FF, FF, FF? This doesn't looks correct at all
What should the sensor provide with these data?

 

Just temperature values. The first byte that is read, should not be 0 either!

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

Where do you set the address to read from? For that matter, what is the slave device? There is no mention of it in your code.

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

Kartman wrote:
Where do you set the address to read from? For that matter, what is the slave device? There is no mention of it in your code.

 

Line 108 of the code. It's in TWI_Read() function! Address is 0x18!

The Slave device is a temperature sensor MCP9808.

Last Edited: Mon. Apr 29, 2019 - 08:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I made this work with the other guy code and a couple of changes!

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

Hi, would you be able to post the working code? Thanks

Pete