Debian 12 for i.MX6UL 2

From embeddedTS Manuals

Getting Started

Some information here


Networking

The network in Debian is configured with /etc/network/interfaces. For complete documentation, see Debian's documentation here

Some common examples are shown below. On this release network interfaces follow the predictible network interface names. Run ip addr show to get a list of the network interfaces.

Most commonly:

  • end0 - Ethernet device 0 (CPU Ethernet)
  • enp1s0 - Ethernet PCIe port 1 slot 0 ethernet
  • usb<mac> - USB ethernet
  • wlan0 - WIFI

DHCP on end0. Edit the file /etc/network/interfaces and add:

auto end0
allow-hotplug end0
iface end0 inet dhcp

Static IP on end0. Edit the file /etc/network/interfaces and add:

auto end0
iface end0 inet static
    address 192.0.2.7/24
    gateway 192.0.2.254

These will take effect on the next boot, or by restarting the networking service:

service networking restart


Wi-Fi Client

Wireless interfaces are also managed with configuration files in "/etc/network/interfaces.d/". For example, to connect as a client to a WPA network with DHCP. Note some or all of this software may already be installed on the target SBC.

Install wpa_supplicant:

apt-get update && apt-get install wpasupplicant -y

Run:

wpa_passphrase youressid yourpassword

This command will output information similar to:

 network={
 	ssid="youressid"
 	#psk="yourpassword"
 	psk=151790fab3bf3a1751a269618491b54984e192aa19319fc667397d45ec8dee5b
 }

Use the hashed PSK in the specific network interfaces file for added security. Create the file:

/etc/network/interfaces.d/wlan0

allow-hotplug wlan0
iface wlan0 inet dhcp
    wpa-ssid youressid
    wpa-psk 151790fab3bf3a1751a269618491b54984e192aa19319fc667397d45ec8dee5b

To have this take effect immediately:

service networking restart

For more information on configuring Wi-Fi, see Debian's guide here.


Wi-Fi Access Point

First, hostapd needs to be installed in order to manage the access point on the device:

apt-get update && apt-get install hostapd -y


Note: The install process will start an unconfigured hostapd process. This process must be killed and restarted before a new hostapd.conf will take effect.

Edit /etc/hostapd/hostapd.conf to include the following lines:

interface=wlan0
driver=nl80211
ssid=YourAPName
channel=1
Note: Refer to the kernel's hostapd documentation for more wireless configuration options.


To start the access point launch hostapd:

hostapd /etc/hostapd/hostapd.conf &

This will start up an access point that can be detected by WIFI clients. A DHCP server will likely be desired to assign IP addresses. Refer to Debian's documentation for more details on DHCP configuration.


Installing New Software

Debian provides the apt-get system which allows management of pre-built applications. The apt tools require a network connection to the internet in order to automatically download and install new software. The update command will download a list of the current versions of pre-built packages.

apt-get update

A common example is installing Java runtime support for a system. Find the package name first with search, and then install it.

root@tsa38x:~# apt-cache search openjdk
default-jdk - Standard Java or Java compatible Development Kit
default-jdk-doc - Standard Java or Java compatible Development Kit (documentation)
default-jdk-headless - Standard Java or Java compatible Development Kit (headless)
default-jre - Standard Java or Java compatible Runtime
default-jre-headless - Standard Java or Java compatible Runtime (headless)
jtreg - Regression Test Harness for the OpenJDK platform
libreoffice - office productivity suite (metapackage)
openjdk-11-dbg - Java runtime based on OpenJDK (debugging symbols)
openjdk-11-demo - Java runtime based on OpenJDK (demos and examples)
openjdk-11-doc - OpenJDK Development Kit (JDK) documentation
openjdk-11-jdk - OpenJDK Development Kit (JDK)
openjdk-11-jdk-headless - OpenJDK Development Kit (JDK) (headless)
openjdk-11-jre - OpenJDK Java runtime, using Hotspot JIT
openjdk-11-jre-headless - OpenJDK Java runtime, using Hotspot JIT (headless)
openjdk-11-jre-zero - Alternative JVM for OpenJDK, using Zero
openjdk-11-source - OpenJDK Development Kit (JDK) source files
uwsgi-app-integration-plugins - plugins for integration of uWSGI and application
uwsgi-plugin-jvm-openjdk-11 - Java plugin for uWSGI (OpenJDK 11)
uwsgi-plugin-jwsgi-openjdk-11 - JWSGI plugin for uWSGI (OpenJDK 11)
uwsgi-plugin-ring-openjdk-11 - Closure/Ring plugin for uWSGI (OpenJDK 11)
uwsgi-plugin-servlet-openjdk-11 - JWSGI plugin for uWSGI (OpenJDK 11)
java-package - Utility for creating Java Debian packages

In this case, the wanted package will likely be the "openjdk-11-jre" package. Names of packages can be found on Debian's wiki pages or the packages site.

With the package name apt-get install can be used to install the prebuilt packages.

apt-get install openjdk-11-jre
# More than one package can be installed at a time.
apt-get install openjdk-11-jre nano vim mplayer

For more information on using apt-get refer to Debian's documentation here.


Setting up SSH

Openssh is installed in our default Debian image, but by default openssh does not permit root logins, and requires a password to be set. Additionally, a host key is required if one hasn't already been created on the target board. To allow remote root login:

sed --in-place 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
systemctl restart ssh.service
passwd root # Set any password

If you ssh to this system it will now support ssh as root.


Starting Automatically

A systemd service can be created to start up headless applications. Create a file in /etc/systemd/system/yourapp.service

[Unit]
Description=Run an application on startup

[Service]
Type=simple
ExecStart=/usr/local/bin/your_app_or_script

[Install]
WantedBy=multi-user.target

If networking is a dependency add "After=network.target" in the Unit section. Once you have this file in place add it to startup with:

# Start the app on startup, but will not start it now
systemctl enable yourapp.service

# Start the app now, but doesn't change auto startup
systemctl start yourapp.service
Note: See the systemd documentation for in depth documentation on services.


Cross Compiling

Debian provides cross toolchains within their distribution for different architectures.

For best portability we recommend using a container like docker to run a Debian 12 rootfs for the toolchain. This will allow a consistent toolchain to run from almost any Linux system that can run Docker. Keep in mind that while docker does run under OSX and Windows, these are run under a case insensitive filesystem which will cause problems with complex builds like the Linux kernel so a Linux host is still recommended.

  • Ubuntu/Debian:
sudo apt-get install docker.io -y
  • Fedora
sudo dnf install docker -y

After installing docker on any distribution make sure your user is in the docker group:

# Add your user to the docker group.  You may need to logout/log back in.
sudo usermod -aG docker $USER

Make sure you can run docker's hello world image as your user to verify it is working:

docker run hello-world

Now create a file Dockerfile:

sudo mkdir -p /opt/docker-toolchain/docker-debian-bookworm-armhf
# Use any preferred editor, vim/emacs/nano/etc
sudo nano /opt/docker-toolchain/docker-debian-bookworm-armhf/Dockerfile
# syntax = docker/dockerfile:1.2

FROM debian:bookworm

RUN dpkg --add-architecture armhf

RUN apt-get update && apt-get install -y \
    autogen \
    automake \
    bash \
    bc \
    bison \
    build-essential \
    bzip2 \
    ca-certificates \
    ccache \
    chrpath \
    cpio \
    curl \
    diffstat \
    fakeroot \
    file \
    flex \
    gawk \
    gcc-arm-linux-gnueabihf \
    git \
    gzip \
    kmod \
    libgpiod-dev:armhf \
    libncursesw5-dev \
    libssl-dev \
    libtool \
    libyaml-dev \
    locales \
    lz4 \
    lzop \
    make \
    multistrap \
    ncurses-dev \
    pkg-config \
    python3 \
    python3-cbor \
    python3-pexpect \
    python3-pip \
    qemu-user-static \
    rsync \
    runit \
    socat \
    srecord \
    swig \ 
    texinfo \
    u-boot-tools \
    zstd \
    unzip \
    vim \
    wget \
    xz-utils

# Provide a more friendly name
ENV debian_chroot debian_bookworm
RUN echo "PS1='\${debian_chroot}\\[\033[01;32m\\]@\\H\[\\033[00m\\]:\\[\\033[01;34m\\]\\w\\[\\033[00m\\]\\$ '" >> /etc/bash.bashrc

# Set up locales
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
        echo 'LANG="en_US.UTF-8"'>/etc/default/locale && \
        dpkg-reconfigure --frontend=noninteractive locales && \
        update-locale LANG=en_US.UTF-8
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8

Next make a shell script to enter into this docker container. Create /usr/local/bin/docker-debian-bookworm:

# Use any preferred editor, vim/emacs/nano/etc
sudo nano /usr/local/bin/docker-debian-bookworm
#!/bin/bash -e

# Enters a docker running Debian 12 Bookworm
# Any arguments are run in the docker, or if no arguments it runs a shell

export TAG=debian-bookworm-armdev
SCRIPTPATH=$(readlink -f "$0")
DOCKERPATH=/opt/docker-toolchain/docker-debian-bookworm-armhf/

DOCKER_BUILDKIT=1 docker build --tag "$TAG" "$DOCKERPATH" --quiet

exec docker run --rm \
	-it \
	--volume "$(pwd)":/work \
	--user $(id -g):$(id -u) \
	-w /work \
	-e HOME=/tmp \
	"$TAG" \
	$@;

Make this executable, and call it:

sudo chmod a+x /usr/local/bin/docker-debian-bookworm

# dont run as root
docker-debian-bookworm

The first time this runs it will download a base Debian image, and run the above apt-get commands which may take around 10 or so minutes depending on your internet connection and disk speed. After it has run once, it will stay cached and adds almost no overhead to run.

This docker can be thought of as a very low overhead virtual machine that only has access to the directory where it is run.

For example, to build a simple c project, create a ~/Desktop/hello-world/hello.c:

mkdir -p ~/Desktop/hello-world/

In ~/Desktop/hello-world/hello.c:

#include <stdio.h>

int main() {
    printf("Hello world!\n");
    return 0;
}

We can now use the docker in that directory to use Debian's cross compiler to create a binary that targets armhf:

user@hostname:~$ cd ~/Desktop/hello-world/
user@hostname:~/Desktop/hello-world$ docker-debian-bookworm
sha256:a92e70c3d7346654b34c0442da20ae634901fd25d1a89dd26517e7d1c1d00c47
debian_bookworm@a8ddfa54989f:/work$ ls
hello.c
debian_bookworm@a8ddfa54989f:/work$ arm-linux-gnueabihf-gcc hello.c -o hello
debian_bookworm@a8ddfa54989f:/work$ arm-linux-gnueabihf-strip hello
debian_bookworm@a8ddfa54989f:/work$ file hello
hello: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=ffda981721a1531418ed1da27238707851ae0126, for GNU/Linux 3.2.0, stripped


LEDs

LEDs can be controlled manually by userspace applications once the Linux kernel is booted. LEDs are controllable through the sysfs interface to the kernel. See the platform's hardware manual for information on what LEDs are available in the system and where they are physically located.


More information on the LED system including connecting LEDs, different ways of controlling LEDs, and documentation on various LED triggers can be found in the Linux kernel documentation.


LED sysfs Overview


LEDs appear to the system at /sys/class/leds/<color>:<purpose>/. From there, the three most relevant files are brightness, max_brightness, and trigger. The brightness file is used to manually change the LED state, while trigger can be used to specify automatic triggers for the LED.


Manual LED Control


The brightness file is used to specify a brightness value from 0 to max_brightness; with 0 being off and max_brightness being on at full brightness.

Note: Not all platforms support arbitrary brightness of LEDs. On these platforms, any non-zero value written to brightness will turn the LED on at full brightness.


For example, to turn the green:power LED off and then back on:

root@tsimx6ul:~# echo 0 > /sys/class/leds/green\:power/brightness
root@tsimx6ul:~# echo 255 > /sys/class/leds/green\:power/brightness


LED Triggers


LEDs have an associated trigger file, this can be used to configure the LEDs to blink in various patterns or on certain system activity.

For example, on the TS-7250-V3, the green LED triggers supported by the kernel are:

root@tsimx6ul:~# cat /sys/class/leds/green\:power/trigger 
[none] timer backlight cpu cpu0 default-on panic mmc0 mmc1 rfkill-any rfkill-none rfkill0

The trigger name wrapped in square braces is what is currently enabled.



To set this LED to be triggered based on activity on any CPU:

root@tsimx6ul:~# echo cpu > /sys/class/leds/green\:power/trigger
root@tsimx6ul:~# cat /sys/class/leds/green\:power/trigger 
none timer backlight [cpu] cpu0 default-on panic mmc0 mmc1 rfkill-any rfkill-none rfkill0

The green LED will begin to flicker. By forcing high CPU activity, the green LED will become more solid:

root@tsimx6ul:~# yes >/dev/null
# Press CTRL+C to terminate the above command



To have the LED blink on eMMC activity on the TS-7250-V3:

root@tsimx6ul:~# echo mmc0 > /sys/class/leds/green\:power/trigger
root@tsimx6ul:~# cat /sys/class/leds/green\:power/trigger 
none timer backlight cpu cpu0 default-on panic [mmc0] mmc1 rfkill-any rfkill-none rfkill0

Similarly, creating high disk activity will cause the LED to be on longer and more bright.

Industrial I/O (IIO)

IIO devices appear at /sys/bus/iio/devices/<device>/

Link to more in-depth and agnostic IIO manual for ADC, IMU, and other device monitoring


eMMC Enhanced Modes

eMMC supports enhanced modes, here is how to utilize these.


Character LCD interface

Discuss connecting this to a driver for use without needing to manually manipulate pins.


RTC

Setting the RTC. Using special RTC modes on some platforms like alarm.


GPIO

GPIO - Quick Start

The Linux kernel provides a special character device for GPIO control. Used in conjunction with libgpiod allows for userspace control of GPIO pins that is fast and efficient. Our Debian distribution provides both libgpiod as well as gpiod utilities for manipulating GPIO pins.


See the platform's hardware manual for more information on the GPIO pins available, their physical locations, electrical characteristics, as well as chip and line designations for each GPIO for use with libgpiod and utilities.



Overview of gpiod Concepts


Hardware which provides GPIO usually split them in to banks of pins. These banks often naturally fall on memory access boundaries as the GPIO banks are usually memory mapped in some capacity.

Linux's GPIO driver system does this same logical grouping, but are referred to as chips and lines as opposed to banks and pins. All of the information here will refer to them as chips and lines. Additionally, the Linux kernel's dynamic driver loading order means there is not always a guarantee of chip numbering when multiple drivers are involved. While a single driver may service multiple chips and guarantee those are always in the same order relative to each other; adding more GPIO to the system, via SPI or I2C expanders for example, may cause the newly added GPIO chip(s) to load first causing the other GPIO chips to now be off by one.

In order to combat this, chips are given human readable labels and can be referenced by those as opposed to an arbitrary number. The labels are dynamically created from the devicetree description of the GPIO if a label is not explicitly provided as part of the devicetree. We recommend using these chip labels where possible in order to prevent issues arising from driver load order.

Each gpiod chip contains a number of lines. Lines are created when the driver creates the chip, and lines will always be in the same order in the same chip. Lines can also have labels attached to them. This allows searching for a specific line by label and being able to retrieve its chip and line number.


Listing GPIO Chips


The gpiod tools includes a number of commands to discover and manipulate GPIO pins. The first is gpiodetect. This tool lists all GPIO chips present on the booted system. The chips are listed as:

<chip number> [<chip label>] (<number of lines present in the chip>)


For example, on the TS-7250-V3:

root@tsimx6ul:~# gpiodetect 
gpiochip0 [209c000.gpio] (32 lines)
gpiochip1 [20a0000.gpio] (32 lines)
gpiochip10 [5000406c.fpga_gpio] (16 lines)
gpiochip11 [2-0020] (16 lines)
gpiochip2 [20a4000.gpio] (32 lines)
gpiochip3 [20a8000.gpio] (32 lines)
gpiochip4 [20ac000.gpio] (32 lines)
gpiochip5 [50004010.fpga_gpio] (16 lines)
gpiochip6 [50004040.fpga_gpio] (16 lines)
gpiochip7 [50004054.fpga_gpio] (16 lines)
gpiochip8 [5000405c.fpga_gpio] (16 lines)
gpiochip9 [50004064.fpga_gpio] (16 lines)
Note: The chip labels will be constant while the chip numbering may change. We recommend always using the chip label where possible!


Listing All GPIO Chips and Lines


The gpiod tool gpioinfo lists every chip and each line associated with a chip. There is also significant detail provided for each line that lists: the line number, if it has a name associated with it, which (if any) device/driver has the line currently acquired, and the line settings.


For example, on the TS-7250-V3:

root@tsimx6ul:~# gpioinfo 
gpiochip0 - 32 lines:
...
gpiochip5 - 16 lines:
        line   0:      unnamed       unused   input  active-high 
        line   1:   "DIO_PIN1"       unused   input  active-high 
        line   2:   "DIO_PIN3"       unused   input  active-high 
        line   3:   "DIO_PIN5"       unused   input  active-high 
        line   4:   "DIO_PIN7"       unused   input  active-high 
        line   5:   "DIO_PIN8"       unused   input  active-high 
        line   6:   "DIO_PIN9"       unused   input  active-high 
        line   7:  "DIO_PIN11"       unused   input  active-high 
        line   8:  "DIO_PIN13"       unused   input  active-high 
        line   9:  "DIO_PIN15"       unused   input  active-high
...

Above, gpiochip5 contains 16 lines, 9 of which are used to control pins on the DIO header. The pins are properly named, currently unused, and set as active-high inputs.


Finding GPIO Lines by Name


The gpiofind command is intended to do a reverse lookup of a chip and line when given a line name.


For example, on the TS-7250-V3, searching for the line name DIO_PIN1 will reveal the same chip and line number that can be seen above:

root@tsimx6ul:~# gpiofind "DIO_PIN1"
gpiochip5 1


By design, gpiofind's output can be used as input in to other gpiod commands. For example, to read the state of the pin named DIO_PIN1:

# Leave DIO_PIN1 floating
root@tsimx6ul:~# gpioget $(gpiofind "DIO_PIN1")
1

# Tie DIO_PIN1 to ground
root@tsimx6ul:~# gpioget $(gpiofind "DIO_PIN1")
0


Reading GPIO Line States


The gpiod tool gpioget is used to both set the pin to an input and return the current state of the pin. It can be passed the chip number, gpiochip* number, or the chip label and the line under that chip to return the state of. As above, gpiofind output can also be substituted in the command. For example, on the TS-7250-V3, all of the gpioget commands get the value of the same GPIO line, DIO_PIN1:

root@tsimx6ul:~# gpioget 5 1
1
root@tsimx6ul:~# gpioget gpiochip5 1
1
root@tsimx6ul:~# gpioget 50004010.fpga_gpio 1
1
root@tsimx6ul:~# gpioget $(gpiofind "DIO_PIN1")
1

There are other control options that can be passed to gpioget to change the polarity and/or the bias of the line. These are not applicable to every GPIO controller or driver, however.

Note: When working with lines that have a high capacitive or inductive load on them, gpioget may not reflect the true line state on the first execution. The gpioget command changes the pin to an input state and then reads the value. If a line has a known load on it, a proper delay between setting the pin to an input and reading the pin should be calculated and used.


Modifying GPIO Line States


The gpioset tool is used to both set the line to an output and set its output state in one command. Like gpioget, gpioset can be passed the chip number, gpiochip* number, or the chip label and the line under that chip to change. As above, gpiofind output can also be substituted in the command. For example, on the TS-7250-V3, all of the gpioset commands set the value of the same GPIO line, DIO_PIN1 to logical 1:

root@tsimx6ul:~# gpioset 5 1=1
root@tsimx6ul:~# gpioset gpiochip5 1=1
root@tsimx6ul:~# gpioset 50004010.fpga_gpio 1=1
root@tsimx6ul:~# gpioset $(gpiofind "DIO_PIN1")=1


GPIO - Application Examples

What to do here? maybe not use this heading and use something like "more detailed use" guide?


GPIO - Additional Notes

For more information on libgpiod and all of the userspace utilities for it, see the libgpiod documentation


Power control of peripherals such as USB

memtool

How to directly manipulate memory mapped devices, etc.


hwmon

Talk about things like temperature measurement?


CAN

The Linux kernel and userspace libraries provide support for many CAN protocols and standards, including but not limited to:

  • SAE J1939
  • ISO 15765-2:2016
  • CANopen
  • DeviceNet
  • NMEA 2000
  • UAVCAN


CAN - Quick Start

Our Debian distribution provides ip and can-utils which can be used for quickly bringing up the interfaces and checking them out. See the platform's hardware manual for more information on the number of CAN interfaces, their physical locations, and other details.


SocketCAN compatible interfaces are handled by Linux like other networking interfaces. The ip tool can be used to bring up and configure the CAN interfaces. For example, to bring up can0 on a device and configure it for 1 Mbaud:

root@tsimx6ul:~# ip link set can0 up type can bitrate 1000000

From there, the can-utils tools, cansend and candump, can be used to send simple messages and receive packets from the CAN bus.


For example, on a single platform with two CAN ports, can0 and can1, tie the CAN_L of each port together and CAN_H of each port together (please reference the platform's hardware manual for these pin locations), bring up the two interfaces at the same baud rate, run candump on one of the interfaces and use cansend to send a message over the bus to the other interface. The messages will be echo'ed out to the terminal:

root@tsimx6ul:~# ip link set can0 up type can bitrate 1000000
root@tsimx6ul:~# ip link set can1 up type can bitrate 1000000
root@tsimx6ul:~# candump can0 &
[1] 514
root@tsimx6ul:~# cansend can1 7df#03010c
  can0  7DF   [3]  03 01 0C
root@tsimx6ul:~#


CAN - Application Examples

Blah blah, link to github here.

Other languages have bindings to access CAN such as Python, Java using JNI.


CAN - Additional Notes

See the Kernel's CAN documentation here.

In production use of CAN we also recommend setting a restart-ms for each active CAN port.

root@tsimx6ul:~# ip link set can0 type can restart-ms 100

This allows the CAN bus to automatically recover in the event of a bus-off condition.

SPI / spidev?

Talk about this?


I2C

Talk about i2ctools, detect, etc.


MTD? FRAM? EEPROM?

Not sure how to describe this section, but, most of these will use mtdblock, or eeprom file.


Watchdog

This one is super important to discuss


Sleep modes?

Supervisory microcontroller

This instantly powers off the system


Wake modes

Timer, GPIO, etc.


Suspend to RAM

Does what it says on the tin


Wake modes

Timer/RTC sometimes, touchscreen sometimes, GPIO sometimes, other peripheral configuration