Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 05:01 07 Jul 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : (uM) Possible I2C Bug with 4.5C

Author Message
redrok

Senior Member

Joined: 15/09/2014
Location: United States
Posts: 209
Posted: 06:30pm 12 Oct 2014
Copy link to clipboard 
Print this post

Hi All;

I'm having problems transferring data from the Master to a Slave uMite.

I have setup 2 programs, one for the Master the other for the Slave.

As far as I can tell, only a few characters can be sent before a kind
of reset occurs.

1. This has the effect of choking on the first few characters.
2. Then usually the rest will be sent.
3. Lastly, a null string is reported.

The best condition, (Least errors), is when:
1. I2C speed is 400KHz.
2. The Slave is running at 5MHz, (Master doesn't mater).
Any faster and choking starts to happen.
3. But that pesky null string still happens.

I've been trying to understand the problem for 2 weeks and have
come to the conclusion that I'm an idiot or there's some kind of bug.

Redrok

BTW, after first loading each program do a "Goto InitializeVars".
This stores some Vars which get loaded at startup.

Just run them and the slave will send the Partial Strings to the terminal.


' ------------------------------------------------------------ -------------------
' MMTstMasterStringLcd.bas
' Goto InitializeVars ' Do this once to Initialize the Stored Vars
Var Restore
Print "CpuSpeed=";Str$(CpuSpeed,3);" I2cSpeed=";Str$(I2cSpeed,4);" Line=";Str$(SL,2);" Position=";Str$(SP,2)
Cpu CpuSpeed
SAdd=&H26
Dim Msg(84)
Pause 100

' Watchdog 5000
' Gosub SLcdClear
DoAgain:
Q$ = Inkey$ : If Q$ <> "" Then Gosub Setup
Lcdout$ = Right$("0"+Str$(SL),2) + Right$("0"+Str$(SP),2) + Lcdst$
Print "CILPSU";Str$(CpuSpeed,3);"MHz";Str$(I2cspeed,4);"KHz";Str$( SL,3);"Lin";Str$(SP,3);"Pos";Str$(Len(Lcdout$),3);"Chr ";Chr$(34);Lcdout$;Chr$(34)
Gosub SLcd
LLen = Len(Lcdst$)
Lcdst$=Right$(Lcdst$,LLen-1)+Left$(Lcdst$,1)
Pause PTime
Goto DoAgain
End

Setup:
Print "C=Cpu speed, I=I2c speed, L=Line, P=Position, S=String, U=paUse time"
If Ucase$(Q$) = "C" Then Input "Cpu Speed 5..48 "; CpuSpeed : Cpu CpuSpeed
If Ucase$(Q$) = "I" Then Input "I2cSpeed 10..400 "; I2cSpeed
If Ucase$(Q$) = "L" Then Input "LCD Line 1 .. 4 "; SL
If Ucase$(Q$) = "P" Then Input "LCD Position 1 .. 80 "; SP
If Ucase$(Q$) = "S" Then Input "Input A String "; Lcdst$ : If Len(Lcdst$) = 0 then Return
If Ucase$(Q$) = "U" Then Input "Input paUse time in mS "; PTime
Var Save CpuSpeed, I2cSpeed, SL, SP, PTime, Lcdst$
Return

' Drive The LCD
SLcd:
I2C OPEN I2cSpeed, 500
I2C WRITE SAdd, 1, Len(Lcdout$), Lcdout$
If MM.I2C Then Error "Drive LCD Cmd 4"
I2C CLOSE
Return

InitializeVars:
CpuSpeed = 5
I2cSpeed = 400
SL = 1
SP = 1
PTime = 1000
Lcdst$ = "_234567_10_234567_20_234567_30_234567_40_234567_50_234567_6 0_234567_70_234567_80"
' Lcdst$ = "_234567_10_234567_20_234567_30_234567_40"
' Lcdst$ = "_234567_10_234567_20"
' Lcdst$ = "ABCDEFGHIJ"
Var Save CpuSpeed, I2cSpeed, SL, SP, PTime, Lcdst$
End
' ------------------------------------------------------------ -------------------
' MMTstSlaveStringLcd.bas
' Goto InitializeVars ' Do this once to Initialize the Stored Vars
Var Restore
Print "CpuSpeed =";Str$(CpuSpeed,3)
Cpu CpuSpeed
SAdd=&H26
Dim Msg(100)
Lcd Init 4,5,6,7,2,3 ' LCD D4 D5 D6 D7 RS EN
I2C SLAVE OPEN &H26, 0, 1, WriteD, ReadD
DO
' WATCHDOG 10000
Q$ = Inkey$ : If Q$ <> "" Then Gosub Setup
LOOP

Setup:
Print "C=Cpu speed"
If Ucase$(Q$) = "C" Then Input "Cpu Speed 5..48 "; CpuSpeed : Cpu CpuSpeed : Var Save CpuSpeed
Return

ReadD:
I2C SLAVE READ 84, Lcdin$, Rcvd
SL = Val(Mid$(Lcdin$,1,2))
SP = Val(Mid$(Lcdin$,3,2))
If Rcvd >= 4 then Lcdst$ = Right$(Lcdin$,Rcvd-4)
Print "C ";Str$(CpuSpeed,3);"MHz ";Str$(SL,3);"Lin";Str$(SP,3);"Pos";Str$(Rcvd,3);"Chr ";Chr$(34);Lcdin$;Chr$(34);
If SL > 0 And SL <= 4 And SP > 0 Then Lcd SL, SP, Lcdst$ Else Print " Bad LCD Command"; ' Only send the LCD Legal Commands
Print
Lcdst$ = ""
IReturn

WriteD:
IReturn

InitializeVars:
CpuSpeed = 5
Var Save CpuSpeed
End
' ------------------------------------------------------------ -------------------
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6269
Posted: 06:49pm 12 Oct 2014
Copy link to clipboard 
Print this post

This might be the same problem:
http://www.thebackshed.com/forum/forum_posts.asp?TID=6599&PN =18

The I2C has a 256 byte circular buffer so any bytes received have to get read before the buffer overflows.

Jim
VK7JH
MMedit
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3282
Posted: 07:14pm 12 Oct 2014
Copy link to clipboard 
Print this post

Hmm, a couple if things.

Firstly, you do not need to do a "Goto InitializeVars" initially. Executing VAR RESTORE does not generate an error if there is nothing to restore. So you could replace "Goto InitializeVars" with "IF CpuSpeed == 0 THEN InitializeVars".

But the real problem is the I2C issue and your program makes it impossible to identify just what the problem is. It requires the tester to setup a test hardware with LCD, etc then reverse engineer your program to understand what is going on.

You need to distil it down to a dozen or less lines that demonstrates the issue without requiring unnecessary hardware. Then the question is, how does your demo differ from the demo code in the User Manual? (which does work)

As Jim has pointed out, there is one known bug. But it does not sound like your issue. To me it sounds like noise on the I2C bus. Do you have the correct pullups? Are you using a long cable? Use an oscilloscope to see what is going on. Also try it at a reasonable speed (400KHz can be difficult to get working).

Geoff
Geoff Graham - http://geoffg.net
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9589
Posted: 07:36pm 12 Oct 2014
Copy link to clipboard 
Print this post

Just my 2c, and agree with Geoff and Jim.

With respect to the 400kHz thing, it is always a good idea to run the I2C at 100kHz for a start, just so you can prove that things are working as expected - slow down your bus, and see if the problem fixes itself.

400kHz I2C does work fine, but you have to respect some rules(short as possible data and clock lines, and have a ground wire between data lines) - see page 61 of the MicroMite manual:

[quote]
When running the I2C bus atabove 150 kHzthe cabling between the devices becomes important. Ideally the cables should be as short as possible (to reduce capacitance) and also the data and clock lines should not run next to each other but have a ground wire between them (to reduce crosstalk). If the data line is not stable when the clock is high, or the clock line is jittery, the I2C peripherals can get "confused" and end up locking the bus (normally by holding the clock line low). If you do not need the higher speeds then operating at 100 kHz is the safest choice.[/quote]

Smoke makes things work. When the smoke gets out, it stops!
 
redrok

Senior Member

Joined: 15/09/2014
Location: United States
Posts: 209
Posted: 06:07am 13 Oct 2014
Copy link to clipboard 
Print this post

Hi All;

I'm having problems transferring data from the Master to a Slave uMite.

I have setup 2 programs, one for the Master the other for the Slave.

As far as I can tell, only a few characters can be sent before a kind
of reset occurs.

1. This has the effect of choking on the first few characters.
2. Then usually the rest will be sent.
3. Lastly, a null string is reported.

The best condition, (Least errors), is when:
1. I2C speed is 400KHz.
2. The Slave is running at 5MHz, (Master doesn't mater).
Any faster and choking starts to happen.
3. But that pesky null string still happens.

I've been trying to understand the problem for 2 weeks and have
come to the conclusion that I'm an idiot or there's some kind of bug.

Redrok

BTW, after first loading each program do a "Goto InitializeVars".
This stores some Vars which get loaded at startup.

' ------------------------------------------------------------ -------------------
' MMTstMasterStringLcd.bas
' Goto InitializeVars ' Do this once to Initialize the Stored Vars
Var Restore
Print "CpuSpeed=";Str$(CpuSpeed,3);" I2cSpeed=";Str$(I2cSpeed,4);" Line=";Str$(SL,2);" Position=";Str$(SP,2)
Cpu CpuSpeed
SAdd=&H26
Dim Msg(84)
Pause 100

' Watchdog 5000
' Gosub SLcdClear
DoAgain:
Q$ = Inkey$ : If Q$ <> "" Then Gosub Setup
Lcdout$ = Right$("0"+Str$(SL),2) + Right$("0"+Str$(SP),2) + Lcdst$
Print "CILPSU";Str$(CpuSpeed,3);"MHz";Str$(I2cspeed,4);"KHz";Str$( SL,3);"Lin";Str$(SP,3);"Pos";Str$(Len(Lcdout$),3);"Chr ";Chr$(34);Lcdout$;Chr$(34)
Gosub SLcd
LLen = Len(Lcdst$)
Lcdst$=Right$(Lcdst$,LLen-1)+Left$(Lcdst$,1)
Pause PTime
Goto DoAgain
End

Setup:
Print "C=Cpu speed, I=I2c speed, L=Line, P=Position, S=String, U=paUse time"
If Ucase$(Q$) = "C" Then Input "Cpu Speed 5..48 "; CpuSpeed : Cpu CpuSpeed
If Ucase$(Q$) = "I" Then Input "I2cSpeed 10..400 "; I2cSpeed
If Ucase$(Q$) = "L" Then Input "LCD Line 1 .. 4 "; SL
If Ucase$(Q$) = "P" Then Input "LCD Position 1 .. 80 "; SP
If Ucase$(Q$) = "S" Then Input "Input A String "; Lcdst$ : If Len(Lcdst$) = 0 then Return
If Ucase$(Q$) = "U" Then Input "Input paUse time in mS "; PTime
Var Save CpuSpeed, I2cSpeed, SL, SP, PTime, Lcdst$
Return

' Drive The LCD
SLcd:
I2C OPEN I2cSpeed, 500
I2C WRITE SAdd, 1, Len(Lcdout$), Lcdout$
If MM.I2C Then Error "Drive LCD Cmd 4"
I2C CLOSE
Return

InitializeVars:
CpuSpeed = 5
I2cSpeed = 400
SL = 1
SP = 1
PTime = 1000
Lcdst$ = "_234567_10_234567_20_234567_30_234567_40_234567_50_234567_6 0_234567_70_234567_80"
' Lcdst$ = "_234567_10_234567_20_234567_30_234567_40"
' Lcdst$ = "_234567_10_234567_20"
' Lcdst$ = "ABCDEFGHIJ"
Var Save CpuSpeed, I2cSpeed, SL, SP, PTime, Lcdst$
End
' ------------------------------------------------------------ -------------------
' MMTstSlaveStringLcd.bas
' Goto InitializeVars ' Do this once to Initialize the Stored Vars
Var Restore
Print "CpuSpeed =";Str$(CpuSpeed,3)
Cpu CpuSpeed
SAdd=&H26
Dim Msg(100)
Lcd Init 4,5,6,7,2,3 ' LCD D4 D5 D6 D7 RS EN
I2C SLAVE OPEN &H26, 0, 1, WriteD, ReadD
DO
' WATCHDOG 10000
Q$ = Inkey$ : If Q$ <> "" Then Gosub Setup
LOOP

Setup:
Print "C=Cpu speed"
If Ucase$(Q$) = "C" Then Input "Cpu Speed 5..48 "; CpuSpeed : Cpu CpuSpeed : Var Save CpuSpeed
Return

ReadD:
I2C SLAVE READ 84, Lcdin$, Rcvd
SL = Val(Mid$(Lcdin$,1,2))
SP = Val(Mid$(Lcdin$,3,2))
If Rcvd >= 4 then Lcdst$ = Right$(Lcdin$,Rcvd-4)
Print "C ";Str$(CpuSpeed,3);"MHz ";Str$(SL,3);"Lin";Str$(SP,3);"Pos";Str$(Rcvd,3);"Chr ";Chr$(34);Lcdin$;Chr$(34);
If SL > 0 And SL <= 4 And SP > 0 Then Lcd SL, SP, Lcdst$ Else Print " Bad LCD Command"; ' Only send the LCD Legal Commands
Print
Lcdst$ = ""
IReturn

WriteD:
IReturn

InitializeVars:
CpuSpeed = 5
Var Save CpuSpeed
End
' ------------------------------------------------------------ -------------------

  Geoffg said   Hmm, a couple if things.

Firstly, you do not need to do a "Goto InitializeVars" initially. Executing VAR RESTORE does not generate an error if there is nothing to restore. So you could replace "Goto InitializeVars" with "IF CpuSpeed == 0 THEN InitializeVars".

Of course. I just wanted to be complete so others could reproduce the problem.
As with all the "Saved Vars" one really only has to do them once, or at least only when they are changed. In this "Testing" phase I needed to be able to quickly change them, more or less, on the fly.
  Quote  But the real problem is the I2C issue and your program makes it impossible to identify just what the problem is. It requires the tester to setup a test hardware with LCD, etc then reverse engineer your program to understand what is going on.

Actually, the LCD part is not failing at all, except when passed bad parameters.

Just comment out, in the Slave Program, Lines 8, 9, and 26, the 3 lines that operate the LCD.
All data that would be sent to the LCD is echoed to the console so I can see what is going on.

I'm running TeraTerm with a single RS-232 to USB interface. I added a DPDT switch in the RS-232 lines so I could easily switch between the Master & Slave consoles. This works well as I can quickly program each uMite and monitor their consoles.

I formatted each uMite output to the consoles so both Master & Slave data line up. This way it is easy to compare the two outputs.
  Quote  You need to distil it down to a dozen or less lines that demonstrates the issue without requiring unnecessary hardware.

Ok, The problem is almost certainly in the Slave lines 20..29. This is where the I2C bytes are being caught from the bus.

I'm using an I2C sniffer, that cool tiny scope by Gabotronics called the XProtoLab to monitor the I2C bus. Every byte the Master sends is correct in every detail. Therefore, I think the problem must be in the Slave interrupt routine.

A bit more about operating the programs:
1. Once running the Master and Slave each operate on a simple single Inkey$ interface.
2. The Master commands are "CILPSU" C=Cpuspeed I=I2cspeed L=lcdLine P=lcdPosition S=change the String U=paUse to change the rate strings packets are sent.
3. The Slave commands are "C " C=Cpuspeed.
4. Once a command changes a parameter it is saved using Var Save. Each time a reset occurs the Saved Vars are restored.
This makes it convenient to run through basic operating parameters.
  Quote  Then the question is, how does your demo differ from the demo code in the User Manual? (which does work)

I think I copied everything correctly. In the beginning I had tried many things just to get it going and settled on the Slave running at 5MHz and and the I2C at 400KHz.
  Quote  As Jim has pointed out, there is one known bug. But it does not sound like your issue. To me it sounds like noise on the I2C bus. Do you have the correct pullups? Are you using a long cable?

The pullups are 1KOhm to increase the rise times, cable length is about 12", and another 18" stub to the Sniffer.
  Quote  Use an oscilloscope to see what is going on.

My good scope shows clean transitions and nearly zero cross talk.
  Quote  Also try it at a reasonable speed (400KHz can be difficult to get working).

That's the unusual problem!!!
Here is my Theory
Something happens when a packet is sent.
Apparently there is a limited time, or number of clock cycles, before the I2C chokes.
The faster the I2C runs more of the packet is accepted before it chokes.
The slower the uMite runs more of the packet is accepted before it chokes.
Here are a few examples:

5MHz 400KHz 8600 Clocks:
C 5MHz 1Lin 1Pos 84Chr "0101_234567_60_234567_70_234567_80_234567_10_234567_20_2345 67_30_234567_40_234567_50"
C 5MHz 0Lin 0Pos 0Chr "" Bad LCD Command

5MHz 200KHz 10600 Clocks:
C 5MHz 1Lin 1Pos 51Chr "0101234567_20_234567_30_234567_40_234567_50_234567_"
C 5MHz 60Lin 0Pos 33Chr "60_234567_70_234567_80_234567_10_" Bad LCD Command

5MHz 100KHz 13600 Clocks:
C 5MHz 1Lin 1Pos 32Chr "0101234567_40_234567_50_234567_6"
C 5MHz 0Lin 23Pos 52Chr "0_234567_70_234567_80_234567_10_234567_20_234567_30_" Bad LCD Command

5MHz 20KHz 14000 Clocks:
C 5MHz 1Lin 1Pos 5Chr "01012"
C 5MHz 34Lin 56Pos 69Chr "34567_70_234567_80_234567_10_234567_20_234567_30_234567_40_ 234567_50_" Bad LCD Command
C 5MHz 23Lin 45Pos 10Chr "234567_60_" Bad LCD Command

5MHz 10KHz 20000 Clocks:
C 5MHz 1Lin 0Pos 3Chr "010" Bad LCD Command
C 5MHz 14Lin 56Pos 29Chr "14567_40_234567_50_234567_60_" Bad LCD Command
C 5MHz 23Lin 45Pos 38Chr "234567_70_234567_80_234567_10_234567_2" Bad LCD Command
C 5MHz 0Lin 23Pos 14Chr "0_234567_30_23" Bad LCD Command

10MHz 400KHz 6200 Clocks:
C 10MHz 1Lin 1Pos 29Chr "0101_10_234567_20_234567_30_2"
C 10MHz 34Lin 56Pos 55Chr "34567_40_234567_50_234567_60_234567_70_234567_80_234567" Bad LCD Command

20MHz 400KHz 4800 Clocks:
C 20MHz 1Lin 1Pos 10Chr "0101_20_23"
C 20MHz 45Lin 67Pos 74Chr "4567_30_234567_40_234567_50_234567_60_234567_70_234567_80_2 34567_10_234567" Bad LCD Command

40MHz 400KHz 6400 Clocks:
C 40MHz 1Lin 1Pos 6Chr "010123"
C 40MHz 45Lin 67Pos 78Chr "4567_10_234567_20_234567_30_234567_40_234567_50_234567_60_2 34567_70_234567_80_" Bad LCD Command

So the time to choke is:
Proportional to I2cSpeed and inversely proportional to CpuSpeed
As a guess:
Since each packet contains 86 bytes (1 Address + 1 Length + 84 String characters)
Clock cycles to first choke is about:
(6Chars + 2Chars) * 8 Bits/Char = 64bits
64bits / 400K bits/S = 0.00016 Seconds
40MHz = 40M Clocks/S
0.00016S * 40M Clocks/S = 6400 Clocks
Or
((6Chars + 2Chars) * 8Bits/Char) * 40MClocks/S / (400KBits/S) = 6400Clocks

These Clock times probably have a bunch of error to them but there seems to be a trend over a wide range.

Note! There may be 2 to 4 output lines all of which faithfully and accurately show all 84 characters with no errors. This shows that the I2C bus doesn't have any noise problems. One would
expect some errors if it was noisy, but there aren't any.

So, what do you think is the problem?

I hope I have explained this enough.

  Quote  Geoff
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6269
Posted: 11:45am 13 Oct 2014
Copy link to clipboard 
Print this post

Try putting a PAUSE in at the beginning of the rec interrupt routine
ReadD:
PAUSE 20
I2C SLAVE READ 84, Lcdin$, Rcvd
SL = Val(Mid$(Lcdin$,1,2))
SP = Val(Mid$(Lcdin$,3,2))
....

The interrupt is triggered as soon as the first bytes are received but it takes time to receive all 84 bytes.

If the value of Rcvd is less than expected, you need a longer delay.
Instead of a delay, you could check Rcvd and go back and read again until you have all the data.

Start with the delay.

Jim
VK7JH
MMedit
 
redrok

Senior Member

Joined: 15/09/2014
Location: United States
Posts: 209
Posted: 04:14pm 13 Oct 2014
Copy link to clipboard 
Print this post

Hi TassyJim;
  TassyJim said   Try putting a PAUSE in at the beginning of the rec interrupt routine
ReadD:
PAUSE 20
I2C SLAVE READ 84, Lcdin$, Rcvd
SL = Val(Mid$(Lcdin$,1,2))
SP = Val(Mid$(Lcdin$,3,2))
....

That worked nicely. Although I set it to 100mS to cover all the bases.
  Quote  The interrupt is triggered as soon as the first bytes are received but it takes time to receive all 84 bytes.

Ok, I get it that it takes time to read all the data.
But, why does MMBasic move on to the next command in the interrupt routine before the I2C command is finished receiving all its data?
How does that work?
  Quote  If the value of Rcvd is less than expected, you need a longer delay.
Instead of a delay, you could check Rcvd and go back and read again until you have all the data.

How can you tell if all the characters have come in if the quantity to be received are not known?
I do know the maximum number of characters though.
I could add a preamble to the text string that says how many more characters are to be sent I suppose.
  Quote  Start with the delay.

Jim

Redrok
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9589
Posted: 04:38pm 13 Oct 2014
Copy link to clipboard 
Print this post

How do you specify the uM slave bus speed?
You can set the master speed, but there is no option in the slave commands for telling MMBASIC what speed bus it has. Perhaps the slave commands default to 100kHz speed or something, and if that is the case, then setting a faster master speed would no doubt cause garbled comms between the two.

I am playing with this idea at the moment....
Smoke makes things work. When the smoke gets out, it stops!
 
BobD

Guru

Joined: 07/12/2011
Location: Australia
Posts: 935
Posted: 04:43pm 13 Oct 2014
Copy link to clipboard 
Print this post

I2C slave speed is just that. It is a slave to the master in clock speed also. It syncs on the clock provided by the master.
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9589
Posted: 05:04pm 13 Oct 2014
Copy link to clipboard 
Print this post

Ahhhhh - understand - thanks.

In my experiments thus far, I have got no results at all at 400kHz speed, but am getting results at 100kHz.


MASTER:

[code]
I2C open 100,100
D$="12345678901234567890123456789012345678901234567890"
D$=D$+Chr$(13)
I2C write &B1010101,0,Len(D$),D$
I2C read &B1010101,0,1,B
Print Chr$(B):D$=""
End
[/code]


SLAVE:

[code]
I2C slave open &B1010101,0,0,TXD,RXD

Do:Loop

TXD:
I2C slave write 1,75
Pause 50
IReturn

RXD:
Do
I2C slave read 1,B,N
D$=D$+Chr$(B)
If B=13 Then Exit Do
Loop
Print D$:D$=""
Pause 50
IReturn
[/code]

I have a feeling, that when used in a master/slave arrangement like this with two uM chips, you may well have to send data byte-at-a-time with acknowledge rather then just throwing the string at it. I am about to play with that idea now.
Smoke makes things work. When the smoke gets out, it stops!
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6269
Posted: 05:33pm 13 Oct 2014
Copy link to clipboard 
Print this post

The I2C command has no idea how much data is arriving, only the amount you want to read.

I2C can be treated a lot like RS232 data.
If you know how many bytes are coming in, you can wait for that many but you need to consider the case of transmission being interrupted.

If the I2C routine waited for the number of bytes you have specified (84 in your case) the program will hang if fewer that 84 arrived.

The Rcvd variable gives you the actual number of bytes received without the risk of program stalling.

You could read one character at a time and check for an end of line or similar or check the Rcvd value and if it is less than the number you wanted, go back and read again. This is quicker than setting a long delay but you still need to cover the loss of all transmission.

Jim


VK7JH
MMedit
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9589
Posted: 05:41pm 13 Oct 2014
Copy link to clipboard 
Print this post

I am able to get reliable results with the clock set to 10kHz. Any faster then this, and the comms are garbled. This is byte-at-a-time writing. Master sends a byte, then waits for slave to acknowledge, then sends another, till it runs into a CR, then it stops. The tinkering continues....





EDIT: OK, I have it working now right up to 400kHz.

MASTER:

[code]
I2C open 400,100
D$="The quick brown fox jumps over the lazy dog."
D$=D$+Chr$(13)
For X=1 To Len(D$)
I2C write &B1010101,0,1,Asc(Mid$(d$,x,1))
I2C read &B1010101,0,1,B 'Wait for slave to acknowledge byte
Next
Print Chr$(B)
End
[/code]


SLAVE:

[code]
I2C slave open &B1010101,0,0,TXD,RXD

Do
If B=13 Then D$=D$+Chr$(13):Print D$:D$="":B=0
Loop

TXD:
I2C slave write 1,75
IReturn

RXD:
I2C slave read 1,B,N
D$=D$+Chr$(B)
IReturn
[/code]

Apart from one lost character at the very beginning, all is as expected, even up at 400kHz. I will now make D$ much longer and see what happens, but I expect that slave-acknowledge must be used in the slave uM, or you will get nowhere - the master throws the data at the slave, and the slave is not fast enough to process an incoming byte, and get back to listening again, before the master has sent more data.Edited by Grogster 2014-10-15
Smoke makes things work. When the smoke gets out, it stops!
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9589
Posted: 06:23pm 13 Oct 2014
Copy link to clipboard 
Print this post

Working at 400kHz with a string 224 bytes long.

MASTER:

[code]
I2C open 400,100
EOL$=Chr$(13) + Chr$(10)
DA$="The quick brown fox jumps over the lazy dog." + EOL$
DB$="An expert is someone who will tell you why you can't do something."
DB$=DB$+EOL$
DC$="Forgive me if I am being thicker then the offspring of a" + EOL$
DD$="village idiot and a TV weather girl...(Blackadder)"
D$=Chr$(85)+DA$+DB$+DC$+DD$+EOL$
For X=1 To Len(D$)
I2C write &B1010101,1,1,Asc(Mid$(d$,x,1))
I2C read &B1010101,0,1,B 'Wait for slave to acknowledge byte
Next
I2C write &B1010101,0,1,27 'Send end-of-data byte
Print Chr$(B)
End
[/code]


SLAVE:

[code]
I2C slave open &B1010101,0,0,TXD,RXD

Do
If B=27 Then Print D$:D$="":B=0
Loop

TXD:
I2C slave write 1,75
IReturn

RXD:
I2C slave read 1,B,N
D$=D$+Chr$(B)
IReturn
[/code]





We're always missing the first byte of the message - not sure why at this stage, but if it was consistent, then a dummy byte could always be used for byte one.

EDIT: I have fixed the missing byte one, by adding a capital U to the start of the data string, as a data-slicer sync kind of thing. No missing characters now.



Edited by Grogster 2014-10-15
Smoke makes things work. When the smoke gets out, it stops!
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6269
Posted: 09:18pm 13 Oct 2014
Copy link to clipboard 
Print this post

Another way to receive data when the length of the string is unknown.
Theory: Once the data starts coming it arrives without any delays. This means that you can tell the data has finished when there is no data in a brief time.

Data is read from the buffer in short chunks and added to record$
The size of the PAUSE depends on the I2C transmission rate.

ReadD:
' received a message
record$=""
pause 1 ' it takes 500 uS for 5 bytes at 100k
do
I2C SLAVE READ 255, rx$, rcvd ' try to read a lot more than expecting
record$=record$+rx$
pause 1
loop until rcvd = 0 'keep looping until no more data received

IReturn


Not tested.

Jim
VK7JH
MMedit
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9589
Posted: 12:41pm 14 Oct 2014
Copy link to clipboard 
Print this post

Will experiment with that idea today.

Any idea why I should need the capital U sync byte to make my code work?

Is there an internal I2C data buffer in the uM and if so, what is it's size?

There is mention of the I2C bug that is known about. Can someone link me to that, so I can read about it. I remember reading in posts about the bug, but never bothered to actually read up on what the bug was.
Smoke makes things work. When the smoke gets out, it stops!
 
viscomjim
Guru

Joined: 08/01/2014
Location: United States
Posts: 925
Posted: 01:24pm 14 Oct 2014
Copy link to clipboard 
Print this post

Hi Grogster, take a look here.
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9589
Posted: 02:39pm 14 Oct 2014
Copy link to clipboard 
Print this post

Thanks.

Smoke makes things work. When the smoke gets out, it stops!
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025