Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Last updated: Tuesday 18 November 2025 @ 17:23:11

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.

What is DTB?

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 -> offset 0x048
  • P9_16 = gpmc_a3, abs = 0x084C -> offset 0x04C

For both pins, MODE = 6 selects ehrpwm1A/B.


3. Check for Device Tree compatibility

  1. 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.

    Terminal

    tr '\0' '\n' </proc/device-tree/compatible
    
  2. Hopefully you should see:

    Output

    ti,am335x-bone-black
    ti,am335x-bone
    ti,am33xx
    

    What this output means...

    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

  1. Dump the currently running tree in to a file live.dts we can view

    Terminal

    sudo dtc -I fs -O dts /sys/firmware/devicetree/base > live.dts 2>/dev/null
    
  2. You will then see that the compatible boards we found earlier should match those in this live.dts confirming that board type is correct.

    Terminal

    grep -m1 -w compatible live.dts
    

    Output

    compatible = "ti,am335x-bone-black\0ti,am335x-bone\0ti,am33xx";
    
    • \0 is a null terminator which is the separator for each board type

5. Check labels in your live DT

Now you will target well-known labels so the overlay binds on the image.

  1. Create a folder in your home directory dts and within that a file called device-tree.sh that prints the Device Tree symbol labels and the paths they resolve to in the live tree:

    Code

    # 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
    
  2. Run it:

    Terminal

    bash dts/device-tree.sh
    

    Output

    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@0
    

    Why this matter?

    You 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_NOTFOUND during fdtoverlay), so this step avoids both wasted reboots and potential boot failures.

6. Create the overlay (ADC + PWM1)

  1. Create the file: dts/BB-ADC-PWM-00A0.dts

    Terminal

    nano /dts/BB-ADC-PWM-00A0.dts
    

    dts 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 / configfs system 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";
      };
      

    • 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.

      PadPhysical PinOffsetModeFunctionDescription
      gpmc_a2P9_140x0486ehrpwm1APWM channel A
      gpmc_a3P9_160x04C6ehrpwm1BPWM 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 AM335x has three PWM Subsystems (PWMSS0–2), each containing an EHRPWM, ECAP, and EQEP block.

      • &epwmss1 — enables the PWM Subsystem 1 (normally disabled by default).

      • &ehrpwm1_tbclk — ensures the Time Base Clock for PWM1 is 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 the PWM1 peripheral inside epwmss1.

      • 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>;
          };
      };
      

7. Compile to .dtbo

  1. Run the following command:

    Terminal

    sudo dtc -@ -I dts -O dtb -o /boot/dtbs/$(uname -r)/overlays/BB-ADC-PWM-00A0.dtbo dts/BB-ADC-PWM-00A0.dts
    

    Explanation

    Command

    • 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 &labels so 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.
    • -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 .dtbo extension to signal it’s an overlay (it’s still a DTB under the hood, just with the overlay sections).

8. Validate the overlay

  1. Run a merge test without rebooting:

    Terminal

    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
    

    Terminal

    echo "rc=$?"; ls -lh /tmp/merged.dtb
    

    Output

    rc=0
    -rw-r--r-- 1 root root 98K May 28 11:10 /tmp/merged.dtb
    
    • If rc=0 and /tmp/merged.dtb has a size > 100 kB, the overlay merged cleanly.
  2. Inspect key nodes:

    Terminal

    sudo dtc -I dtb -O dts -o /tmp/merged.dts /tmp/merged.dtb
    

    Output

    /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.

    Terminal

    grep -n 'status = "okay"' /tmp/merged.dts | head
    

    Output

    1093:         status = "okay";
    1122:         status = "okay";
    1143:         status = "okay";
    1149:         status = "okay";
    1286:         status = "okay";
    1563:         status = "okay";
    1881:         status = "okay";
    2106:         status = "okay";
    2247:         status = "okay";
    2566:         status = "okay";
    
    • epwmss1, ehrpwm1_tbclk, ehrpwm1, and tscadc marked "okay"

10. Enabling for next boot

  1. Run the following commands to have the overlay presistent on boot

    Terminal

    echo 'enable_uboot_overlays=1' | sudo tee -a /boot/uEnv.txt
    

    Explanation

    • 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 .dtbo files you list before Linux starts.

    Terminal

    echo 'uboot_overlay_addr4=/boot/dtbs/6.12.28-bone25/overlays/BB-ADC-PWM-00A0.dtbo' | sudo tee -a /boot/uEnv.txt
    

    Explanation

    • uboot_overlay_addr4=/lib/firmware/BB-ADC-PWM-00A0.dtbo
      • Registers your overlay in slot 4 (slots are uboot_overlay_addr0addr7).
      • U-Boot loads the base .dtb, then in numeric order loads each listed .dtbo and merges it into the base tree. Using slot 4 is conventional for user overlays and avoids clashes with other defaults.
  2. Now you can reboot

    Terminal

    sudo reboot
    

    Warning

    If you have errors in any previou step and not resloved them, you could brick your device... you have been warneds

11. Verify it worked

  1. Once rebooted and logged in run the following:

    Terminal

    ls /sys/bus/iio/devices
    
  2. Then run this command to see adc device

    Terminal

    grep -H . /sys/bus/iio/devices/iio:*/name
    

    Output

    /sys/bus/iio/devices/iio:device0/name:TI-am335x-adc.0.auto
    

    Warning

    Analogue header map:

    • AIN0..6 -> P9_39, P9_40, P9_37, P9_38, P9_33, P9_36, P9_35
    • Max input is 1.8 V — use a divider above that.
  3. PWM should also appear under /sys/class/pwm/

    Terminal

    tree -d /sys/class/pwm/**
    

    Output

    /sys/class/pwm/pwmchip0
    |-- device -> ../../../48302200.pwm
    |-- power
    |-- pwm0
    |   `-- power
    |-- pwm1
    |   `-- power
    `-- subsystem -> ../../../../../../../../../../class/pwm
    

12. Quick-check summary

Important


StepCommandPurpose
1. Identify base DTBtr -d '\0' </proc/device-tree/chosen/fdtfileKnow what to target
2. Merge testsudo fdtoverlay -i /boot/dtbs/$(uname -r)/am335x-boneblack.dtb -o /tmp/merged.dtb -v /lib/firmware/BB-ADC-PWM-00A0.dtboValidate compatibility
3. Inspectsudo dtc -I dtb -O dts -o /tmp/merged.dts /tmp/merged.dtbCheck merged result
4. Live loadsudo mkdir /sys/kernel/config/device-tree/overlays/test && sudo cat /lib/firmware/BB-ADC-PWM-00A0.dtbo > /sys/kernel/config/device-tree/overlays/test/dtboApply without reboot
5. Remove overlaysudo rmdir /sys/kernel/config/device-tree/overlays/testRevert