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.
Modules this lab uses
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
ldtoandlgpio
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
- Read the live tree under
/proc/device-tree, and decompile it withdtcinto 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/sysand/dev(for examplels /dev/rtc* /dev/i2c-*) for your before-and-after comparison. - 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. - Run
ldto list, thenldto info H40P_I2C_1to see the I2C overlay on header pins 27 (SDA) and 28 (SCL). Apply it withsudo ldto enable H40P_I2C_1and check that a new/dev/i2c-*appears. - 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).
- Write your own overlay that adds the DS3231 to that I2C bus with the
compatiblestring of the kernel's DS3231 driver (the kernel's devicetree binding documentation names it), compile it withdtc, and apply it live through the kernel's configfs overlay interface (module 9 shows the commands). - Check that a second
/dev/rtcappears, and save the after listing. Then remove your overlay andsudo ldto disable H40P_I2C_1, and check that both go away.
Steps: GPIO
- 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.
- Find both pins' lines with
gpioinfo, and turn the LED on and off withgpiosetbefore writing any code. To measure its current, shut the board down withsudo 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. - Blink the LED from a program of your own with libgpiod, and read the button, debouncing it in software.
- 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.
- 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 withkillall yes).
Steps: a load the pin cannot drive
- 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.
- 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.
- Power on, and switch the motor with
gpioset, then from your program. - 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
/sysand/devlistings. - 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.