Custom Overlays (BeagleBone AM335x): ADC + PWM
This chapter shows why and how to use a Device Tree overlay to enable:
- ADC (TSCADC -> IIO)
- EHRPWM1 on P9_14 / P9_16
1. Why overlays?
On AM335x, many header pins are multiplexed (GPIO, UART, I²C, SPI, PWM, …).
The base DTB enables only a safe subset.
Overlays are “patches” you can apply at boot (or at runtime with configfs) to:
- turn blocks on (
status = "okay") - apply pinmux (select a peripheral mode on a pad)
- avoid pin conflicts on your board
This keeps the kernel generic and your board configuration explicit and reproducible.
A DTB (Device Tree Blob) is a binary description of the hardware that the Linux kernel reads at boot time.
It tells the kernel what hardware exists, which drivers to load, and how those hardware components are connected and configured.
2. Pinmux math for P9_14 / P9_16
The AM335x control module exposes pad registers starting at 0x44E1_0800.
The pinctrl-single,pins entries use offset = absolute - 0x0800:
- P9_14 =
gpmc_a2, abs =0x0848-> offset0x048 - P9_16 =
gpmc_a3, abs =0x084C-> offset0x04C
For both pins, MODE = 6 selects ehrpwm1A/B.
3. Check for Device Tree compatibility
-
Run the following the command to see what device tree we have, because we don't want to create an overlay with the wrong tree - or we will run into boot errors.
-
Hopefully you should see:
The compatible field tells the kernel (and U-Boot) which hardware description this tree matches.
-
"
ti,am33xx" = matches the AM33xx SoC family (generic) -
"
ti,am335x-bone" = BeagleBone (original) -
"
ti,am335x-bone-black" = BeagleBone Black (specific board revision)
Your overlay must list at least one of these strings in its root node
-
4 Checking live.dts
-
Dump the currently running tree in to a file
live.dtswe can view -
You will then see that the compatible boards we found earlier should match those in this
live.dtsconfirming that board type is correct.
5. Check labels in your live DT
Now you will target well-known labels so the overlay binds on the image.
-
Create a folder in your
homedirectorydtsand within that a file calleddevice-tree.shthat prints the Device Tree symbol labels and the paths they resolve to in the live tree:# List symbols we care about ls /proc/device-tree/__symbols__ | egrep 'am33|epwmss1|ehrpwm1|tscadc' || true # Print each symbol -> its target path for s in am33xx_pinmux epwmss1 ehrpwm1 tscadc; do f="/proc/device-tree/__symbols__/$s" if [ -f "$f" ]; then printf "%-14s -> %s\n" "$s" "$(tr -d '\0' < "$f")" else echo "$s (missing)" fi done -
Run it:
am335x_adc am33xx_pinmux ehrpwm1 ehrpwm1_tbclk epwmss1 tscadc am33xx_pinmux -> /ocp/interconnect@44c00000/segment@200000/target-module@10000/scm@0/pinmux@800 epwmss1 -> /ocp/interconnect@48000000/segment@300000/target-module@2000/epwmss@0 ehrpwm1 -> /ocp/interconnect@48000000/segment@300000/target-module@2000/epwmss@0/pwm@200 tscadc -> /ocp/interconnect@44c00000/segment@200000/target-module@d000/tscadc@0You check
/proc/device-tree/__symbols__/before writing an overlay so you can:-
verify that your target labels (
&am33xx_pinmux,&epwmss1,&ehrpwm1,&tscadc, etc.) exist in the current kernel’s device tree, -
and confirm that your overlay’s
target = <&label>will bind cleanly at runtime.
If the label is missing or points somewhere unexpected, your overlay will fail to apply (
FDT_ERR_NOTFOUNDduringfdtoverlay), so this step avoids both wasted reboots and potential boot failures. -
6. Create the overlay (ADC + PWM1)
-
Create the file:
dts/BB-ADC-PWM-00A0.dtsdts file code.... [40 lines]
/dts-v1/; /plugin/; /* Enable AM335x ADC (TSCADC) and EHRPWM1 -> P9_14/P9_16 (MODE 6). */ &{/} { compatible = "ti,am335x-bone-black","ti,am335x-bone", "ti,am33xx"; part-number = "BB-ADC-PWM"; version = "00A0"; }; /* Pinmux controller (am33xx_pinmux). * Pad offsets are (absolute - 0x0800): * P9_14: gpmc_a2 abs 0x0848 -> off 0x048 -> MODE=6 (ehrpwm1A) * P9_16: gpmc_a3 abs 0x084C -> off 0x04C -> MODE=6 (ehrpwm1B) */ &am33xx_pinmux { bb_pwm1_pins: pinmux_bb_pwm1_pins { pinctrl-single,pins = < 0x048 0x06 /* P9_14 -> ehrpwm1A */ 0x04C 0x06 /* P9_16 -> ehrpwm1B */ >; }; }; /* Ensure the PWMSS1 subsystem and its TBCLK are enabled */ &epwmss1 { status = "okay"; }; &ehrpwm1_tbclk { status = "okay"; }; /* Bind the pins to EHRPWM1 */ &ehrpwm1 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&bb_pwm1_pins>; }; /* Enable ADC (tscadc) and expose AIN0..AIN6 */ &tscadc { status = "okay"; adc { ti,adc-channels = <0 1 2 3 4 5 6>; }; };Explanation of the dts file...
- Header:
- Declares this file as a Device Tree Source (v1) and marks it as a plugin (overlay) rather than a full base tree.
/dts-v1/;— required for all modern DTS files./plugin/;— instructs the compiler to include symbol references (__symbols__) so this file can patch an existing live tree./dts-v1/; /plugin/;
- Root Node Overlay:
- This fragment doesn’t modify hardware; it adds metadata used by the kernel and bootloader.
compatible— ensures the overlay only applies to matching boards (BeagleBone Black and related AM335x variants).part-number— used by the cape manager /configfssystem to identify the overlay.version— overlay revision (helps in debugging and upgrade control).
- Effectively this section declares “this overlay is safe to load on a BeagleBone Black.”
/* Enable AM335x ADC (TSCADC) and EHRPWM1 -> P9_14/P9_16 (MODE 6). */ &{/} { compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; part-number = "BB-ADC-PWM"; version = "00A0"; }; - This fragment doesn’t modify hardware; it adds metadata used by the kernel and bootloader.
-
Pinmux Configuration
-
Each AM335x pad can serve multiple functions (GPIO, UART, PWM, etc.).
-
&am33xx_pinmux— references the global pinmux controller node. -
pinmux_bb_pwm1_pins— a custom pin group we define here. -
pinctrl-single,pins— a list of pad offsets and mode values.
Pad Physical Pin Offset Mode Function Description gpmc_a2P9_14 0x048 6 ehrpwm1APWM channel A gpmc_a3P9_16 0x04C 6 ehrpwm1BPWM channel B - Mode 6 selects the PWM peripheral function for those pads.
/* * Pinmux controller (am33xx_pinmux) * Pad offsets are (absolute - 0x0800): * P9_14: gpmc_a2 abs 0x0848 -> off 0x048 -> MODE=6 (ehrpwm1A) * P9_16: gpmc_a3 abs 0x084C -> off 0x04C -> MODE=6 (ehrpwm1B) */ &am33xx_pinmux { bb_pwm1_pins: pinmux_bb_pwm1_pins { pinctrl-single,pins = < 0x048 0x06 /* P9_14 -> ehrpwm1A */ 0x04C 0x06 /* P9_16 -> ehrpwm1B */ >; }; }; -
-
PWM Subsystem Enablement
-
The
AM335xhas three PWM Subsystems (PWMSS0–2), each containing anEHRPWM,ECAP, andEQEPblock. -
&epwmss1— enables the PWM Subsystem 1 (normally disabled by default). -
&ehrpwm1_tbclk— ensures the Time Base Clock forPWM1is active.- Without this clock, the PWM registers can’t be written or toggled.
-
Setting
status = "okay";tells the kernel to probe and register these devices.
/* Ensure the PWMSS1 subsystem and its TBCLK are enabled */ &epwmss1 { status = "okay"; }; &ehrpwm1_tbclk { status = "okay"; }; -
-
PWM Node Binding
-
This connects the pinmux group defined earlier (
bb_pwm1_pins) to the actual PWM driver node. -
&ehrpwm1— symbolic label for thePWM1peripheral insideepwmss1. -
pinctrl-names/pinctrl-0— standard Linux convention for naming and linking pin control states. -
status = "okay";— ensures this PWM channel appears under/sys/class/pwm/.
/* Bind the pins to EHRPWM1 */ &ehrpwm1 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&bb_pwm1_pins>; }; -
-
ADC (TSCADC) Enablement
-
The TSCADC (Touchscreen Controller + ADC) block on the AM335x provides up to 8 analog channels (AIN0–AIN7).
-
&tscadc— symbolic label for the ADC node in the base tree. -
status = "okay";— powers up and registers the IIO ADC driver. -
adc { ti,adc-channels = <0 … 6>; }— exposes seven analog inputs (AIN0–AIN6).- AIN7 is typically reserved for the on-chip temperature sensor.
/* Enable ADC (tscadc) and expose AIN0..AIN6 */ &tscadc { status = "okay"; adc { ti,adc-channels = <0 1 2 3 4 5 6>; }; }; -
- Header:
7. Compile to .dtbo
-
Run the following command:
sudo dtc -@ -I dts -O dtb -o /boot/dtbs/$(uname -r)/overlays/BB-ADC-PWM-00A0.dtbo dts/BB-ADC-PWM-00A0.dtsCommand
dtc– the Device Tree Compiler. It converts human-readable DTS source into a binary DTB blob.
Flags:
-
-@– include an overlay symbol table and fixup sections in the output (__symbols__,__fixups__,__local_fixups__).- This is crucial for overlays (files with
/plugin/;): it preserves your&labelsso the overlay loader can resolve them against the live/base device tree when applying the overlay. Without-@, overlay application typically fails or can’t find targets.
- This is crucial for overlays (files with
-
-I dts– input format is DTS (text source). -
-O dtb– output format is DTB (binary “flattened device tree”). -
-o /boot/dtbs/$(uname - r)/overlays/BB-ADC-PWM-00A0.dtbo– write the compiled blob here. We use the.dtboextension to signal it’s an overlay (it’s still a DTB under the hood, just with the overlay sections).
8. Validate the overlay
-
Run a merge test without rebooting:
sudo fdtoverlay \ -i /boot/dtbs/$(uname -r)/am335x-boneblack.dtb \ -o /tmp/merged.dtb \ -v /boot/dtbs/$(uname -r)/overlays/BB-ADC-PWM-00A0.dtbo -
Inspect key nodes:
/tmp/merged.dts: Warning (unit_address_vs_reg): /target-module@4b000000/target-module@140000/pmu@0: node has a unit name, but no reg or ranges property ... /tmp/merged.dts: Warning (simple_bus_reg): /ocp/interconnect@44c00000/segment@200000/target-module@0/prcm@0/clocks: missing or empty reg/ranges property ... /tmp/merged.dts: Warning (unique_unit_address): /ocp/interconnect@4a000000/segment@0/target-module@100000/ethernet@0: duplicate unit-address (also used in node /ocp/interconnect@4a000000/segment@0/target-module@100000/switch@0) ... /tmp/merged.dts: Warning (graph_child_address): /ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/tda19988@70/ports: graph node has single child node 'port@0', #address-cells/#size-cells are not necessary- If you see this it is ok, these are warning from the base that existed beforehand.
10. Enabling for next boot
-
Run the following commands to have the overlay presistent on boot
enable_uboot_overlays=1:- Enables U-Boot’s “overlay loader” path.
- When this is
1, the BeagleBone boot scripts will look for overlay slots and apply any.dtbofiles you list before Linux starts.
echo 'uboot_overlay_addr4=/boot/dtbs/6.12.28-bone25/overlays/BB-ADC-PWM-00A0.dtbo' | sudo tee -a /boot/uEnv.txtuboot_overlay_addr4=/lib/firmware/BB-ADC-PWM-00A0.dtbo- Registers your overlay in
slot 4(slots areuboot_overlay_addr0…addr7). - U-Boot loads the base
.dtb, then in numeric order loads each listed.dtboand merges it into the base tree. Using slot 4 is conventional for user overlays and avoids clashes with other defaults.
- Registers your overlay in
-
Now you can reboot
11. Verify it worked
-
Once rebooted and logged in run the following:
-
Then run this command to see
adcdevice -
PWM should also appear under
/sys/class/pwm/
12. Quick-check summary
| Step | Command | Purpose |
|---|---|---|
| 1. Identify base DTB | tr -d '\0' </proc/device-tree/chosen/fdtfile | Know what to target |
| 2. Merge test | sudo fdtoverlay -i /boot/dtbs/$(uname -r)/am335x-boneblack.dtb -o /tmp/merged.dtb -v /lib/firmware/BB-ADC-PWM-00A0.dtbo | Validate compatibility |
| 3. Inspect | sudo dtc -I dtb -O dts -o /tmp/merged.dts /tmp/merged.dtb | Check merged result |
| 4. Live load | sudo mkdir /sys/kernel/config/device-tree/overlays/test && sudo cat /lib/firmware/BB-ADC-PWM-00A0.dtbo > /sys/kernel/config/device-tree/overlays/test/dtbo | Apply without reboot |
| 5. Remove overlay | sudo rmdir /sys/kernel/config/device-tree/overlays/test | Revert |