Skip to content
Libre Computer
Platform

Lab 6

Device tree and GPIO

Read the description of the hardware your kernel booted with, write an overlay that adds a real-time clock to it and binds the kernel's own driver, then drive the 40-pin header yourself: light an LED within the pin's limits, switch a motor the pin could never power, read a button, and measure how long Linux takes to do it.

What you need

  • Your station's board, booted to Linux, with the serial console or SSH
  • From your station's parts: LEDs, resistors, a push button, the DS3231 real-time clock module, the motor set (a logic-level MOSFET, a flyback diode, 100 Ω and 10 kΩ resistors and a small 5 V DC motor), the breadboard and jumper wires
  • The bench logic analyser
  • The bench multimeter
  • libretech-wiring-tool, installed in lab 1, for ldto and lgpio

Before you start

Read the rules that keep the board alive and module 10's electrical limits. The header is 3.3 V: power the DS3231 module from a 3.3 V pin, never from 5 V, and wire every part with the board unplugged. The motor runs from 5 V through the MOSFET only; the GPIO pin drives nothing but the MOSFET's gate.

Pick your pins in the Das Frite GPIO pinout: it shows each header pin's functions, its libgpiod line and which bus it can carry. Pins 3, 5 and 6 carry your serial console; leave them alone. Das Frite's catalogue also has a ready-made DS3231 overlay (ldto info i2c-a-ds3231); write your own first, then compare.

Pick your pins in the Solitude GPIO pinout: it shows each header pin's functions, its libgpiod line and which bus it can carry. Your serial console is on its own 3-pin header, so the 40-pin header is all yours.

Steps: device tree

  1. Read the live tree under /proc/device-tree, and decompile it with dtc into a source file you can read. Find the first kernel log lines (dmesg) that say it booted from a device tree. Save a listing of the devices in /sys and /dev (for example ls /dev/rtc* /dev/i2c-*) for your before-and-after comparison.
  2. Run lgpio pinmux 7J1 27 (7J1 is the wiring tool's name for the 40-pin header), then select header pin 27 in the GPIO pinout: read every function its pad can carry and the register write that selects each.
  3. Run ldto list, then ldto info H40P_I2C_1 to see the I2C overlay on header pins 27 (SDA) and 28 (SCL). Apply it with sudo ldto enable H40P_I2C_1 and check that a new /dev/i2c-* appears.
  4. With the board unplugged, wire the DS3231's SDA to pin 27, SCL to pin 28, VCC to a 3.3 V pin and GND to a ground pin. Boot, and apply the I2C overlay again (a live overlay does not survive a reboot).
  5. Write your own overlay that adds the DS3231 to that I2C bus with the compatible string of the kernel's DS3231 driver (the kernel's devicetree binding documentation names it), compile it with dtc, and apply it live through the kernel's configfs overlay interface (module 9 shows the commands).
  6. Check that a second /dev/rtc appears, and save the after listing. Then remove your overlay and sudo ldto disable H40P_I2C_1, and check that both go away.

Steps: GPIO

  1. Pick the LED's resistor: from the LED's voltage (its datasheet, about 2 V for a red LED) and a current of 2 mA, R = (3.3 V − 2 V) ÷ 2 mA = 650 Ω, so use the next standard value up, 680 Ω. With the board unplugged, wire the LED through it from a free GPIO pin to ground. Wire a push button from another free GPIO pin to ground, with a 10 kΩ pull-up from that pin to a 3.3 V pin (or the line's own pull-up, set from libgpiod), so the pin reads high when the button is open and low when it is pressed.
  2. Find both pins' lines with gpioinfo, and turn the LED on and off with gpioset before writing any code. To measure its current, shut the board down with sudo poweroff, wait for the halt and unplug it, set the multimeter to current (its mA jack and range) and wire it in series with the LED, then power on and turn the LED on. Shut down and unplug again, take the meter out and close the circuit. Move the meter's lead back to the voltage jack and set it to volts before you measure the pin's voltage with the LED on: a meter left on current shorts whatever it touches. Compare both readings with your calculation.
  3. Blink the LED from a program of your own with libgpiod, and read the button, debouncing it in software.
  4. With the board unplugged, connect the logic analyser's ground to a ground pin on the header, one channel to the LED pin and a second to the button pin. Power on and measure the toggle rate.
  5. Measure the latency on the two channels: from the button's first edge to your program's response on the LED pin. For this run, act on the first edge (debounce afterwards, or switch debouncing off), so the histogram shows Linux's response time and not your debounce delay. Record many presses with the board idle, then repeat under a CPU load on every core (the busy loops from lab 2: start one busy loop per core with for i in $(seq $(nproc)); do yes > /dev/null & done, and stop them afterwards with killall yes).

Steps: a load the pin cannot drive

  1. Read the motor's rated current. Compare it with what a pin can give (module 10), and write down why the motor must not be connected to the pin.
  2. With the board unplugged, build the motor circuit in module 10's figure: the motor from a 5 V header pin to the MOSFET's drain, the flyback diode across the motor with its stripe to 5 V, the MOSFET's source to ground, and its gate to a free GPIO pin through 100 Ω, with 10 kΩ from the gate to ground. Have your TA check the diode's direction before you power on.
  3. Power on, and switch the motor with gpioset, then from your program.
  4. Toggle the gate from your program at a few hundred hertz with different on and off times, and see the motor's speed follow the ratio. Look at the gate on the logic analyser.

What to measure

  • The LED's current and the pin's voltage with the LED on, against your calculation.
  • The fastest toggle rate your program reaches.
  • The latency from button to LED, as a histogram, idle and under load.

What you hand in

  • Your overlay source, and the before-and-after /sys and /dev listings.
  • Your GPIO code.
  • Your LED resistor calculation with the measured current, and the motor circuit's schematic with two sentences on why the pin cannot run the motor and what the diode does.
  • A latency histogram for both cases, idle and under load, with the count axis starting at zero and units in the axis titles.
  • One paragraph on why Linux is not hard real time, using your histogram.
  • Your one-page final project proposal, due with this report.
  • Your own short report: what you did, what you measured, and one problem you met and how you solved it.

If it goes wrong

If the second /dev/rtc does not appear, check the kernel log with dmesg for the driver's probe: a wrong compatible string, a bus that is not enabled or a node with status off are the usual causes. If the board does not boot after an overlay change, work down when it will not boot on your bench page.

If the motor does not turn, check that the MOSFET is a logic-level part and its source is grounded. If the board resets when the motor starts, the motor draws more than the board's supply can spare: use a smaller motor or give it its own supply, with grounds joined.