AN2565 / AVR311 TWI Slave in assembly?

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

I have a project (ATmega324PB) in assembly that is mostly done but now need to add an I2C slave so I can connect to a SAM4S host.  It is something I started over 10yrs ago with some fairly complex interrupt driven code on other peripherals.   

 

The app note looks perfect, except of course it is in C.  I'd rather not port everything else over this late in the game.  I am essentially looking for a assembly version of the app note code. 

 

I may take a try at it myself as it looks rather straightforward state machine stuff that should translate very easily.  Just wondering if anybody has anything off the shelf that would save me the effort.

 

I have heard of folks compiling C source and then just copying the resulting assembly output.  Seems possible, may take a shot at that too.

 

Thanks!

Dave

 

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

Compile the C then use the asm that generates (-save-temps, fverbose-asm etc)

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

Following up on this.  I can't seem to make clock stretching work to hold off the master until I am ready with data. 

 

AVR311 clearly states:  "If a Slave device cannot handle incoming data until it has performed some other function, it can hold SCL
low to force the Master into a wait-state."

 

The ATmega324 Data sheet says: 

 

    "As long as the TWINT Flag is set, the SCL line is held low."

 

and

     "Writing a one to TWINT clears the flag."

 

The Interrupt flow chart in AVR311 (fig 2-3)  clearly shows that "Receiving a stop or repeated start while still addressed as a slave"  should then disable the TWI interrupt and NOT clear the interrupt flag.   

 

Now the code for this condition in the AVR311 ISR is as follows:

 

    case TWI_SRX_STOP_RESTART:       // A STOP condition or repeated START condition has been received while still addressed as Slave    
                                                        // Enter not addressed mode and listen to address match
      TWCR = (1<<TWEN)|                                 // Enable TWI-interface and release TWI pins
             (1<<TWIE)|(1<<TWINT)|                      // Enable interrupt and clear the flag
             (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|           // Wait for new address match
             (0<<TWWC);                                 //
      
      TWI_busy = 0;  // We are waiting for a new address match, so we are not busy
      
      break;           

 

The code and the flow chart don't seem to agree.  The flow chart suggests when I receive the STOP condition from the host the AVR should hold the clock line low until the calling routines restart the I2C bus.  The code though seems to simply clear everythign and exit.  

 

Has anybody seen the Clock held low (clock stretching) by this code?  I need time between the host doing an address write and a data read in order to create and fill the buffer with the response data.    As is I have no way to hold off the host so it occasionally reads back the write data since there is only one buffer.

 

Thanks in advance for any help/comments

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

I only see an I2C master from him, but thanks for the suggestion.  Did I miss something?  Funny thing is that have done masters many times gong all the way back to the late 90's on very early AVRs.  Never had an issue.  First shot at a slave and nothing but grief!

 

 

 

 

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

I deleted my post after I realised it was not relevant (master only). Sorry for the noise!

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

lundquist wrote:

Following up on this.  I can't seem to make clock stretching work to hold off the master until I am ready with data. 

 

AVR311 clearly states:  "If a Slave device cannot handle incoming data until it has performed some other function, it can hold SCL
low to force the Master into a wait-state."

 

I'm not sure I'm following, as later you state when a stop is received....

Normally clock streatching is done in the middle of a an I2C transaction, ie. master addresses slave for a read operation and slave does not yet have the data ready to place on the bus, at that point it stretches the clock while it fetches the data needed.   Not after the stop has been received.....

 

In the few slave I2C operations, my slave would fetch the latest data before the master asked for it, and have in a buffer ready to send when the master wanted it.

The slave, temp sensor, O2 ratio sensor, etc. would always be working to have the latest data available, and handle the comms with master as needed rather when wait for the call and rush to get the next data point.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Hi Jim,

 

For my application the the last thing the host sends me is a byte indicating what data it wants.  It then immediately sends me a byte read command and expects the data to be ready.  

 

At 100KHz the only thing between the ID byte and the need for the data is the slave address byte for the read.  That is only about 100uSec.  

 

If I did the the reply data formatting in the ISR I could probably hit this but the reference app code is setup assuming you fill the buffer in the calling non ISR code.   At the moment I do that in a round robin background loop so I don't always hit 10uS and thus I get errors.   I just need some way to let the host know it has to wait for me to fill the buffer.  

 

The app note flow chart implies this is done in the ISR by not clearing the interrupt flag.   

 

My understanding based on what I have been reading today is that the slave can hold off the master by holding the clock low during any low phase of the clock.  So I could even do this in the middle of the the address byte of the read.  Or even the ack of that byte?   The TWI though is byte oriented so I think it would be on the first clock of any byte.  ???  Still working this all out.

 

Still looking for somebody else to confirm that clock stretching doesn't work on this code. 

 

Dave

 

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

lundquist wrote:
The app note flow chart implies this is done in the ISR by not clearing the interrupt flag. 

It's been awhile and with a previous employer so I don't have access to the code, but it seems like you said, leave the interrupt flag set, disabled interrupts, exit the ISR(), get the data and reenable interrupts and the ISR() picks up where it left off and completes the transfer.    YMMV!

 

Good luck.

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Thanks!  

 

Against all my general tendencies I thought this time, no I won't code this myself.  It's 2020 after all, there has to be an off the shelf driver that works.  Right?  

 

The requirements for what I want to do are pretty simple.   I am going to do my own simple interrupt driven driver in the next day or two.  Will advise.  Appreciate the input.

 

Dave

 

 

 

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

Never used it myself but there is  "AVR 302 I2C Slave Implementation" in assembler if you want to have a look.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Took a peek at that one and unfortunately it is a bit banged implementation.  I've used bit banged Masters before but in this case I don't think this will fly.  My host needs to run the clock pretty fast since I am sharing  the bus with a another part that has some fairly high throughput requirements.   I also have some other time critical high sample rate ADC stuff going here on the ATmega324.  Somewhat DSPish multi channel Analog sensor conditioning.   

 

At the moment my plan is to try and "fix" the AVR311 code without starting from scratch.  It seems as though if I make the code follow the flow chart in the app note it will do what I want.   

 

Thanks for all the ideas/suggestions.  Will report back on my progress.

 

Dave

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

A Slave's normal strategy is to "always have the answer that the Master wants".

 

For example,   a Slave that returns the temperature will measure routinely.    And can always report the recent result whenever requested.

 

If the desired Slave behaviour is "sleep until you are asked to start a measurement",   the Master issues the "start measuring" request.    Then gets on with other tasks.

 

After a suitable time for completion,  the Master issues a "return the result".

If anally retentive,   the Master could always constantly issue an "are we there yet?" request.

 

IMHO,  the App Notes offer unlikely Slave behaviour.

But you can design your logic and adapt the App code to suit a real life situation.

 

It is going to be much the same for SPI, UART, I2C, ...

In fact it is about the same as using a local peripheral in the first place i.e. start,  check for completion,  read the result.    The only difference is the I2C / SPI / RS485 / ... protocol for transporting the request or reply.

 

David.

 

p.s. the language is not relevant.  Writing or reading an SFR or polling an SFR bit is exactly the same in ASM, C, C++, ...

You can translate a C statement directly into ASM.    (or just steal the Compiler generated ASM)

Last Edited: Sat. Jun 20, 2020 - 03:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"always have the answer that the Master wants".

HUHH the electronic version of "Yes prime minister" !

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Agreed. But, two points:

 

I don't know what the question is until the last byte of preceding write command.  The host just gives me a simple single byte register address and then immediately issues a read command for that register data.  I basically have 1 byte time or 100uS or I miss filling the buffer.   Most of the time I make it, but I have other interrupts running too and occasionally I don't.  The host then reads back the register address it wrote instead of the desired data.   

 

I guess I could implement higher level protocol to manage this with busy polling etc, but 1) that adds a lot of complexity and 2) this clock stretching stuff is designed specifically to address this exact issue.  I don't think I am being unreasonable in expecting it to work.  Even the AVR311 app note says:

 

"If a Slave device cannot handle incoming data until it has performed some other function, it can hold SCL
low to force the Master into a wait-state."

 

They show it in the flow chart and then don't implement it in the code.   

 

I have had some luck modifying the code to match the flow chart and it looks to be working, mostly.  FYI i am using a Digilent Analog Discovery 2 as a debug I2C host.  Very useful tool as it can do I2C scripting.  The only gap is that it's logic analyzer function cannot operate at the same time as the protocol simulator so I am not hooking up a Saleae Logic 8 to do the analysis.

 

Hey, at least I am learning something! :(. 

 

Dave

 

 

 

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


Minor update.  The ATmega324 does not seem to like to be clock stretched at the start of of an address byte.  If my master timing is such that I don't need to clock stretch everything works perfectly.  If I pull in the read back so that I get any clock stretching, the address byte for the read transaction immediately after the clock stretch is not decoded properly and not ACK'd.  Interestingly my address+R byte is 0xB1 (10110001) and if I look in the TWDR after a failed address (No ACK) I see 0x63 (01100011).  Sort of looks liek a rotate by 1 bit of the correct data.    The Saleae though think it is a perfectly good address byte.   

 

 

My AVRINT# line shows pulses on any TWI interrrupt and I never get one under these circumstances.  

 

Here is a normal read with no preceding clock stretch.

 

 

I do see a few threads with some mention of missed address ACKs.  Need to do some digging.  Suggestions welcome.

 

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

Please post a typical Master sequence.

 

What sort of data are you returning ?  e.g. temperature, pressure, voltage, ...

Does your Slave run on batteries ?  e.g. sleep 99.9% of the day.

 

The mega324PB should have the same TWI peripheral as 324, 324PA, ..., 328P, 328PB,  ..., 2560, ...

 

Most I2C hardware chips can always reply immediately when they know which register has been requested.

e.g. RTC,  accelerometer, ...

 

No respectable Master is going to request a "slow" operation and expect an instant result.

 

David.

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

My sequence is as follows:

 

First a 2 byte Message from the Master 

 

       {SLave Addr + W}, {Virtual Register Address}

       0xB0, 0X02 is one example I am testing

 

The Master then immediately turns around and reads the data at the virtual register address

 

        {Slave Add + R}, {Read value for Reg 2}

 

The host is a fast SAM4S so these 4 bytes are back to back at 100KHz clock rate.  The data is a collection of 8 detect flags from some analog processing I do interrupt driven at 15KHz AtoD conversion rate samples of a bank of IR position sensors.  I run off batteries but a pretty big one.  Power is not a big concern.

 

The hardware cannot know what value to reply with.  Some firmware has to see the register address and then load the appropriate data in the buffer.  The AVR311 approach is that this decode is done in the background.  

 

One solution would be to put that code in the ISR.  But even that doesn't guarantee good results.  The AtoD interrupt I run is a higher priority.  That ISR may prevent me from from filling the buffer in time.  I suspect though it would also work as that ISR is less 40uS.  It just isn't the way the whole AVR311 is intended to operate though.  It clearly assumes responses are done outside the ISR and when ready the TWI is restarted.   If I were starting from scratch I would probably do this with the full response buffer fill in the ISR. 

 

But I did manage to get things working!  As reported putting a clock stretch ahead of the slave Read address seems to fail.  I moved it AFTER the slave read address is received.   I put it In the ISR that processes the slave address read ack, I then clock stretch if the data the host needs is not ready.  Same technique as before but just moved one byte later.  It should work better there anyway since it will only do the stretch if the slave is not ready at the last possible moment.

 

Will provide more details.  Kind of burned out here, but it is working.

 

My clear conclusion is that the AVR311 code is fundamentally flawed.  I suspect it may work in some cases but it is in now way capable of doing clock stretching  

 

Thanks.