One I2C bus, two programs - how does that work ?

On Sun, 24 Nov 2019 10:14:38 +0100, "R.Wieser" declaimed the following:

Except for the minor facet that Linux treats /everything/ as a /file/

-- and files don't have commonly have inherent mutex locking. That includes pretty much everything in /dev

formatting link

If one has the privilege to open the device file, anything goes. In early releases of the OS, one had to run programs using sudo in order to access GPIO pins -- they've since tended to add a "gpio" group, give the user "gpio" group membership, and tied the pins to the group for access.

One doesn't normally have privileges to access, say, a printer "device"; instead "printing" involves generating a data file of the print "image", and that file is submitted to a spooler daemon -- the daemon has access to the printer device, and it is responsible for only feeding one spooled file at a time to it.

This is the equivalent of a method you've already rejected -- creating a protocol your multiple applications will use to submit requests to a daemon, and the daemon then does all the actual I2C processing. Such a protocol would have to encapsulate entire transactions as one unit. {This will show up a bit later in this reply, in the context of existing features.}

The other candidate would be "concurrent".

As I understand, you don't have to worry about two I2C operations interfering with each other... BUT

Most actions to read from some device (a 3-axis MEMS sensor, say) require TWO I2C operations. First one performs a WRITE operation to specify to the device what internal data registers are going to be requested. On then performs a READ operation to actually fetch that data.

There is nothing that I've seen that would prevent a second process from doing a WRITE of its own to the device specifying a different register to be read.

WRITE request X data WRITE request Y data READ (gets Y instead of X) READ (who knows what gets returned*)

* For a device with X, Y, Z; many of them will "increment" the register so instead of

write request X read X write request Y read Y write request Z read Z

one just does

write request X read X read Y read Z

Locking so that the WRITE and READ occur before another process can get an operation on the device is not inherent. For this MEMS sensor, that means locking it for between 4 [the incrementing mode] and 6 separate [explicit request mode] operations.

When the two processes are accessing different devices (different bus or same bus different address), there is no conflict -- the state of the device is not affected between the WRITE and READ from a process, regardless of what the other process does between them. The driver ensures that one operation is performed before the next gets on the bus.

--
	Wulfraed                 Dennis Lee Bieber         AF6VN 
	wlfraed@ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/
Reply to
Dennis Lee Bieber
Loading thread data ...

Dennis,

...

Which means its good I asked. :-)

But that raises the question about what I should think about whats in the third link you posted (i2c-concurrent-access-on-linux-mutex), which seems to indicate its present - and on a low level too.

...

I didn't (intend to) reject the gatekeeper method itself, just the part where the communication with it is supposed to be done by the user "through some protocol". I would like to create/see an API, preferrably shadowing an existing I2C one.

[snip]

That is the problem I tried to explain in my "lightbulb moment" paragraph. Although I referred to the I2C signals themselves (the "start" and "stop" ones) I also assumed that those signals would coincide with the locking and unlocking of the I2C bus (or the other way around).

In other words, I was coining up the possibility of an atomic write-read sequence (by not ending the write sequence with a "stop" condition, but sending another "start" instead). It would solve most, if not all of the interference problems.

The only question is if that has already been implemented, or if I need to/can do it myself.

Regards, Rudy Wieser

Reply to
R.Wieser

I2C is an edge-triggered protocol by design. Very small but fast glitches can upset the hardware peripherals. Designing I2C interfaces with good noise immunity is well beyond the scope of very cost-sensitive devices including the Raspberry Pi.

Stephen

--
Stephen Pelc, stephen@mpeforth.com 
MicroProcessor Engineering Ltd - More Real, Less Time 
133 Hill Lane, Southampton SO15 5AF, England 
tel: +44 (0)23 8063 1441, +44 (0)78 0390 3612 
web: http://www.mpeforth.com - free VFX Forth downloads
Reply to
Stephen Pelc

Given the nature of I2C, high-reliability and safety-critical applications need to assume that I2C *will* fail at some stage.

Reliabilty of a comms link does not mean that the link has no errors, only that you will be able to detect the errors.

Stephen

--
Stephen Pelc, stephen@mpeforth.com 
MicroProcessor Engineering Ltd - More Real, Less Time 
133 Hill Lane, Southampton SO15 5AF, England 
tel: +44 (0)23 8063 1441, +44 (0)78 0390 3612 
web: http://www.mpeforth.com - free VFX Forth downloads
Reply to
Stephen Pelc

Ok, I thought that consumers you are targeting are your own applications, not some third party from unknown developers :)

Well, who knows, maybe you can invent some new standard (which AFAIK exists for this kind of applications) :)

--
obruT
Reply to
obruT

On Sun, 24 Nov 2019 19:43:02 +0100, "R.Wieser" declaimed the following:

See below...

formatting link
""" Note about combined messages: Some I2C controllers can only send one message per transfer, plus something called combined message or write-then-read. This is (usually) a small write message followed by a read message and barely enough to access register based devices like EEPROMs. There is a flag to support this mode. It implies max_num_msg = 2 and does the length checks with max_comb_*_len because combined message mode usually has its own limitations. Because of HW implementations, some controllers can actually do write-then-anything or other variants. To support that, write-then-read has been broken out into smaller bits like write-first and read-second which can be combined as needed. """ Emphasis: *** Some I2C controllers can only send one message per transfer ***

""" int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg * msgs, int num)

execute a single or combined I2C message """

So taking the two together -- it is possible that i2c_transfer() handles each part as a separate transaction. But to determine that will require looking at the kernel source code for the I2C on the target (R-Pi) computer. If one is lucky, even if the messages are handled as separate actual transactions the kernel may wrap all of them into one lock -- ie: each API call gets wrapped in lock/unlock functions.

That does mean you have to use that specific function if you need a sequence of "write setup, read a {, read b, ...}", you can not use i2c_master_send, i2c_master_recv, nor the various i2c_smbus_[read|write] calls.

NOTE there is also an """

int __i2c_transfer(struct i2c_adapter * adap, struct i2c_msg * msgs, int num)

unlocked flavor of i2c_transfer """ which goes on to state that "Adapter lock must be held when calling this function." -- which again suggests that i2c_transfer() may internally lock. {and so does the i2c-concurrent-access-on-linux-mutex stackflow commentary}

Best guess, without checking source code, is that use of any read/write API call has the risk of conflict, even if the read/write API in turn calls i2c_transfer(), as the lock is at the single call level. ANY I2C device that requires multiple write/read combinations is vulnerable unless one builds the individual messages and directly invokes i2c_transfer()

If you aren't using the low-level C API, you'll need to check the code of the library you are using...

Python smbus library does not have a multi-message API.

formatting link

Python smbus2 library

formatting link
, OTOH, has """ i2c_rdwr(*i2c_msgs)

Combine a series of i2c read and write operations in a single transaction (with repeated start bits but no stop bits in between). """ (but I can't figure out how to specify where the read data goes...)

""" @staticmethod def read(address, length): """ Prepares an i2c read transaction. :param address: Slave address. :type: address: int :param length: Number of bytes to read. :type: length: int :return: New :py:class:`i2c_msg` instance for read operation. :rtype: :py:class:`i2c_msg` """ arr = create_string_buffer(length) return i2c_msg( addr=address, flags=I2C_M_RD, len=length, buf=arr) """ Let's see -- it creates a buffer "arr" and specifies that buffer when creating the msg instance. My best guess is that AFTER i2c_rdwr() one has to loop through each of the i2c_msgs (passed to i2c_rdwr()) looking for the read message(s) (so, most likely skipping the first which sets up the device), and extracting the buffer field (which is a CTYPES POINTER field

-- have to see how one converts from ctypes pointer to Python string).

CircuitPython (Adafruit via blinka library) looks like they explicitly lock (but again -- I think that lock is only relevant to a single process that may be running multiple threads; not multiple processes).

formatting link
Using the busdevice add-on
formatting link
provides a write_then_readinto() function, but it is not clear if it handles multiple reads at once (unless one read per byte of the input buffer length).

""" if hasattr(self.i2c, 'writeto_then_readfrom'): # In linux, at least, this is a special kernel function call self.i2c.writeto_then_readfrom(self.device_address, out_buffer, in_buffer, out_start=out_start, out_end=out_end, in_start=in_start, in_end=in_end)

else: # If we don't have a special implementation, we can fake it with two calls self.write(out_buffer, start=out_start, end=out_end, stop=False) self.readinto(in_buffer, start=in_start, end=in_end) """

And just to be annoying:

formatting link

--
	Wulfraed                 Dennis Lee Bieber         AF6VN 
	wlfraed@ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/
Reply to
Dennis Lee Bieber

obruT,

For some reason I always try to come up with solutions that are usable for an as broad as possible audience, even if what I make will only run on my own machine. Call it a quirk. :-)

It already exists* ? Any idea where I should look for it ?

*A bit of a "duh" actually - I'm rather certain I'm not the first, or even the hundred-est one with this problem

Regards, Rudy Wieser

Reply to
R.Wieser

Dennis,

My apologies for the late reply. I needed some time to rethink my position.

After I posted my reply talking about an I2C "backpack" for a simple two-line LCD display I realized that even just monitoring the LCD's "busy" line and subsequently feed it (more) data would require a level of intelligence of the I2C driver that I could/should not expect from it.

In short, I realized I got a bit ahead of myself. :-|

...

[snip]

Thanks. You (again) gave me lots of stuff to look at. :-)

And as you described it an actually working-as-atomic sequence of multiple (mixes of) read/write sequences is a bit of the luck-of-the-draw. IOW: It /might/ work in one implementation, but doesn't need to in another. I can't say I like that ....

Did you really /have/ to do that (being annoying) ? :-)

But yes, thats (multi-threading) also something to be looked at.

Regards, Rudy Wieser

Reply to
R.Wieser

On Sat, 30 Nov 2019 15:09:37 +0100, "R.Wieser" declaimed the following:

That's the simpler situation -- as it is easy to use mutex semaphores to surround critical sections since all threads have access to the process state. Not so easy to do when the "sections" are in completely separate processes with separate memory blocks, etc.

{Wow -- those 2x16 LCDs have mutated since I last looked at them... I think I have one or two of the pure 8/4-bit parallel models, followed by at least one Parallax serial model -- where they added a sub-board with a PIC processor and rely on simple UART protocol for communication, to now having I2C busses

I wonder how many Parallax still sells -- when a UART serial 2x16 is $41, while some imported I2C version is $6 on Amazon.

Then again -- Parallax BASIC Stamps {PIC-based} [if still in stock] were going for $70 when much more capable Arduino {AVR-based} went for $40, and a TI TIVA C123 {ARM Cortex M4F} can be had for $13 }

--
	Wulfraed                 Dennis Lee Bieber         AF6VN 
	wlfraed@ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/
Reply to
Dennis Lee Bieber

Normally for a device which requires reading and writng you would create a driver providing an API to clients, which would serialise access to the device.

However if it is only reading the value of sensors, it is probably easiest to have one program which regular reads the values, and publishes them for other programs to use.

For example, to get a running average of the CPU temperature over a period of 15 minutes, I wrote a python program which runs as a daemon at start up, reading the CPU temperature every second calculating the running average, min and max values. The values are read by running another instance of the program which communicates with the daemon instance via a named pipe.

---druck

Reply to
druck

Dennis,

Which was/is the situation I'm thinking of (just imagine the OS accessing the RTC, while another program is trying to set one of its alarms).

Thats the model I was referring to (salvaged a number of them).

The "I2C backpack" is actually just a dumb 8-bit I2C expander plus a backlight-feeding transistor and a potmeter for the contrast. In short, everything you normally feed to the 4-bit interface of the LCD now has to go over I2C - hence the previously mentioned checking of the LCD's busy line.

formatting link

By the way: There seem to be two major variants: One using the lower 4 bits of the expander for data, and the other (ofcourse) uses the higher bits.

Regards, Rudy Wieser

Reply to
R.Wieser

druck,

That would be the second-optimal solution (the first one being that its done by the OS itself).

Alas, thats not always possible. Take for example an RTC. When its installed the OS might access it whenever it pleases, and at the same time another program might be trying to set one of its alarms. I don't think I've got much control over when/how the OS does its thing, nor can I easily put a gatekeeper inbetween the OS and the RTC ...

Next to that, such a driver providing an API to clients needs the cooperation of the involved programs. Including those written by other hobbyists like myself. Do you see the problem ?

Regards, Rudy Wieser

Reply to
R.Wieser

On Sat, 30 Nov 2019 18:56:41 +0100, "R.Wieser" declaimed the following:

Odd -- I thought 4-bit mode on those displays always used the same four bits... 8-bit mode, obviously, uses all of them at once.

Complete units

formatting link

(though the adapter is cheap (once I found a price in $... if you need a lot of them

formatting link
) And
formatting link
have all three interfaces: UART, I2C, and SPI

--
	Wulfraed                 Dennis Lee Bieber         AF6VN 
	wlfraed@ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/
Reply to
Dennis Lee Bieber

I actually did something simuilar to get data from a sound deamon back to a contrilling web browser, but I didn't use a pipe. I created a ramdiosk of a few K and mounted that and wrote a file in it. Daemon writes: browser reads.

Very simple.

Cant ever fill up. It just gets overwritten by 'current state' every so often...

--
Of what good are dead warriors? ? Warriors are those who desire battle  
more than peace. Those who seek battle despite peace. Those who thump  
their spears on the ground and talk of honor. Those who leap high the  
battle dance and dream of glory ? The good of dead warriors, Mother, is  
that they are dead. 
Sheri S Tepper: The Awakeners.
Reply to
The Natural Philosopher

Dennis,

They do, they do.

Its about which outputs of the I2C expander are connected to it. With some you need to send the LCD 4-bit data in the upper nibble and the LCD control signals in the lower to the expander, and others the other way around.

I'm an hobbyist myself (don't need quantities) and got a single I2C backpack for 2 Euros. Not a bad price.

Regards, Rudy Wieser

Reply to
R.Wieser

on

The only problem being if someone tries reading while it being re-written. Did you use advisory locking, or a write and replace strategy ?

---druck

Reply to
druck

to go

line.

I've made my own backpack for a 4x20 LCD using an MCP23017. Works fine. I didn't bother with monitoring the busy flag. There are various timing tables for HD44780 operations in the data sheet which can be used. Makes it a lot simpler, and you would generate a hell of a lot of I2C traffic. I've also programmed HD44780 character LCDs direct as well, without using the busy flag - it is a very common prectice. I belive all the HD44780 look-a-likes maintain the timings.

Jim

Reply to
Jim Jackson

I dont think that can happen. Disk writes are atomic operations as are disk reads below the sector level.

Did you use advisory locking, or a write and replace strategy?

--
There is something fascinating about science. One gets such wholesale  
returns of conjecture out of such a trifling investment of fact. 

Mark Twain
Reply to
The Natural Philosopher

Jim,

I'm aware of the "just wait for a bit" solution (no pun intended :-) ). But both the need to have to specify the timings for all kinds of different actions (complicates the matter) as well as the doubt if it wil work for any random connected display made me choose polling the busy line over it. Dependability over simplenes I'm afraid.

:-) Thats why I normally stick a small delay into the loop. Long enough to give other calls a chance, and short enough to keep the overall delay small.

Regards, Rudy Wieser

Reply to
R.Wieser

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.