I2C Driver for Raspberry Pi programmer needed
Pages: 1 2
Chris Evans (457) 1614 posts |
Now that the RISC OS port is well under way. Is there anyone out there wanting to take on the I2C driver? |
Steve Revill (20) 1361 posts |
Hi Chris. I handed the sample you gave me over to Ben this evening. I doubt he’ll be able to work on the driver with other stuff at a higher priority on his ToDo list, but it does mean we’re in a position to potentially test anything that someone else writes. |
Ben Avison (25) 445 posts |
Just an implementation note: there’s no need to work on the IIC module – these days it’s a veneer onto the kernel SWI OS_IICOp. The reason for this is that the kernel needs to access the I2C bus to access RTC and non-volatile RAM chips. It can’t call out to the IIC module for these, because it needs to access these before the ROM module chain has been initialised. Instead, you should be implementing the HAL I2C API as described here: https://www.riscosopen.org/wiki/documentation/show/HAL%20IIC%20API (Historically, before RISC OS 5, there were actually two completely separate implementations of an I2C driver, in the kernel and in the IIC module – this wasn’t great for maintainability or portability. Having the code in one location has also made it possible for it to be called re-entrantly.) |
Dave Higton (1515) 3534 posts |
I’m volunteering for this one. |
Dave Higton (1515) 3534 posts |
It’s showing signs of life already. Early days yet, though. |
Dave Higton (1515) 3534 posts |
The IIC APIs are at a very low level. The device calling SWI_IICOp has to know all the hardware details of the device, notably the size of the address field within the device, and the page size for writing. All the operations have to be decomposed into idiot-level operations prior to calling SWI_IICOp. As an example, I’m using an AT24C32D for experiments. This has a 2 byte intra-device address field (it’s 32 kib, i.e. 4 kiB, so it has a 12 bit address field) in addition to the single byte device address, and a page size of 32 bytes. Any multi-byte write operations must be decomposed into page writes that must not cross a page boundary. It’s not pretty – although I have to admit that there is no way to read these parameters from a device, so the caller cannot escape the need to know the parameters. However, I can envisage a better high level call, where the breaks into page writes are handled by the cleverer version of OS_IICOp. At a lower level: it isn’t possible, or reasonable, to know or to set the states of the SCL and SDA lines of the Raspberry pi. The API was devised for bit-banging. HAL_IICSetLines will have to be a no-op, but I don’t know what it or HAL_IICReadLines should return. |
Jeffrey Lee (213) 6048 posts |
I was going to point you towards the docs on the wiki, but it looks like they aren’t quite as clear on the subject as I thought. But basically all that’s required is that you should implement only one of the two APIs (the high-level HAL_IICTransfer one or the low-level HAL_IICReadLines/HAL_IICSetLines one). HAL_IICType should indicate to the OS which API is implemented, and then the OS will stick to only using the calls appropriate to that API. So in your case you can just leave HAL_IICReadLines and HAL_IICSetLines as no-ops (i.e. NullEntry’s in the HAL entry table), and implement all the others. If in doubt, you can always check the OMAP or Tungsten HALs, as they both use the high-level interface. |
Ben Avison (25) 445 posts |
Jeffrey beat me to pointing at the fact that there are two different HAL APIs for I2C, depending upon whether the hardware has a dedicated I2C controller or just has to do it by bit-banging. On the high-level interface, by all means add an even higher-level interface (perhaps an additional SWI in the IIC module would be the place to put it?) but be aware that not all I2C devices use the 256 × 8-bit register model that you’re probably familiar with from EEPROMs where you write a byte containing the register number and then read or write one or more register values. (I’m assuming that’s what you’re thinking of?) |
Dave Higton (1515) 3534 posts |
I’ve not heard of such a register model. What I’m familiar with is where the address within the device, for random reads and writes, occupies 1, 2 or 3 bytes that are written immediately after the device address with R/nW set to Write. Then pages can be written one at a time. The AT24C32D (merely an example) has pages of 32 bytes. The AT17C512 has a 3 byte address (it’s only 64 kiB, so you’d think it would only need 2) which enables a few more bytes to be written in order to set the reset polarity of the device, and has IIRC 256-byte pages. It’s normal for sequential reads to auto-increment the address through the devices’ entire memory map before wrapping to 0, whereas the address for sequential writes wraps within the page. When all is said and done, the paging I’m writing about here is pretty much dedicated to EEPROMs, and won’t affect the RTCC that is most likely to interest RO Raspberry Pi users, which means that any higher level API could be introduced later – if there’s any interest at all. |
Dave Higton (1515) 3534 posts |
Having said that, the question of address lengths is also applicable to RAM-like devices. |
Dave Higton (1515) 3534 posts |
Where are the I/O registers when a HAL call is running? Are they always in the same place, including at initialisation time? I need to call a bit of stuff at initialisation time (I’m calling it right after the call to Interrupt_Init) so as to set up the alternate function for the IIC pins, and to set up the IIC clock speed (by default it seems to run at about 160 kHz, which isn’t a recognised speed). I don’t know whether the I/O has been remapped at this point. Thereafter I need to call the IIC controller for the various HAL_IIC operations, which will normally be after mapping is in place in the OS. I haven’t found any calls within the BCM2835 code to remap anything, so I don’t know how it’s done. Even the NVMemory code in the OMAP3 branch has changed hugely, so I haven’t yet found how to use that as a reference. |
Ben Avison (25) 445 posts |
The BCM2835 HAL maps in the IO space pretty early on – it couldn’t do much otherwise. It stores a pointer to the logical address of the base of IO space in its static variable “PeriBase” – search the HAL for this string and I’m sure you’ll find examples of how it’s used. |
Dave Higton (1515) 3534 posts |
Got it, and it seems to work, thanks, Ben. |
Tank (53) 375 posts |
How’s this going Dave ? |
Dave Higton (1515) 3534 posts |
It’s going positively, but slower than I would like. I’ve planned a big push for this weekend. |
Alex Gibson (528) 55 posts |
I also have a RTC module for the Pi, looks like this: Can I help with any testing? |
Dave Higton (1515) 3534 posts |
You will be able to, when I have anything testable. Right now the simple things (things that just return fixed values) work, but transfers don’t. |
Dave Higton (1515) 3534 posts |
Some progress: I have single simple transfers working. Questions about the algorithm for the checksum when only a checksum is returned. Is it a simple add in 32-bit groups, no carry involved? Are the words low-byte-in-low-address? If the buffer is not a multiple of 4, do we pad more significant bytes with zeros? Do we initialise the accumulator to 0? (I think the obvious answer to each is “yes”, but it isn’t actually specified anywhere AFAICS.) And I presume that a request for a write with the checksum-only flag set can be faulted? |
Jeffrey Lee (213) 6048 posts |
Checksum writes can be faulted, yes. For checksum reads:
|
Dave Higton (1515) 3534 posts |
There’s some danger that it’s working :-) Much more testing required now. How does RISC OS know what kind of RTCC is connected? How does it know the address and the register layout? The old PCF8583 and the DS1338 (as used in CJE’s RTCC for the RPi) have different register layouts, for example. |
Chris Evans (457) 1614 posts |
AIUI the DS1307 as used in the Iyonix is software compatible with the DS1338 The only difference being support for 3v3 operation! Replacing a DS1307 in an Iyonix with a DS1338 just works. |
Jeffrey Lee (213) 6048 posts |
The kernel has code to probe for a handful of different RTC/CMOS chips. It’s fairly simple code that just looks for something that responds to specific addresses, so could easily get fooled if a new kind of chip is present. If more control is needed for a particular platform, the HAL NVRAM API & HAL RTC device API can be used to control everything. |
Chris Evans (457) 1614 posts |
Using latest ROM our Real Time Clock & Temperature Sensor are now working on our Pi:-) |
Dave Higton (1515) 3534 posts |
In turn, my thanks go to Ben Avison for reviewing, correcting and merging my code; and to my employer for lending me a rather nice digital storage oscilloscope to check what the IIC lines were doing! |
Tank (53) 375 posts |
Just tested the I2C on my Pi with this device (just made the buzzer work !!) |
Pages: 1 2