View previous topic :: View next topic |
Author |
Message |
emiel
Joined: 18 Jul 2007 Posts: 14
|
Posted: Tue Dec 24, 2019 2:37 pm Post subject: I2C slave unstable / how to recover master |
|
|
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 |
|
|
MWS
Joined: 22 Aug 2009 Posts: 2262
|
Posted: Tue Dec 24, 2019 6:30 pm Post subject: Re: I2C slave unstable / how to recover master |
|
|
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 |
|
|
emiel
Joined: 18 Jul 2007 Posts: 14
|
Posted: Wed Dec 25, 2019 1:21 pm Post subject: |
|
|
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
Thanks in advance |
|
Back to top |
|
|
MWS
Joined: 22 Aug 2009 Posts: 2262
|
Posted: Wed Dec 25, 2019 6:51 pm Post subject: |
|
|
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 |
|
|
emiel
Joined: 18 Jul 2007 Posts: 14
|
Posted: Fri Dec 27, 2019 4:06 pm Post subject: |
|
|
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 (, 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 |
|
|
MWS
Joined: 22 Aug 2009 Posts: 2262
|
Posted: Fri Dec 27, 2019 5:32 pm Post subject: |
|
|
emiel wrote: | Any further suggestions on this matter? | Ones you decide to ignore or not to ignore? |
|
Back to top |
|
|
emiel
Joined: 18 Jul 2007 Posts: 14
|
Posted: Fri Dec 27, 2019 6:36 pm Post subject: |
|
|
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 |
|
|
MWS
Joined: 22 Aug 2009 Posts: 2262
|
Posted: Fri Dec 27, 2019 7:24 pm Post subject: |
|
|
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 |
|
|
six1
Joined: 27 Feb 2009 Posts: 553
|
Posted: Sat Dec 28, 2019 8:18 am Post subject: |
|
|
@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 |
|
|
albertsm
Joined: 09 Apr 2004 Posts: 5913 Location: Holland
|
Posted: Sat Dec 28, 2019 1:37 pm Post subject: |
|
|
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 |
|
|
emiel
Joined: 18 Jul 2007 Posts: 14
|
Posted: Fri Jan 03, 2020 12:22 pm Post subject: |
|
|
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 ) |
|
Back to top |
|
|
albertsm
Joined: 09 Apr 2004 Posts: 5913 Location: Holland
|
Posted: Wed Jan 08, 2020 2:27 pm Post subject: |
|
|
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 |
|
|
emiel
Joined: 18 Jul 2007 Posts: 14
|
Posted: Mon Jan 27, 2020 11:45 pm Post subject: |
|
|
Hello All, Mark,
I had some trouble seeing how I could further strip the slave program beyond the set/reset ports and a simple loop as shown above. After way too many hours of debugging including using the logic analyzer and building all kinds of trigger capture devices with another device, I decided to stop this direction and go for another solution.
Fortunately I quickly was up to steam enjoying Bascom as I used too!
Thanks to all for your suggestions and apology for late respons.
Kind regards,
emiel |
|
Back to top |
|
|
albertsm
Joined: 09 Apr 2004 Posts: 5913 Location: Holland
|
Posted: Tue Jan 28, 2020 2:35 pm Post subject: |
|
|
i know you solved it , but to answer the question :
i guess you missed the question where i asked for the code for the master. these kind of problems can only be checked when you know what is going on at both sides.
stripping the slave : get rid of all code till the problem goes away. that means the pin toggling. there is even a change that the toggling caused some electric stress. but when you used a scope (not a logic analyzer) you would have found this.
good luck with the project. _________________ Mark |
|
Back to top |
|
|
|
|
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
|
|