Raspbian is starting to become too heavy... some weird system-based stuff happens, then there is a cloud-thing. We do not need any of that on the old 512MB devices which mostly sit in some dark corner reporting on and controlling something.
The installation job is familiar: download an image to put on an SD-card, put the card into its place, and power up.
Initial installation on console has root with blank password. We must set one
Add users to the users group with specified uids:
useradd -u 201 -G wheel -g users hware
There is also the usermod command ther, to change things.
Set hostname etc. in /etc/rc.conf
users should be in the wheel group so they can use su to change to root as needed.
It is notable that the manual pages seem very comprehensive, compared to the ones on Linux. There will be references to them in the following, since they are really helpful with discovering how the system works.
The C compiler reports itself to be cc (nb3 20231008) 10.5.0 It is useful to see what other things it complains about compared to gcc and clang. clang didn't like some old K&R-style definitions but this cc is unhappy about ctype function looking at character arguments (not integer arguments).
Also, there is no qsort_r function defined so a workaround had to be made for that.
struct timeval wasn't defined where expected, it is however available from s<sys/time.h>
Compiling the usual utilities show there is no /usr/local so we start out making:
/usr/local/src /usr/local/bin /usr/local/lib /usr/local/include
Then the ldconfig has to be changed. In file ld.so.conf, had to add /usr/local/lib there.
Now all the usual utilities work.
There are three i2c busses enabled. Their devices are /dev/iic0, /dev/iic1, and /dev/iic2.
The main one that has SDA on pin P3, and SCL on pin P5 is /dev/iic2, while the others may be less obvious. One would be the on on P27/P28, and one is for the HDMI.
The tool i2cscan that exists is less than useful, it seems to report something on every address on every bus, from 0x09 to 0x77.
The communication is through ioctl(2) calls to the open(2) iic device, with the command I2C_IOCTL_EXEC where a struct i2c_ioctl_exec is passed with information about what to do. These constants and types are defined in /dev/i2c/i2c_io.h As usual with these things, we need to include the fcntl.h, unistd.h and sys/ioctl.h. All the details are in man 4 iic.
The operations can be write or read, with or without terminating with a stop conditions. SMBus equivalent actions typically use the I2C_OP_READ_WITH_STOP and I2C_OP_WRITE_WITH_STOP stop condition. Which ones are applicable will depend on the actual device in use. For example, READ_BLOCK or repeated reads without stop would be typical for multibyte reads from EEPROMs and other devices that have an automatically incrementing read address register.
While the i2c comes ready configured for use out of the box, accessing the other gpio lines is a little more work. While the actual IO is handled through the ioctl calls as per man 4 gpio, there is some configuration needed prior to that in order to make this available.
The default is no gpios made available. Obviously this isn't very interesting, so how to turn things on?
First, the main breaker has to be turned on in /etc/rc.conf through the statement
gpio=YES
Then, there is a file /etc/gpio.conf where each gpio line to be used after startup is identified, possibly given a name, and the configuration state, such as input, output, or one of the alternate functions, which will have to be found through hardware documentation.
man gpio.conf has all the details.
For a project using the Pi 2 with the 40-pin gpio header, I have used the following in this file. The gpios all sit on device gpio0, and the number is the traditional GPIO numbers. Some of these are input, others are output, and which is which will depend on what is to be connected there. The power pins and reserved pins are listed as comments. The pins used for i2c do not need any configuration info here.
Some of the ones set here as alt0 are used for SPI, more on that in further down.
They have been assigned names based on the physical pin number, but any other reasonable name will do.
# Blank lines and lines beginning with # are ignored # 3.3 V P_1 # gpio0 2 set alt0 P_3 # gpio0 3 set alt0 P_5 gpio0 4 set in P_7 # GND P_9 gpio0 17 set in P_11 gpio0 27 set in P_13 gpio0 22 set in P_15 # 3.3 V P_17 gpio0 10 set alt0 P_19_MOSI gpio0 9 set alt0 P_21_MISO gpio0 11 set alt0 P_23_SCLK # GND P_25 # gpio0 0 set in P_27_ID_SDA gpio0 5 set in P_29 gpio0 6 set in P_31 gpio0 13 set in P_33 gpio0 19 set in P_35 gpio0 26 set in P_37 # GND P_39 # 5 V P_2 # 5 V P_4 # GND P_6 # gpio0 14 set in P_8_TxD # gpio0 15 set in P_10_RxD gpio0 18 set in P_12 # GND P_14 gpio0 23 set in P_16 gpio0 24 set in P_18 # GND P_20 gpio0 25 set in P_22 gpio0 8 set alt0 P_24_CE0 gpio0 7 set alt0 P_26_CE1 # gpio0 1 set in P_28_ID_SCL # GND P_30 gpio0 12 set in P_32 # GND P_34 gpio0 16 set in P_36 gpio0 20 set out P_38 gpio0 21 set out P_40
With this in place, then gpioctl gpio0 list will show the ones that are enabled and with the assigned names.
4: P_7 5: P_29 6: P_31 7: P_26_CE1 8: P_24_CE0 9: P_21_MISO 10: P_19_MOSI 11: P_23_SCLK 12: P_32 13: P_33 16: P_36 17: P_11 18: P_12 19: P_35 20: P_38 21: P_40 22: P_15 23: P_16 24: P_18 25: P_22 26: P_37 27: P_13
Programming uses ioctl(2) calls on the open(2) /dev/gpio0 device. Thus we have to include unistd.h, fcntl.h, errno.h, sys/types.h, sys/gpio.h, and sys/ioctl.h, GPIOWRITE and GPIOREAD defined in sys/gpio.h are the main ones that are used, for reading and writing.
Unfortunately, there is no event capturing or interrupting ability made. The documentations has that shown as a bug. So some kind of wait loop reading a gpio line repeatedly, or some kind of secondary hardware that can catch and hold events, has to be added for this. It was necessary to do so for the rain gauge, to capture the 1/10 mm pulses coming from it.
SPI is also not enabled right away. It can be enabled for use, and it works well once turned on. Like the gpio and i2c it uses ioctl calls for its operation.
Look in /boot/dtb for the relevant dtb file. For the Pi2 this is bcm2836-rpi-2-b.dtb Make a backup copy of this file.
Convert this to the editable .dts file:
dtc -I dtb -O dts bcm2836-rpi-2-b.dtb -o bcm2836-rpi-2-b.dts
Look for the entry for spi@7e204000
Change the status there from "disabled" to "okay"
convert back to dtb:
dtc -I dts -O dtb bcm2836-rpi-2-b.dts -o bcm2836-rpi-2-b.dtb
Set the alternate function 0 for gpios 7 thru 11 in /etc/gpio.conf:
gpio0 7 set alt0 P_26_CE1 gpio0 8 set alt0 P_24_CE0 gpio0 9 set alt0 P_21_MISO gpio0 10 set alt0 P_19_MOSI gpio0 11 set alt0 P_23_SCLK
The names P_26_CE1 etc. are for convenience and ease of discovery. there must also be a statement
gpio=YES
in /etc/rc.conf for configuring this, as is also required with the regular gpios.
Reboot. /dev/spi0 is now present and can be made opened with open(2) and do work using the ioctl(2) calls as per spi(4).
As usual, we have to include the unistd.h, fcntl.h, and sys/ioctl.h, along with dev/spi/spi_io.h.
The code has to configure the relevant SPI device:
struct spi_ioctl_configure sic;
int sic.sic_addr; 0 for device driven by CE0 (P26), 1 for device driven by CE1 (P24)
int sic.sic_speed; SPI bus speed. Set to 100000 or some such useful value.
DO NOT SET TO 0. If set to 0 the machine will hang, and
have to be power-cycled!
int sic.sic_mode; SPI mode, 0 to 3
Mode 0: cpol=0 cpha=0
Data is shifted out on falling SCLK and when CE activates
Data is shifted in on rising SCLK
Mode 1: cpol=0 cpha=1
Data is shifted out on rising SCLK
Data is shifted in on falling SCLK
Mode 2: cpol=1 cpha=0
Data is shifted out on rising SCLK and when CE activates
Data is shifted in on falling SCLK
Mode 3: cpol=1 cpha=1
Data is shifted out on falling SCLK
Data is shifted in on rising SCLK
s = ioctl(fd, SPI_IOCTL_CONFIGURE, &sic);
Having configured, we can read and/or write.
struct spi_ioctl_transfer sit;
int sit.sit_addr; 0 for device driven by CE0 (P26), 1 for device driven by CE1 (P24)
const void *sit.sit_send; Octets to be sent, NULL if nothing.
size_t sit.sit_sendlen; Number of octets to be sent. 0 if none.
void *sit.sit_recv; Space for Octets to be received
size_t sit_recvlen; Expected number of octets
s = ioctl(fd, SPI_IOCTL_TRANSFER, &sit);
Testing was done using a max31855 thermocouple thermometer. This only needs the receive function, and reads 32 bits in 4 octets.
There may be a similar way to enable another spi device, spi1.
The spi@7e215080 entry in the dts file must be changed to "okay"
This bus has 3 device-select lines, and thus 3 addresses. The entries in /etc/gpio.conf will be something like:
gpio0 16 set alt4 P_36_CE2 gpio0 17 set alt4 P_11_CE1 gpio0 18 set alt4 P_12_CE0 gpio0 19 set alt4 P_35_MISO gpio0 20 set alt4 P_38_MOSI gpio0 21 set alt4 P_40_SCLK
Note the alternate function for these is alt4.
sic_addr and sit_addr can thus be 0, 1, or 2 for devices on this bus.
There is also mention of a third bus, spi2, but this is on gpios 40--45 which we don't have any access to.