Forum - MCS Electronics

 

FAQFAQ SearchSearch RegisterRegister Log inLog in

I2C slave unstable / how to recover master

 
Post new topic   Reply to topic    www.mcselec.com Forum Index -> BASCOM-AVR
View previous topic :: View next topic  
Author Message
emiel

Bascom Member



Joined: 18 Jul 2007
Posts: 13

blank.gif
PostPosted: Tue Dec 24, 2019 2:37 pm    Post subject: I2C slave unstable / how to recover master Reply with quote

Hello,

With the bascom I2C slave lib, I build a slave module that remotely reads out some buttons & 1Wire devices. It works fine but within some hours master or slave or both hang.

Debugging with a logic analyzer & some trigger logic learned some interesting things:

1) the slave gets into error when processing I2C instructions from master, directed to slave while in parallel (in main) reading 1W devices
2) as the slave gets corrupted, also the master hangs (typically pulls SDA low) and can only be recovered by a reset.

So it starts with 1), why do I2C routines get stuck when running 1W in main?

When zooming into this I found someting remarkable (at least to me): when there's I2C trafic, either adressed to slave or to other devices, the main program seems to get impacted.

See screenshot attached. A simple on/ off setting of ports with 'waitms' in between becomes messy when in parallel there's I2C trafic. Since the I2C has prio via interrupts, I would expect the wait time to increase, but certainly not decrease? Could this indicate some issues with how the registers are saved during the interrupts/ I2C handling or an issues with timer conflicts? And as such, could this cause also issues 'back to the I2C algorithmes' since in end they fail as well? (PS: I could not get clear which timer is used by the I2C slave routines, it doesn't matter if I use timer0 or timer1 for the main prog; both seem to 'work'?)

Any suggestions?

Other questions is if anyone has any suggestion how the I2C at the master can be recovered without reset. I would imaging that a HW reset should be able to be mimic'ed in software?

Thanks, Emiel


Code:

$regfile = "attiny85.dat"

$crystal = 8000000
$hwstack = 44
$swstack = 32
$framesize = 32

....

But_port_L alias portB.4
But_port_R alias portB.3

...
'Config Timer0 = Timer , Prescale = 1024
Config Timer1 = Timer , Prescale = 1024

config usi = twislave , address = I2C address  

...

do

...

   for DBm = 1 to DB

      set but_port_L
      waitms 1
      reset but_port_L
      waitms 1

   next
   waitms 10

Loop

 


(BASCOM-AVR version : 2.0.8.2 )
Back to top
View user's profile
MWS

Bascom Member



Joined: 22 Aug 2009
Posts: 1743

blank.gif
PostPosted: Tue Dec 24, 2019 6:30 pm    Post subject: Re: I2C slave unstable / how to recover master Reply with quote

emiel wrote:
I would expect the wait time to increase, but certainly not decrease?
This hints controller resets, place a distinct pin toggle sequence before do/loop, this allows to observe resets.
Quote:
(PS: I could not get clear which timer is used by the I2C slave routines, it doesn't matter if I use timer0 or timer1 for the main prog; both seem to 'work'?)
Which means you can not read, in the help:
Quote:
The library was written for TIMER0 and INT0.
While the interrupt can be specified, you need to change the library code when you use a non-default interrupt. For example when you like to use INT1 instead of the default INT0.

Beside that, your code chunk does not allow to check whether your code is correct at all, further it's possible you have a hardware issue and therefor the resets occur.
Hardware issue can be anything, transients, improper wiring of slave, missing blocking capacitors, a.s.o.
Back to top
View user's profile
emiel

Bascom Member



Joined: 18 Jul 2007
Posts: 13

blank.gif
PostPosted: Wed Dec 25, 2019 1:21 pm    Post subject: Reply with quote

Thanks for quick response.

Wrt the interrupts: I indeed apparently focused only on the 'config USI' help, where I could not find it. So thanks for the hint.

On the main subject: I'm sure the processor does NOT reset. If so, the DB is initialized at 1 and should only show 1 set/ reset. Later in the program the DB is set for debugging at the various interrupt calls. This does not happen since at those calls I also set/ reset another port to monitor with the logic analyzer so that I can see if the routines are called. And since the slave is not addressed, they're also not called.

I now attached the full code, pls don't mind the debugging stuff as it's not cleaned-up yet, but maybe you can find already some strange things I do Wink

Thanks in advance
Back to top
View user's profile
MWS

Bascom Member



Joined: 22 Aug 2009
Posts: 1743

blank.gif
PostPosted: Wed Dec 25, 2019 6:51 pm    Post subject: Reply with quote

emiel wrote:
I'm sure the processor does NOT reset.
If you think to know the answer in front and if you are not willing to follow an advice, why do you even pose a question?
You understood correctly that:
Quote:
I would expect the wait time to increase, but certainly not decrease?

A delay is nothing but a loop in which a few registers are counted down, the countdown can be interrupted, then it lasts longer.
It can not made shorter, other than its counter registers would be trashed by an interrupt, while on the other hand each interrupt saves important registers, including these counter registers.
So no, under the presumption there is no bug in the interrupting code (in the lib, which I can not test), there is no way the delay becomes shorter.
Another way we would see shorter pulses would be, if the pin is altered somewhere else the code, according your code this is not the case.
Random resets may look like that and even recovering stack overflows (without reset and very, very seldom) would be able to show this behavior.

As you don't like my explanation, what is yours, why pulses on pin B.4 show up to be shorter than 1ms?

Other I saw from your code that as well I2C can request data anytime, it may be also in midst of data transfer to Data_send(), therefor you would need either to lock Update_output from interrupts - which is not a good idea - or you need to find another way to make sure not only parts of data are valid. As a data request can occur anytime, it may be also in midst of an 1wire read. Not sure how many times 1wire-devices lockup, but I can't see any protection against.

I would kick out the 1wire request and let it run for a while without, if it locks not up then, I'd know the culprit and could look up further in detail.

Disastrous for I2c-slave functionality would be if interrupts get disabled by a CLI by a locked up routine, however _1wire routines in mcs.lib did not show any use of CLI. You can upload your hex, I can look up for CLI's then.

For a random shot you can also increase stack and frame, looks you have plenty of SRam left, the ATiny85 sports 256, starting at &h0060.
If you are not sure how much to increase, open the report after compilation (ctrl-W) and look under 'Space left:'.
Back to top
View user's profile
emiel

Bascom Member



Joined: 18 Jul 2007
Posts: 13

blank.gif
PostPosted: Fri Dec 27, 2019 4:06 pm    Post subject: Reply with quote

Hello,

I had the exact same interpretation of the wait function so that's why I assumed this should be understood first/ as well (and could be part of the root cause of the instability).

Increasing the stack did not help. Next I completely stripped the program to the bare minimum, but the wait is still messed up when there's I2C traffic. So only thing I now can think of is something going wrong in the I2C slave routines?

In screenshot of the logic analyzer you see instances of toggling the output ports. It's ok when no I2C traffic and gets corrupted when there's traffic.

Any further suggestions on this matter?






Code:

$regfile = "attiny85.dat"

$crystal = 8000000
'$hwstack = 44
'$swstack = 32
'$framesize = 32

$hwstack = 64
$swstack = 64
$framesize = 64

Const prog_clock = 178                                      '8000000/1024/100=78; 256-78=178--> get 10ms program clock (100Hz)
Const I2C address = &H50

But_port_L alias portB.4
But_port_R alias portB.3
config But_port_L = output
config But_port_R = output

config usi = twislave , address = I2C address

dim DB , DBi , DBm , DBmain as byte
DBmain = 11

enable interrupts                                           'it is important you enable interrupts

Do

   for DBm = 1 to DBmain

      set but_port_L
      waitms 1
      reset but_port_L
      waitms 1

   next
   waitms 10

Loop

end


'Timer_interrupt:

'   set but_port_R

'   if But_counter > 0 then incr But_counter
'   if 1W_wait_timer > 0 then decr 1W_wait_timer
'   set Update_data

'   Timer1 = prog_clock

'   reset but_port_R                                         '

'return




'The following labels are called from the library. You need to insert code in these subroutines
'Notice that the PRINT commands are remarked.
'You can unmark them and see what happens, but it will increase code size
'The idea is that you write your code in the called labels. And this code must execute in as little time
'as possible. So when you slave must read the A/D converter, you can best do it in the main program
'then the data is available when the master requires it, and you do not need to do the conversion which cost time.


'A master can send or receive bytes.
'A master protocol can also send some bytes, then receive some
'The master and slave address must match.

'the following labels are called from the library when master send stop or start

Twi_start_received:

 'set CT_start_received
 'print "Master sent start or repeated start"

 DB = 2
 set but_port_R
 reset but_port_R
 set but_port_R
 reset but_port_R

Return

Twi_stop_received:

   'set CT_stop_received
   'print S = "Master sent stop"

 DB = 3
 set but_port_R
 reset but_port_R
 set but_port_R
 reset but_port_R
 set but_port_R
 reset but_port_R

Return

'master sent our slave address and will now send data
Twi_addressed_goread:

   'set CT_addressed_goread
   'Print "We were addressed and master will send data"

     DB = 4
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R

Return


Twi_addressed_gowrite:

   'set CT_addressed_gowrite
   'Print "We were addressed and master will read data"

     DB = 5
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R

Return

'this label is called when the master sends data and the slave has received the byte
'the variable TWI holds the received value
Twi_gotdata:

   'set CT_gotdata

     DB = 6
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R

   'Print "received : " ; hex(Twi) ; " byte no : " ; Twi_btw
   'if usidr = 0 then print "usidr=0" else print "usidr: =1"

   'Data_received(Twi_btw) = twi
   'If twi_btw = 2 then set CT_got_last_data


   'Expect 2 bytes: 1 data and 1 crc
   'Data byte: bit 0,1: request button (0), request temp1 (1), request temp2 (2),request all (3)
   'Data byte: bit 2,3: temp reading update freq default (0), high (4), medium (Cool, low (12)
   'Data byte: bit 5: temp reading format 0.1 res/ offset -10 (0) or 0.2 res/ offset 0 (16)


Return

'this label is called when the master receives data and needs a byte
'the variable twi_btr is a byte variable that holds the index of the needed byte
'so when sending multiple bytes from an array, twi_btr can be used for the index
Twi_master_needs_byte:

   'set CT_master_needs_byte

     DB = 7
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R
     set but_port_R
     reset but_port_R

   'Print "Master needs byte : " ; Twi_btr;

Return

'TWI_STOP_RSTART_RECEIVED:
Twi_stop_rstart_received:

Return
Back to top
View user's profile
MWS

Bascom Member



Joined: 22 Aug 2009
Posts: 1743

blank.gif
PostPosted: Fri Dec 27, 2019 5:32 pm    Post subject: Reply with quote

emiel wrote:
Any further suggestions on this matter?
Ones you decide to ignore or not to ignore?
Back to top
View user's profile
emiel

Bascom Member



Joined: 18 Jul 2007
Posts: 13

blank.gif
PostPosted: Fri Dec 27, 2019 6:36 pm    Post subject: Reply with quote

Dear MWS,

If it's about not uploading the hex file: somehow the attachement upload does not accept the hex file (?) so that's why I copied the bas file, so that you maybe can compile yourself
If it's about removing the 1W: I started with removing the 1W code, but no change. Then I removed ALL code around it, but problem still persists
If it's about increasing the stack: I tried as you can see.

What did I miss from your suggestions what still can be a reasonable option?

(Furthermore: I apologize if I in any case offend you by not doing literlly what you say, but think along your lines and try to move on. If that does not work for you, pls further ignore this topic. I appreciate your support, but NOT by your offensive responses )
Back to top
View user's profile
MWS

Bascom Member



Joined: 22 Aug 2009
Posts: 1743

blank.gif
PostPosted: Fri Dec 27, 2019 7:24 pm    Post subject: Reply with quote

emiel wrote:
NOT by your offensive responses

If you think my response was offensive, you a) don't know what 'offensive' really means and b) I will stop to respond to your questions.
Good luck.
Back to top
View user's profile
six1

Bascom Expert



Joined: 27 Feb 2009
Posts: 540

germany.gif
PostPosted: Sat Dec 28, 2019 8:18 am    Post subject: Reply with quote

@emiel

try inserting PUSHALL and POPALL in each sub like this:

Code:

Twi_start_received:

 'set CT_start_received
 'print "Master sent start or repeated start"
pushall
 DB = 2
 set but_port_R
 reset but_port_R
 set but_port_R
 reset but_port_R
popall
Return
 

_________________
For technical reasons, the signature is on the back of this message.
Back to top
View user's profile
albertsm

Administrator



Joined: 09 Apr 2004
Posts: 4858
Location: Holland

blank.gif
PostPosted: Sat Dec 28, 2019 1:37 pm    Post subject: Reply with quote

first of all i like to point out that there are different slave libs, all for different processors.
- the first one was the soft i2c slave, this one does use INT0 and TIMER0
- then there is one for processors with hardware TWI. This is the best one since it handles almost everything in hardware.
- then there was the USI one. THis is some clever serial hardware with multiple purposes. it can be used as a slave. but as you noticed, there are some downsides.
so you are using the USI slave.

This does NOT use any timer or other interrupt, only the USI interrupt.
The downside is that this is some counter logic that keeps firing interrupts because there is not much intelligence in the hardware.
What i mean is that TWI once addressed, does not bother with other addressed slaves. it goes smooth from one state to the other.
But USI keeps on working and thus reacting on all the bus traffic.

like all interrupt code, code should be short and not take a long time to execute.
using i2c, there is not a real problem if the int. takes some more time but when other code is running, it depends on that code if you can delay it.
for this reason, the slave should just return data or receive data. this takes little time.

1wire is very time critical. so this should be executed without the slave interrupting the 1wire code.
you could disable interrupts when performing 1wire code.

the pushall/popall is already inside the USI SLAVE so there is no need for this.

_________________
Mark
Back to top
View user's profile Visit poster's website
emiel

Bascom Member



Joined: 18 Jul 2007
Posts: 13

blank.gif
PostPosted: Fri Jan 03, 2020 12:22 pm    Post subject: Reply with quote

Thanks for the the replies,

Since the attiny85 has a USI interface, I assumed I needed to use the USI slave lib. Would the TWI lib work as well on this device?

Wrt disturbing the 1wire communication : this indeed happens but by checking the crc or just ignore when an interrupt has occured, I can deal with it. The other way around: to disable the interrupts will lead to disturb the I2C communication which is more complicated to handle/ recover.


Actually this was my original problem: somehow the I2C slave routines get disturbed when in parallel running the 1W in the main loop, where-as you would expect the oppposite (interrupt driven routines ok; main prog disturbed). So that's when I start debugging with the logic analyser with the results above.

And I still have no clue how in the fully stripped prog above (only some set ports high and low plus some waitms statements in the main loop) and just listening to the I2C traffic, the main prog can be impacted in such that the wait loops get incidentally shorter (!) ? Only thing I can imagine is that either the USI lib does not correctly save the registers by push/ pop and hence overrides the wait counters OR I don't understand how the wait loops work... ?

(maybe this is in the end not related to my initial issue, but it seems so basic that I first want to get this solved Wink)
Back to top
View user's profile
albertsm

Administrator



Joined: 09 Apr 2004
Posts: 4858
Location: Holland

blank.gif
PostPosted: Wed Jan 08, 2020 2:27 pm    Post subject: Reply with quote

since the processor you selected does not have TWI, you can not use the TWI slave.

the slave saves all used registers.
but as always with isr you need to take care that the main code and the ISR code are not working on the same variables.
what you describe seems more like a trashed register. regs used by the USI int are however saved especial the ones used by waitms.

a downside of USI is that it is very simple and interrupt fires for all data, even when not for the USI slave.

on the other hand i2c can work at low frequencies so while some code like 1wire can be goofed up, the master should not be affected.

you should reduce the code in your sample and also post the used master code.

_________________
Mark
Back to top
View user's profile Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    www.mcselec.com Forum Index -> BASCOM-AVR All times are GMT + 1 Hour
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You cannot download files in this forum