PFP9W/wiki Permanent Floating Plan 9 Workshop

The I²C or i2c is a serial communication bus found in a variety of devices. When available on a 9front system it is found under the '#J' kernel device. The #J directory will have directories labled i2cN, where N is the number of the i2c bus. And in the bus directories will be files labled i2c.N.data and i2c.N.ctl, where N is the device address for devices found on that bus.

While the data files can be access like any other file, the devices on them tend to be rather simle and often read and write simle 8bit pieces of data. It is best to use something like C to send the exact bits.

For example, sending the instructions to a Raspberry Pi Sense Hat magnetometer looks like this in C code;

	int fd;
	uchar buf[2];

	/* ctrl_reg1_m, high performance, 10Hz */
	buf[0] = 0x20;
	buf[1] = 0x50;
	pwrite(fd, buf, 2, 0);

	/* ctrl_reg2_m, default scale */
	buf[0] = 0x21;
	buf[1] = 0x00;
	pwrite(fd, buf, 2, 0);

	/* ctrl_reg3_m, power on, continuous mode */
	buf[0] = 0x22;
	buf[1] = 0x00;
	pwrite(fd, buf, 2, 0);

	/* ctrl_reg4_m, high performance Z axis */
	buf[0] = 0x23;
	buf[1] = 0x08;
	pwrite(fd, buf, 2, 0);

The datasheet for this device calls for write to be in the form of the first byte being a register address, and the second byte to be the data to be writen to that register. So a 2 byte buffer (buf|2|) is used. Since the standard read() and write() will advance a pointer, as if writing text into the next space of a file, pwrite() is used to keep that from interfering. With pwrite(), it takes the file descriptor, the data to write to it, the number of bytes to write, and the 0 it to move the offset back to the begining.

Reading that magnetometer looks like this;

	int fd;
	uchar reg[1], low[1], high[1];

	/* X axis */
	reg[0] = 0x28;
	pwrite(fd, reg, 1, 0);
	pread(fd, low, 1, 0);

	reg[0] = 0x29;
	pwrite(fd, reg, 1, 0);
	pread(fd, high, 1, 0);

In this case, the device returns 16 bits of data, but the registers only hold 8 bits. To do a read, a write must first be done to tell the device what register you want to read from, then a read is done to get the data. Again, pread() and pwrite() are bing used to keep the offset at 0.

With i2c devices, be sure to read the datasheet carefully. Some will have different register addressing schemes, take or return larger amounts of bytes, or may require wait times between setting a state and reading data.

Known 9Front issues;

9Front currently limits continuous reads and write to the i2c bus to 138 bytes. The reason being is that they are stored on the kernel stack, so space is limited. If you have a device, like a large or colorful display, you may run into this limit. Depending on your application, you may be able to modify /sys/src/9/port/devi2c.c to change the size of the send or recieve buffer. Or possibly rewrite it to allocate space on the heap. Patches welcome.