Debian 12
Debian Overview
For development, it is recommended to work directly in Debian on the SD
card. Debian provides many more packages and a much more familiar
environment for users already versed in Debian. Through Debian it is
possible to configure the network, use the apt-get suite to manage
packages, and perform other configuration tasks. Out of the box the
Debian distribution does not have any default username/password set.
The account root is set up with no password configured. It is possible
to log in via the serial console without a password but many services
such as ssh will require a password set or will not allow root login at
all. It is advised to set a root password and create a user account when
the unit is first booted.
Setting up a password for root is only feasible on the uSD image.
It is also possible to cross compile applications. Using a Debian host system will allow for installing a cross compiler to build applications. The advantage of using a Debian host system comes from compiling against libraries. Debian cross platform support allows one to install the necessary development libraries on the host, building the application on the host, and simply installing the runtime libraries on the target device. The library versions will be the same and completely compatible with each other. See the respective Debian cross compiling section for more information.
Getting Started With Debian
This Debian release is available in two image flavors. See the Software Images section for links to the latest images.
| Image | Description |
|---|---|
| Headless |
|
| Minimal |
|
We recommend using the Image Replicator tool for writing images to bootable media on the platform itself. A bootable USB drive can also be prepared from a Linux workstation. If inserted at power-on, USB boot takes priority over the eMMC.
# Replace /dev/sdc with the USB disk from lsblk or dmesg.
sudo sgdisk --zap-all /dev/sdc
sudo sgdisk -n 0:0:0 -t 0:8300 /dev/sdc
sudo mkfs.ext4 /dev/sdc1
sudo mkdir /mnt/usb/
sudo mount /dev/sdc1 /mnt/usb/
sudo tar --numeric-owner -xf <image_file>.tar.xz -C /mnt/usb/
sudo chmod 755 /mnt/usb/
sudo umount /mnt/usb/
The same process can rewrite the eMMC while booted from USB by replacing /dev/sdc with /dev/mmcblk0 and /dev/sdc1 with /dev/mmcblk0p1.
The default login is root with no password.
Configuring the Network
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 predictable 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 - Wi-Fi
DHCP on end0. Create the file /etc/network/interfaces.d/end0 with the contents:
allow-hotplug end0
iface end0 inet dhcp
Static IP on end0. Create the file /etc/network/interfaces.d/end0 with the contents:
allow-hotplug 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.
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 with the contents:
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.
Host a Wi-Fi Access Point
The latest image for this platform as of April 28th, 2022 has known issues with the Wi-Fi driver due to incompatibility with cfg80211 powersave modes.
If using Wi-Fi, it is strongly recommended to bring up the Wi-Fi interface, and then run iw wlan0 set power_save off to disable powersave modes.
This issue will be addressed in future images and has already been addressed in our kernel sources. We will continue to provide updates as we receive them from the Wi-Fi module manufacturer.
This section will discuss setting up the WiFi device as an access point that is bridged to an ethernet port. That is, clients can connect to the AP and will be connected to the ethernet network through this network bridge. The ethernet network must provide a DHCP server; this will be passed through the bridge to WiFi client devices as they connect.
It is also possible to run a DHCP client on the platform itself. In this
case the hostapd.conf file needs to be set up without bridging and a
DHCP server needs to be configured. Refer to
Debian's documentation for more details on DHCP
server configuration.
The 'hostapd' utility is used to manage the access point of the device. This is usually installed by default, but can be installed with:
apt-get update && apt-get install hostapd -y
The install process may start an unconfigured 'hostapd' process.
This process must be killed before moving forward.
Modify the file /etc/hostapd/hostapd.conf to have the following
lines:
ssid=YourWiFiName
wpa_passphrase=Somepassphrase
interface=wlan0
channel=7
driver=nl80211
logger_stdout=-1
logger_stdout_level=2
wpa=2
wpa_key_mgmt=WPA-PSK
Refer to the kernel's hostapd documentation for more wireless configuration options.
The access point can be started and tested by hand:
hostapd /etc/hostapd/hostapd.conf
Systemd auto-start with bridge to eth0
It is possible to configure the auto-start of hostapd through
systemd. The configuration outlined below will set up a bridge with
eth0, meaning the Wi-Fi connection is directly connected to the
ethernet network. The ethernet network is required to have a DHCP server
present and active on it to assign Wi-Fi clients an IP address. This
setup will allow Wi-Fi clients access to the same network as the
ethernet port, and the bridge interface will allow the platform itself
to access the network.
Set up hostapd
First, modify the hostapd configuration to understand the bridge interface:
echo "bridge=br0" >> /etc/hostapd/hostapd.conf
Create the file /etc/systemd/system/hostapd_user.service with the
following contents:
[Unit]
Description=Hostapd IEEE 802.11 AP
Wants=network.target
Before=network.target
Before=network.service
After=sys-subsystem-net-devices-wlan0.device
After=sys-subsystem-net-devices-br0.device
BindsTo=sys-subsystem-net-devices-wlan0.device
BindsTo=sys-subsystem-net-devices-br0.device
[Service]
Type=forking
PIDFile=/run/hostapd.pid
ExecStart=/usr/sbin/hostapd /etc/hostapd/hostapd.conf -P /run/hostapd.pid -B
[Install]
WantedBy=multi-user.target
Then enable this in systemd:
systemctl enable hostapd_user.service
systemctl enable systemd-networkd
Set up bridging
Create the following files with the listed contents.
/etc/systemd/network/br0.netdev
[NetDev]
Name=br0
Kind=bridge
/etc/systemd/network/br0.network
[Match]
Name=br0
[Network]
DHCP=yes
/etc/systemd/network/bridge.network
[Match]
Name=eth0
[Network]
Bridge=br0
Wi-Fi Concurrent Client / Access Point
Configure the WiFi as an access point as above
The channel used for AP must match the channel the STA is using! Be sure to set channel=\... in the above file to a proper channel number.
In order for the concurrent modes to work, a separate virtual wireless device must first be created.
Note that hostapd.conf above lists interface=p2p0, a second interface with this name must be
created:
iw wlan0 interface add p2p0 type managed
The access point can then be started and tested by hand:
hostapd /etc/hostapd/hostapd.conf &
An IP address can be set to p2p0:
ifconfig p2p0 192.168.0.1
From this point, other Wi-Fi clients can connect to the SSID
YourWiFiName with the WPA2 key Somepassphrase with a
static IP in the range of 192.168.0.0/24, and will be able to access the platform at
192.168.0.1
More advanced configurations are also possible, including bridging, routing/NAT, or simply separate networks with the Wi-Fi module connecting to a network and hosting its own private network with DHCP.
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@ts:~# 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-8-dbg - Java runtime based on OpenJDK (debugging symbols)
openjdk-8-demo - Java runtime based on OpenJDK (demos and examples)
openjdk-8-doc - OpenJDK Development Kit (JDK) documentation
openjdk-8-jdk - OpenJDK Development Kit (JDK)
openjdk-8-jdk-headless - OpenJDK Development Kit (JDK) (headless)
openjdk-8-jre - OpenJDK Java runtime, using Hotspot JIT
openjdk-8-jre-headless - OpenJDK Java runtime, using Hotspot JIT (headless)
openjdk-8-jre-zero - Alternative JVM for OpenJDK, using Zero/Shark
openjdk-8-source - OpenJDK Development Kit (JDK) source files
uwsgi-app-integration-plugins - plugins for integration of uWSGI and application
uwsgi-plugin-jvm-openjdk-8 - Java plugin for uWSGI (OpenJDK 8)
uwsgi-plugin-jwsgi-openjdk-8 - JWSGI plugin for uWSGI (OpenJDK 8)
uwsgi-plugin-ring-openjdk-8 - Closure/Ring plugin for uWSGI (OpenJDK 8)
uwsgi-plugin-servlet-openjdk-8 - JWSGI plugin for uWSGI (OpenJDK 8)
java-package - Utility for creating Java Debian packages
In this case, the wanted package will likely be the "openjdk-8-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-8-jre
# More than one package can be installed at a time.
apt-get install openjdk-8-jre nano vim mplayer
For more information on using apt-get refer to Debian's documentation here.
Controlling GPIO
GPIO pins can be interacted with via gpiod tools such as gpioset and gpioget. For example, the En. Relay is chip and line 4 8:
gpioset 4 8=1 # Enable the relay solenoid
gpioset 4 8=0 # Disable the relay solenoid
To read the value of a GPIO line, gpioget can be used, for example to read the state of the Push Switch:
gpioget 2 18
Setting up SSH
To install ssh, install the package as normal with apt-get:
apt-get install openssh-server
Make sure the device is configured on the network and set a password for the remote user. SSH will not allow remote connections without a password or a valid SSH key pair.
passwd root
The default OpenSSH server will not permit root to login via SSH as a security precaution. To allow root to log in via ssh anyway, edit the /etc/ssh/sshd_config file and add the line PermitRootLogin yes in the authentication section. This change will take effect after reboot or after sshd service restart.
After this setup it is now possible to connect from a remote PC supporting SSH. On Linux/OS X this is the "ssh" command, or from Windows using a client such as PuTTY.
If a DNS server is not present on the target network, it is possible to save time at login by adding "UseDNS no" in /etc/ssh/sshd_config.
Starting Applications Automatically
A systemd service can be created to start up various applications. Create the file /etc/systemd/system/yourapp.service with the contents:
[Unit]
Description=Run an application on startup
# Uncomment the following line if networking is a dependency of the application being run
# After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/your_app_or_script
[Install]
WantedBy=multi-user.target
The service can be started immediately and enabled on future boots with the following:
# 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
See the systemd documentation for more information on how systemd services can be set up and configured.
Cross Compiling
Debian Toolchain
Debian provides cross toolchains within their distribution for different architectures.
To see all the options, run
apt search crossbuild
For most systems, the relevant selection would be
apt install crossbuild-essential-armhf
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.
Docker
Ubuntu/Debian:
sudo apt-get install docker.io -y
Fedora
sudo dnf install docker -y
Post Install
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
Dockerfile
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
Dockerfile configuration
# 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
Running the Container
Ensure /usr/local/bin is in your path.
nano ~/.profile
Add the following to the end of the file
# set PATH so it includes the local bin if it exists
if [ -d "/usr/local/bin" ] ; then
PATH="$PATH:/usr/local/bin"
fi
Make sure it is applied (this only needs to be done this once. It will be automatically applied on future logins)
source ~/.profile
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
with the contents:
#!/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 -u):$(id -g) \
-w /work \
-e HOME=/tmp \
"$TAG" \
$@;
Make this executable, and call it:
sudo chmod a+x /usr/local/bin/docker-debian-bookworm
docker-debian-bookworm
Containers should not run as the root user unless absolutely necessary. Running as a non-root user limits the impact of a container compromise and protects the host system
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 file ~/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
Compiling the Kernel
Tools
A compatible armhf cross compiler on the build host is needed for building the kernel. Common options include:
- Docker
- Packaged cross compiler
- Buildroot
While on most platforms the kernel can be downloaded, built, and installed all on the device, we recommend against this due to the amount of time, memory, and disk space that can be needed for a build.
Docker
Instructions to create a container suitable for cross-compiling are included here
Packaged Cross Compiler
On Debian based systems, including Ubuntu:
sudo apt-get install crossbuild-essential-armhf
Install the additional tools required to support kernel development
These prerequisite libraries and tools may not be the complete list, depending on the workstation's distribution and age. It may be necessary to install additional packages to support kernel compilation.
# Install dependencies for kernel build
# The following command is for Ubuntu / Debian workstations. If using a different
# distribution, please consult distribution docs for the proper commands to install
# new packages/tools/libraries/etc.
apt-get install git fakeroot build-essential ncurses-dev xz-utils lzop libssl-dev bc flex libelf-dev bison
Buildroot
It is also possible to use our Buildroot repository to build a compatible cross compiler.
Download and Configure
Create a place to store the kernel:
mkdir -p ~/Projects/tsimx6ul/kernel/
Download the kernel
Download the Linux repository on a host Linux workstation
The Linux versions that are currently supported are:
- 6.18
- 6.12
- 6.6
- 5.10
The branch of linux that you want will be
linux-A.B.y
Where A is the major release and B is the minor version. So to clone the 6.6 release, the branch will be linux-6.6.y and for 5.10 it will be linux-5.10.y
cd ~/Projects/tsimx6ul/kernel/
# Do a shallow clone of the sources
git clone --depth 1 -b linux-6.6.y https://github.com/embeddedTS/linux-lts
cd linux-lts/
Change the value of linux-6.6.y if a different release is required.
Configure
Configure environment variables needed for building. This specifies the architecture, the cross compiler that is being used.
export CROSS_COMPILE=arm-linux-gnueabihf- # This may be different if using a different compiler!
export ARCH=arm
The trailing dash (-) on the CROSS_COMPILE is very important. The Makefiles will not find your compiler if you leave it off.
WILC3000
The WILC3000 Wi-Fi/BLE drivers are maintained and built externally out of the kernel tree.
Clone this tree inside of the linux-lts/ directory (it will be built later as part of the kernel
compilation):
git clone -b linux4microchip-2024.04 https://github.com/embeddedTS/wilc3000-external-module/
Export the environment variable to set up building the kernel modules for the WILC3000 Wi-Fi/BLE module:
export WILC=y
Default Config
Set the default configuration for this platform. Note that a minimal defconfig and a full-feature defconfig are available.
The full defconfig includes much more support for things like USB devices, a more broad range of netfilter/iptables filter module support, etc.
The minimal defconfig contains options for supporting the device and a few common peripherals and technologies.
make tsimx6ul_defconfig
# The minimal defconfig can alternately be used with:
# make tsimx6ul_minimal_defconfig
Build and Install
If you are using Docker, remember to start the container with the script from earlier
docker-debian-bookworm
The following will build the kernel and modules, and install the kernel, modules, and headers to a folder and create a tarball from that. This tarball can be unpacked to bootable media, e.g. microSD, eMMC, USB, etc., to update an existing bootable disk.
The script below is most easily saved as a text file and run from the command line as a script. Most terminal emulators will accept the whole script copy/pasted in to the terminal. But it is also possible to copy paste each line of text in to a terminal. In any case, the following is an example of how to compile the kernel. The script or commands used can be modified as needed to suit a specific build pipeline.
The script assumes the following environment variables are set before it is run. See the above sections for what these variables should be set to for this specific platform.
ARCH
- Used to indicate the target CPU architecture.
CROSS_COMPILE
- Used to point to an appropriate cross toolchain for the target platform.
LOADADDR [Optional]
- Used on some platforms to tell U-Boot where to load the file.
WILC [Optional]
- Set to "y" to build and install the WILC3000 Wi-Fi/BLE external modules.
#!/bin/bash -e
# Always build zImage, most common. If LOADADDR is set, then uImage is also built
TARGETS="zImage"
if [ -n "${LOADADDR}" ]; then TARGETS+=" uImage"; fi
# Build the actual kernel, binary files, and loadable modules.
# Use as many CPUs to do this as possible.
make -j"$(nproc)" && make ${TARGETS} && make modules
# Create a temporary directory to install the kernel to in order to use that as a base directory for a tarball.
# Also creates a temporary file that is used as the tarball name.
TEMPDIR=$(mktemp -d)
TEMPFILE=$(mktemp)
mkdir "${TEMPDIR}/boot/"
# Adds "arch/arm/boot/" path prefix to each TARGET
cp $(for i in ${TARGETS}; do echo arch/arm/boot/$i; done) "${TEMPDIR}"/boot/
# Copy the full .config file to the target, this is optional and can be removed
cp .config "${TEMPDIR}"/boot/config
# Copy all of the generated FDT binary files to the target
find arch/arm/boot/dts -name "*ts*.dtb" -exec cp {} "${TEMPDIR}/boot" \;
# Install kernel modules to the target
INSTALL_MOD_PATH="${TEMPDIR}" make modules_install
# Install kernel headers to the target, this is optional in most cases and can be removed to save space on the target
make headers_install INSTALL_HDR_PATH="${TEMPDIR}"
# If WILC is set to "y", then build the external module for the WILC300 Wi-Fi/BLE device.
# Note that this expects the source to be available as a subfolder in the kernel. See the above sections
# for details on getting the driver source if it is used on this specific platform.
if [ "${WILC}" == "y" ]; then
CONFIG_WILC_SPI=m INSTALL_MOD_PATH="${TEMPDIR}" make M=wilc3000-external-module modules modules_install
fi
# Use fakeroot to properly set permissions on the target folder as well as create a tarball from this.
fakeroot sh -c "chmod 755 ${TEMPDIR};
chown -R root:root ${TEMPDIR};
tar czf ${TEMPFILE}.tar.gz -C ${TEMPDIR} .";
# Create a final output tarball and cleanup all of the temporary files and folder.
cp ${TEMPFILE}.tar.gz embeddedTS-linux-lts-"$(date +"%Y%m%d")"-"$(git describe --abbrev=8 --dirty --always)".tar.gz
rm -rf "${TEMPDIR}" "${TEMPFILE}"
If using the Docker container to cross compile, be sure to exit the container after the build
script completes! The tarball will be located in the linux-lts/ folder that was created.
At this point, the tarball can be unpacked to a bootable media for the device. This can be done from a booted device, or by mounting removable media from a host Linux workstation. For example, if the root folder of the target filesystem to be updated is mounted to /mnt/, the following can be used to unpack the above tarball:
Ensure the target filesystem is mounted to /mnt first!
# Extract kernel tarball to target filesystem,
tar xhf embeddedTS-linux-lts-*.tar.gz -C /mnt
The h argument to tar is necessary on recent distributions that use paths with symlinks. Not
using it can potentially render the whole filesystem no longer bootable.
This will correctly unpack the kernel, modules, and headers to the target filesystem which can then be booted as normal.
Features
ADC
The TS-7250-V3 supports five 12-bit ADC channels through the i.MX6UL ADC. All channels support 0-30 V input, and channels 1-3 can optionally be used as 0-20 mA current loop inputs. The ADC header provides dedicated analog ground pins to reduce noise.
The ADCs are exposed through Linux IIO:
iio_attr -c 2198000.adc voltage0
iio_attr -c 2198000.adc voltage1
iio_attr -c 2198000.adc voltage5
iio_attr -c 2198000.adc voltage8
iio_attr -c 2198000.adc voltage9
| ADC Header Pin | Schematic Name | IIO device | IIO name | Voltage | Current loop |
|---|---|---|---|---|---|
| 1 | AN_CH1 | 2198000.adc | voltage0 | 0-30 V | 0-20 mA |
| 3 | AN_CH2 | 2198000.adc | voltage1 | 0-30 V | 0-20 mA |
| 5 | AN_CH3 | 2198000.adc | voltage5 | 0-30 V | 0-20 mA |
| 7 | AN_CH4 | 2198000.adc | voltage8 | 0-30 V | N/A |
| 9 | AN_CH5 | 2198000.adc | voltage9 | 0-30 V | N/A |
The inputs are rated for 30 V maximum, but the ADC front-end scales 40 V down to the 2.5 V reference. A 30 V input will not read the full-scale 12-bit value.
Current loop mode is controlled with GPIO:
gpioset 20ac000.gpio 7=0 # AN_CH1 voltage
gpioset 20ac000.gpio 8=0 # AN_CH2 voltage
gpioset 20ac000.gpio 9=0 # AN_CH3 voltage
gpioset 20ac000.gpio 7=1 # AN_CH1 current
gpioset 20ac000.gpio 8=1 # AN_CH2 current
gpioset 20ac000.gpio 9=1 # AN_CH3 current
For C applications, libiio provides the fastest API and reaches approximately 6 ksps across all channels. Python bindings currently reach approximately 2 ksps with similar logic.
/* Build with gcc adc-test.c -o adc-test -liio
* Gets ~6 ksps
* At the time of writing this does not support the buffer interface */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <iio.h>
uint32_t scale_mv(uint32_t raw)
{
/* We need to scale for the ADC range, of a 12-bit adc at 0-2500mV, and
* the resistor divider (R1+R2/R2)
* (2500/4095) * ((33000 + 2200)/2200)
* This below is this fraction simplified
*/
return raw * 8000/819;
}
int main(int argc, char **argv)
{
static struct iio_context *ctx;
static struct iio_device *dev;
static struct iio_channel *chn[5];
int i, ret;
long long sample;
ctx = iio_create_default_context();
assert(ctx);
dev = iio_context_find_device(ctx, "2198000.adc");
assert(dev);
chn[0] = iio_device_find_channel(dev, "voltage0", false);
chn[1] = iio_device_find_channel(dev, "voltage1", false);
chn[2] = iio_device_find_channel(dev, "voltage5", false);
chn[3] = iio_device_find_channel(dev, "voltage8", false);
chn[4] = iio_device_find_channel(dev, "voltage9", false);
for (i = 0; i < 5; i++) {
ret = iio_channel_attr_read_longlong(chn[i], "raw", &sample);
assert(!ret);
printf("AN_CH%d_mv=%d\n", i, scale_mv((uint32_t)sample));
}
return 0;
}
#!/usr/bin/env python3
import iio
ctx = iio.Context('local:')
dev = ctx.find_device('2198000.adc')
scan_channels = ["voltage0", "voltage1", "voltage5", "voltage8", "voltage9"]
i = int(0)
for chan_name in scan_channels:
chn = dev.find_channel(chan_name)
raw = int(chn.attrs['raw'].value)
# Scale 0-4095 to 0-2500(mV)
scaled = raw * (2.5/4095)
# Scale voltage divider on the pin
r1 = 330
r2 = 22
v = scaled / (r2 / (r1 + r2))
i += 1
print('AN_CH{}_V={:.3f}'.format(i, v))
Bluetooth
On many boards, support for Bluetooth is provided by the BlueZ project. BlueZ supports different profiles for HID, A2DP, and more. Refer to the BlueZ documentation for more information. Please see our BLE Examples page for information on installing BlueZ from source, getting started, and using demo applications.
BlueZ Bluetooth modules can be activated with the following commands:
echo BT_POWER_UP > /dev/wilc_bt
sleep 1
echo BT_DOWNLOAD_FW > /dev/wilc_bt
sleep 1
btattach -N -B /dev/ttymxc2 -S 115200 &
sleep 1
bluetoothctl power on
sleep 1
hcitool cmd 0x3F 0x0053 00 10 0E 00 01
kill %1 # This terminates the above btattach command
sleep 1
btattach -B /dev/ttymxc2 -S 921600 &
At this point, the device is fully set up to be controlled by various BlueZ tools. For example, to do a scan of nearby devices:
bluetoothctl
power on
scan on
This will return a list of devices such as:
root@ts-imx6ul:~# bluetoothctl
Agent registered
[CHG] Controller F8:F0:05:XX:XX:XX Pairable: yes
[bluetooth]# power on
Changing power on succeeded
[CHG] Controller F8:F0:05:XX:XX:XX Powered: yes
[bluetooth]# scan on
Discovery started
[CHG] Controller F8:F0:05:XX:XX:XX Discovering: yes
[NEW] Device 51:DD:C0:XX:XX:XX Device_Name
[NEW] Device 2A:20:E2:XX:XX:XX Device_Name
[CHG] Device 51:DD:C0:XX:XX:XX RSSI: -93
[CHG] Device 51:DD:C0:XX:XX:XX RSSI: -82
[NEW] Device E2:08:B5:XX:XX:XX Device_Name
[CHG] Device 51:DD:C0:XX:XX:XX RSSI: -93
[CHG] Device 2A:20:E2:XX:XX:XX RSSI: -94
[NEW] Device 68:62:92:XX:XX:XX Device_Name
[NEW] Device 68:79:12:XX:XX:XX Device_Name
[bluetooth]# quit
Other supported commands include:
# Allow the BT chip to enter sleep mode
echo BT_FW_CHIP_ALLOW_SLEEP > /dev/wilc_bt
# Power down the BT radio when not in use
echo BT_POWER_DOWN > /dev/wilc_bt
CAN
The TS-7250-V3 CPU has two FlexCAN ports using Linux SocketCAN. These are available on the COM3 header.
ip link set can0 up type can bitrate 1000000
ip link set can1 up type can bitrate 1000000
At this point, applications can use standard SocketCAN APIs. Debian images include cansend and candump:
candump can0
cansend can0 7Df#03010c
In production use, set a restart timeout so the CAN bus can recover automatically from a bus-off condition:
ip link set can0 type can restart-ms 100
ip link set can1 type can restart-ms 100
CPU
This device uses the NXP i.MX6UL CPU, running at up to 696 MHz, based on an Arm Cortex-A7 core and targeting low power consumption.
Refer to NXP's i.MX6UL documentation for detailed CPU information.
GPIO
GPIO pins can be interacted with via gpiod tools such as gpioset and gpioget. For example, the En. Relay is chip and line 4 8:
gpioset 4 8=1 # Enable the relay solenoid
gpioset 4 8=0 # Disable the relay solenoid
To read the value of a GPIO line, gpioget can be used, for example to read the state of the Push Switch:
gpioget 2 18
eMMC Interface
Our default programming of the eMMC is the same as the SD card image for standard partitions.
The default software image contains 3 partitions:
| Device | Contents |
|---|---|
| /dev/mmcblk1 | Full eMMC block device |
| /dev/mmcblk1boot0 | eMMC boot partition |
| /dev/mmcblk1boot1 | eMMC boot partition |
| /dev/mmcblk1p1 | Full Debian linux partition |
This platform includes an eMMC device, a soldered down MMC flash device. Our off the shelf builds are 4 GB, but up to 64 GB are available for customized builds. The eMMC flash appears to Linux as an SD card at /dev/mmcblk1. Our default programming of the eMMC is the same as the SD card image for standard partitions, but includes additional boot partitions that are used by U-Boot and are not affected by the eMMC partition table.
The eMMC module has a similar concern by default to SD cards in that they should not be powered down during a write/erase cycle. However, this eMMC module includes support for setting a fuse for a "Write Reliability" mode, and a "psuedo SLC (pSLC)" mode. With both of these enabled all writes will be atomic to 512 B and each NAND cell will be treated as a single layer rather than a multi-layer cell. If a sector is being written during a power loss, a block is guaranteed to have either the old or new data. Even in cases where the wrong data is present on the next boot, fsck is often able to deal with the older data being present in a 512 B block. The downsides to setting these modes are that it will reduce the overall write speed and halve the available space on the eMMC to roughly 1.759 GB. Please note that even with these settings, Technologic Systems strongly recommends designing the end application to eliminate any situations where a power-loss event can occur while any disk is mounted as read/write. The TS-SILO option can help to eliminate the dangerous situation.
The "mmc-utils" package is used to enable these modes. The command is pre-installed on the latest image. Additionally we have created a script to safely enable the write reliability and pSLC modes. Since the U-Boot binary and environment reside on the eMMC, care must be taken to save the current state of the boot partitions, enable the modes, restore the boot partitions, and re-enable proper booting options. This script can be used in combination with the production mechanism scripting to complete these steps as part of an end application production process.
Enabling these modes causes all data on the disk to become invalid and must be rewritten. Do not attempt to run the 'mmc' commands from the script individually, all steps in the script must occur as they are or the unit may be unable to boot. If there are any failures of the script, care must be taken to resolve any issues while the unit is still booted or it may fail to boot in the future.
The script is only compatible with Rev. D or newer PCBs. Running the script on any previous PCB revision WILL result in the unit being unable to boot! There is no safe way to enable these modes on previous PCB revisions.
Enabling these modes is a one-way operation, it is not possible to undo them once they are made. Because of this, setting these eMMC modes will invalidate Technologic Systems' return/replacement warranty on the unit. See the warranty section for more information on this.
The emmc_reliability script can be found in the TS-7553-V2 utilities github repository.
The script must be run when boot from any media other than eMMC, such as SD, NFS, or USB. No partition of the eMMC disk can be mounted when these commands are run. Doing so may result in corruption or inability for the unit to boot. Once the pSLC mode is enabled, all data on the disk will become invalid. This means the partition table will need to be re-created, the filesystems formatted, and all filesystem contents re-written to disk. This is why we recommend using this script in conjunction with the production mechanism scripting. The emmc_reliability script can be run first, then the rest of the production script can create and format the partitions as well as write data to disk.
The script requires a single argument, the device node of the eMMC disk, and will output verbosely to stderr. Any specific errors will also be printed out on stderr.
Example usage:
./emmc_reliability /dev/mmcblk1
Upon successful run, the script will return 0. Any errors will return a positive code. See the script for detailed error code information.
Ethernet Ports
The NXP processor implements a 10/100 ethernet controller with support built into the Linux kernel. Standard Linux utilities such as ifconfig/ip can be used to control this interface. See the Configuring the Network section for more details. For the specifics of this interface see the CPU manual.
FEC PTP Support
PTP is supported in Linux via the linuxptp project. This allows synchronizing the system clock to within ±1 us.
Note that Linux kernel version 4.9 or greater is required for PTP support with the i.MX6UL CPU. An example of setting up an ethernet interface with PTP and adjusting the clock based on that is below.
apt-get install linuxptp -y
# For PTP on eth0
phc2sys -s /dev/ptp0 -w &
ptp4l -2 -H -i eth0 -m -p /dev/ptp0 &
# For PTP on eth1
phc2sys -s /dev/ptp1 -w &
ptp4l -2 -H -i eth1 -m -p /dev/ptp1 &
If the clocks are significantly off this may take time for the clocks to converge.
FPGA
The TS-7250-V3 FPGA provides board-specific I/O including GPIO banks, 16550-compatible UARTs, SPI, I2C, PWM, ADC support, interrupts, and the PC/104 ISA interface.
The FPGA is memory mapped at 0x50000000. The kernel drivers expose the supported interfaces through standard Linux APIs where possible, which is the recommended access method for applications.
Useful top-level regions include:
| Region | Description |
|---|---|
0x50000000 | FPGA top-level/syscon region |
0x50000100 | 16550-compatible UART region |
0x50000180 | FPGA SPI controller region |
0x50004010 and later | FPGA GPIO banks |
0x50004050 | PC/104 ISA access window |
The syscon register at offset 0x08 controls several muxes, including mikroBUS GPIO mode, PC/104 GPIO mode, DIO SPI GPIO mode, and the optional DIO UARTs. Prefer tshwctl for syscon access:
# Read a syscon register
tshwctl --address 0x08 --peek32
# Enable the DIO header UART muxes
tshwctl --address 0x08 --poke32 0x6000
PC/104 Bus
The TS-7250-V3 includes an ISA bus for compatibility with PC/104 peripherals. User space can access the bus using files exposed by the fpgaisa driver:
/sys/bus/platform/devices/50004050.fpgaisa/
| File | Description |
|---|---|
io8 | 8-bit strobes on IOR/IOW |
io16 | 16-bit strobes on IOR/IOW |
ioalt16 | 16-bit strobes on IOR/IOW with alternate pinout |
mem8 | 8-bit strobes on MEMR/MEMW |
mem16 | 16-bit strobes on MEMR/MEMW |
memalt16 | 16-bit strobes on MEMR/MEMW with alternate pinout |
For 16-bit accesses, addresses must be aligned to an even byte and reads/writes must use multiples of two bytes.
The pc104_peekpoke utility can be used from a shell:
Usage pc104_peekpoke <io/mem> <8/16/alt16> <address> [value]
Eg: pc104_peekpoke io 8 0x140
PC/104 cycles are approximately 1 us per access on either 8-bit or 16-bit accesses.
FRAM
The TS-7250-V3 supports an optional Fujitsu MB85RS16N FRAM device. This is a 2 KiB non-volatile SPI memory with very high write endurance and long data retention.
Linux exposes the FRAM as a flat EEPROM file:
/sys/bus/spi/devices/spi4.1/eeprom
FRAM is not present on all TS-7250-V3 configurations.
I2C
The i.MX6UL supports I2C at 100 kHz or 400 kHz fast mode. This platform uses two CPU I2C buses for onboard devices and one FPGA I2C bus for off-board mikroBUS use.
| Device | Address | Description |
|---|---|---|
/dev/i2c-0 | 0x1e | Magnetometer |
/dev/i2c-0 | 0x54 | Supervisory microcontroller on early hardware |
/dev/i2c-0 | 0x68 | RTC |
/dev/i2c-0 | 0x6a | IMU |
/dev/i2c-1 | 0x20 | NXP PCA9555 GPIO expander, chip 2-0020 |
/dev/i2c-1 | 0x64 | ATSHA204, not populated by default |
/dev/i2c-4 | N/A | mikroBUS header |
IMU
Accelerometer (ST ISM330)
This platform features an ST ism330dhcx accelerometer / gyroscope. The accelerometer has an acceleration range of ±2/±4/±8/±16 g.
The accelerometer is accessed through IIO with channels:
- accel_x
- accel_y
- accel_z
- timestamp
For example:
# ISM330DHCX
iio_attr -c ism330dhcx_accel accel_x
iio_attr -c ism330dhcx_accel accel_y
iio_attr -c ism330dhcx_accel accel_z
The below examples will be written for the ism330dhcx_accel, but if this fails instead use the ism330dlc_accel device. These commands will provide a single sample of all of the values:
root@tsimx6ul:~# iio_attr -c ism330dhcx_accel accel_x
dev 'ism330dhcx_accel', channel 'accel_x' (input), attr 'injection_raw', ERROR: Permission denied (-13)
dev 'ism330dhcx_accel', channel 'accel_x' (input), attr 'raw', value '-183'
dev 'ism330dhcx_accel', channel 'accel_x' (input), attr 'scale', value '0.000598'
dev 'ism330dhcx_accel', channel 'accel_x' (input), attr 'scale_available', value '0.000598 0.001196 0.002392 0.004785'
root@tsimx6ul:~# iio_attr -c ism330dhcx_accel accel_y
dev 'ism330dhcx_accel', channel 'accel_y' (input), attr 'injection_raw', ERROR: Permission denied (-13)
dev 'ism330dhcx_accel', channel 'accel_y' (input), attr 'raw', value '-292'
dev 'ism330dhcx_accel', channel 'accel_y' (input), attr 'scale', value '0.000598'
dev 'ism330dhcx_accel', channel 'accel_y' (input), attr 'scale_available', value '0.000598 0.001196 0.002392 0.004785'
root@tsimx6ul:~# iio_attr -c ism330dhcx_accel accel_z
dev 'ism330dhcx_accel', channel 'accel_z' (input), attr 'injection_raw', ERROR: Permission denied (-13)
dev 'ism330dhcx_accel', channel 'accel_z' (input), attr 'raw', value '16491'
dev 'ism330dhcx_accel', channel 'accel_z' (input), attr 'scale', value '0.000598'
dev 'ism330dhcx_accel', channel 'accel_z' (input), attr 'scale_available', value '0.000598 0.001196 0.002392 0.004785'
To get the real world value, multiply the scale * the raw value. In this case:
- X: -0.109434 g
- Y: -0.174616 g
- Z: 9.861618 g
The default scale is ±2, but ±2/±4/±8/±16 can be selected by setting the scale:
dev 'ism330dhcx_accel', channel 'accel_z' (input), attr 'scale', value '0.000598'
dev 'ism330dhcx_accel', channel 'accel_z' (input), attr 'scale_available', value '0.000598 0.001196 0.002392 0.004785'
To set ±4, you would write the second available scale:
iio_attr -c ism330dhcx_accel accel_x scale 0.001196
The scale values are not independent on this device, and setting x/y/z will set the scale for all 3.
This driver also supports pulling continuous samples using the buffer interface. These can be accessed using iio_readdev:
iio_readdev ism330dhcx_accel -T 0 -s 128 > samples.bin
The format of this file is specified with iio_attr:
root@tsimx6ul:~# iio_attr -c ism330dhcx_accel
dev 'ism330dhcx_accel', channel 'accel_x' (input, index: 0, format: le:S16/16>>0), found 4 channel-specific attributes
dev 'ism330dhcx_accel', channel 'accel_y' (input, index: 1, format: le:S16/16>>0), found 4 channel-specific attributes
dev 'ism330dhcx_accel', channel 'accel_z' (input, index: 2, format: le:S16/16>>0), found 4 channel-specific attributes
dev 'ism330dhcx_accel', channel 'timestamp' (input, index: 3, format: le:S64/64>>0), found 0 channel-specific attributes
The samples are padded to the nearest 8-bytes, so this means the binary format is:
| Bits | Description |
|---|---|
| 15:0 | accel_x, little endian, signed |
| 15:0 | accel_y, little endian, signed |
| 15:0 | accel_z, little endian, signed |
| 63:0 | timestamp, little endian, signed |
| 15:0 | Padding |
The unix utility hexdump supports formatting options which can parse these fields:
root@tsimx6ul:~# hexdump samples.bin --format '1/2 "X:%d " 1/2 "Y:%d " 1/2 "Z:%d " 1/8 "TS:%d" 1/2 "" "\n"' | head -n 4
X:-95 Y:-163 Z:8221 TS:200185381271666439
X:-107 Y:-147 Z:8248 TS:200190332264480519
X:-100 Y:-155 Z:8263 TS:200195283888013063
X:-95 Y:-159 Z:8253 TS:200200232540667655
This gives the raw values which can then be multiplied by the scale to get the real world value.
The IIO library can also be used to fill buffers with samples for processing. For example:
#!/usr/bin/env python3
import struct
import iio
ctx = iio.Context('local:')
ctx.set_timeout(0)
dev = ctx.find_device('ism330dhcx_accel')
with open(f'/sys/bus/iio/devices/{dev.id}/sampling_frequency', 'w') as f:
f.write(f"833.000")
for chan_name in ["accel_x", "accel_y", "accel_z"]:
chn = dev.find_channel(chan_name)
chn.enabled = True
# We will request 64 samples at a time
buffer = iio.Buffer(dev, 64, False)
# sample size (3x 16-bit signed data)
sample_size = 6
# Refill and process the buffer
buffer.refill()
data = buffer.read()
for i in range(0, len(data), sample_size):
if i + sample_size <= len(data):
x, y, z = struct.unpack('<hhh', data[i:i+sample_size])
print(f' accel_x={x}, accel_y={y}, accel_z={z}')
for chn in dev.channels:
chn.enabled = False
This can also be done using the C library:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iio.h>
#define NUM_CHANNELS 3
#define SAMPLE_SIZE 6 // 3x 16-bit signed data (2 bytes per axis)
void process_samples(struct iio_buffer *buffer, size_t sample_size) {
char *data = iio_buffer_start(buffer);
size_t buffer_size = iio_buffer_end(buffer) - iio_buffer_start(buffer);
int16_t x, y, z;
for (size_t i = 0; i < buffer_size; i += sample_size) {
memcpy(&x, &data[i], sizeof(x));
memcpy(&y, &data[i + sizeof(x)], sizeof(y));
memcpy(&z, &data[i + 2 * sizeof(x)], sizeof(z));
printf("accel_x=%d, accel_y=%d, accel_z=%d\n", x, y, z);
}
}
int main() {
struct iio_context *ctx;
struct iio_device *dev;
struct iio_channel *channels[NUM_CHANNELS];
struct iio_buffer *buffer;
const char *channel_names[NUM_CHANNELS] = { "accel_x", "accel_y", "accel_z" };
// Create context and find device
ctx = iio_create_local_context();
if (!ctx || !(dev = iio_context_find_device(ctx, "ism330dhcx_accel")) &&
!(dev = iio_context_find_device(ctx, "ism330dlc_accel"))) {
fprintf(stderr, "Unable to create context or find device\n");
iio_context_destroy(ctx);
return 1;
}
// Enable channels and set sampling frequency
for (int i = 0; i < NUM_CHANNELS; i++) {
channels[i] = iio_device_find_channel(dev, channel_names[i], false);
if (!channels[i] || iio_channel_attr_write(channels[i], "sampling_frequency", "833.000") < 0) {
fprintf(stderr, "Unable to find or configure channel %s\n", channel_names[i]);
iio_context_destroy(ctx);
return 1;
}
iio_channel_enable(channels[i]);
}
// Create buffer and process samples
buffer = iio_device_create_buffer(dev, 64, false);
if (!buffer || iio_buffer_refill(buffer) < 0) {
fprintf(stderr, "Unable to create or refill buffer\n");
iio_context_destroy(ctx);
return 1;
}
process_samples(buffer, SAMPLE_SIZE);
// Cleanup
iio_buffer_destroy(buffer);
iio_context_destroy(ctx);
return 0;
}
Gyroscope (ST ISM330)
This platform features an ST ism330dhcx accelerometer / gyroscope. The gyroscope has a selectable angular range of ±125/±250/±500/±1000/±2000 dps
The gyroscope is accessed through IIO with channels:
- anglvel_x
- anglvel_y
- anglvel_z
- timestamp
Magnetometer (ST IIS2MDCTR)
This board includes an ST IIS2MDCTR 3-axis magnetometer, which has a magnetic field dynamic range of ±50 gauss (16 bits of precision at up to 150 Hz).
The magnetometer is accessed through Linux's industrial I/O (IIO) subsystem as iis2mdc with channels:
- magn_x
- magn_y
- magn_z
- timestamp
For example:
root@tsimx6ul:~# iio_attr -c iis2mdc -c magn_x
dev 'lis2mdl_magn', channel 'magn_x' (input), attr 'raw', value '630'
dev 'lis2mdl_magn', channel 'magn_x' (input), attr 'scale', value '0.001500'
root@tsimx6ul:~# iio_attr -c iis2mdc -c magn_y
dev 'lis2mdl_magn', channel 'magn_y' (input), attr 'raw', value '-165'
dev 'lis2mdl_magn', channel 'magn_y' (input), attr 'scale', value '0.001500'
root@tsimx6ul:~# iio_attr -c iis2mdc -c magn_z
dev 'lis2mdl_magn', channel 'magn_z' (input), attr 'raw', value '9'
dev 'lis2mdl_magn', channel 'magn_z' (input), attr 'scale', value '0.001500'
The 5.10 LTS kernel did not have the same "iis2mdc" driver, but uses a drop in under the name "lis2mdl". use this as the device name on 5.10 instead.
This shows a snapshot of the x, y, z values. To get the measured field strength along each axis, multiply the scale by the raw value to get the actual reading in milligauss. In the above example:
- X: 0.945 mG (milligauss)
- Y: -0.2475 mG
- Z: 0.0135 mG
Interrupts
This section is incomplete in the upstream manual at this time.
LEDs
LEDs can be manipulated from userspace using the LED sysfs interface. The LEDs have 4 behaviors from default software:
| Green Behavior | Red behavior | Meaning |
|---|---|---|
| Solid On | Off | The kernel has booted and the system is running. |
| Off | Solid On | The unit has powered on and is in the bootloader. |
| On for 10s, off for 100ms, and repeating | On for 10s, off for 100ms, and repeating | The watchdog is continuously resetting the board. This happens when the system cannot find a valid boot device, or the watchdog is otherwise not being fed. This is normally fed by the kernel once a valid boot media has started. See the Watchdog Timer section for more details. |
| Off | Off | The device is unable to boot. Typically either it is not being supplied with enough voltage, or the unit has been otherwise damaged. If a stable voltage is being provided and the supply is capable of providing at least 1A to the unit, an RMA is suggested. |
| Off | Blinking about 5ms on, about 10ms off. | The device is receiving too little power, or something is drawing too much current from the unit's power rails causing the unit to reboot consistently. |
The red and green LEDs can be controlled from userspace after bootup using the sysfs LED interface. For example, to turn on the red LED:
echo 1 > /sys/class/leds/red-led/brightness
A number of triggers are also available, including timers, disk activity, and heartbeat. These allow the LEDs to represent various system activities as they occur. See the kernel LED documentation for more information on triggers and general use of LED class devices.
We also use the LED control system to control a number of DIO pins which need to have their default state specified. See the DIO section for more information on this.
MicroSD Interface
The i.MX6UL SD card controller internal to the CPU provides support for microSD cards and is fully compliant with the SD specification. This controller has been tested with Sandisk Extreme SD cards which allow read speeds up to 20.5MB/s, and write speeds up to 21.5MB/s.
Our default software image contains a single partition:
| Device | Contents |
|---|---|
| /dev/mmcblk0 | SD Card block device |
| /dev/mmcblk0p1 | Full Debian linux partition |
Supervisory Microcontroller
The supervisory features are supported in Linux kernel 5.10 and later, and on PCB revision C and later.
The TS-7250-V3 includes a preprogrammed supervisory microcontroller that manages the SBC power state, RTC, reset controller, selected ADC rails, and USB console functionality.
The console routing can be inspected from sysfs:
cat /sys/bus/i2c/devices/0-0010/console_cfg
| Setting | Description |
|---|---|
auto | Console routes to DB9 by default, but routes to USB when a microUSB cable is connected |
always-usb | Console is only available on USB and does not route to DB9 |
The setting is persistent:
echo "always-usb" > /sys/bus/i2c/devices/0-0010/console_cfg
Any time the console is routed to USB instead, ttyS8 is routed to the DB9 port for application use.
SPI
The TS-7250-V3 FPGA includes two OpenCores SPI controllers. Under Linux these are spi4 and spi5.
| Controller | Chip select | Device |
|---|---|---|
spi4 | 0 | /dev/spidev4.0, DIO header SPI bus |
spi4 | 1 | FRAM |
spi4 | 2 | FPGA flash, /dev/mtdblock0 |
spi5 | 0 | /dev/spidev5.0, mikroBUS SPI |
Do not manipulate the FPGA flash unless instructed by embeddedTS support. Erasing it can require an RMA to recover.
The /dev/spidev* devices can be accessed from Linux using the kernel spidev API. Bindings are available for C, Python, Rust, and JavaScript.
UARTs
The TS-7250-V3 includes CPU UARTs and FPGA 16550A-compatible UARTs.
| UART device | Type | TX / + location | RX / - location | CTS | RTS | DCD | DTR | TXEN |
|---|---|---|---|---|---|---|---|---|
ttymxc0 | USB console | P2 MicroUSB | P2 MicroUSB | N/A | N/A | N/A | N/A | N/A |
ttymxc2 | Bluetooth | Onboard | Onboard | N/A | N/A | N/A | N/A | N/A |
ttymxc3 | 3.3 V TTL | XBEE pin 3 | XBEE pin 2 | XBEE pin 12 | N/A | N/A | N/A | N/A |
ttyS8 | RS-232 | DB9 pin 3 | DB9 pin 2 | DB9 pin 8 | DB9 pin 7 | DB9 pin 1 | DB9 pin 4 | N/A |
ttyS9 | RS-232 | COM2 pin 3 | COM2 pin 2 | COM2 pin 8 | COM2 pin 7 | N/A | N/A | N/A |
ttyS10 | RS-232 | COM3 pin 3 | COM3 pin 2 | COM3 pin 8 | COM3 pin 7 | N/A | N/A | N/A |
ttyS11 | RS-485 | COM2 pin 1 | COM2 pin 6 | N/A | N/A | N/A | N/A | N/A |
ttyS12 | RS-485 | COM2 pin 4 | COM2 pin 9 | N/A | N/A | N/A | N/A | N/A |
ttyS13 | TTL | mikroBUS pin 13 | mikroBUS pin 14 | N/A | N/A | N/A | N/A | N/A |
ttyS14 | TTL | DIO pin 5 | DIO pin 7 | N/A | N/A | N/A | N/A | DIO pin 13 |
ttyS15 | TTL | DIO pin 9 | DIO pin 11 | N/A | N/A | N/A | N/A | DIO pin 15 |
The DIO header UARTs are disabled by default. Enable the mux before using ttyS14 and ttyS15:
tshwctl --address 0x08 --poke32 0x6000
USB
The TS-7250-V3 offers two USB 2.0 host ports. The USB A host stack can provide 1 A total power shared between the two ports.
The lower USB host port can be routed to the XBEE header for USB cellular modems:
# Route USB to XBEE
gpioset 209c000.gpio 11=1
# Route USB to the lower Type-A host port, the default
gpioset 209c000.gpio 11=0
USB host power can be controlled to save power or reboot peripherals:
gpioset 20a4000.gpio 0=0 # Turn off USB power
gpioset 20a4000.gpio 0=1 # Turn on USB power
Watchdog
The board implements a WDT inside the supervisory microcontroller. A standard kernel WDT driver is in place that feeds the WDT via the I2C bus. As soon as the kernel starts it will start the WDT and feed it on 30 second timeouts every 15 seconds. If a userspace application opens and uses the watchdog file the kernel will stop auto-feeding and the user application is now responsible for feeding the WDT. The kernel driver supports the "Magic Close" feature of the WDT. This means that a V character must be fed in to the watchdog file before the file is closed in order to disable the WDT. If this does not happen then the WDT is not stopped and it will continue it's countdown. Additionally, if the kernel is compiled with CONFIG_WATCHDOG_NOWAYOUT then the WDT can never be stopped once it is started at boot.
See the Linux WDT API documentation for more information.
WiFi
This board uses an ATWILC3000-MR110CA IEEE 802.11 b/g/n Link Controller Module With Integrated Bluetooth® 4.0. Linux provides support for this module using the wilc3000 driver.
Summary features:
- IEEE 802.11 b/g/n RF/PHY/MAC SOC
- IEEE 802.11 b/g/n (1x1) for up to 72 Mbps PHY rate
- Single spatial stream in 2.4GHz ISM band
- Integrated PA and T/R Switch Integrated Chip Antenna
- Superior Sensitivity and Range via advanced PHY signal processing
- Advanced Equalization and Channel Estimation
- Advanced Carrier and Timing Synchronization
- Wi-Fi Direct and Soft-AP support
- Supports IEEE 802.11 WEP, WPA, and WPA2 Security
- Supports China WAPI security
- Operating temperature range of -40°C to +85°C
External Interfaces
| Interface | Connector type | Description | Reference |
|---|---|---|---|
| ADC | 2 x 5 pin header | Five 0-30 V ADC channels, three optional 0-20 mA current loops | Reference |
| Battery | Vertical coin cell holder | RTC backup battery | Reference |
| COM2 | 2 x 5 pin header | RS-485, RS-422, and RS-232 | Reference |
| COM3 | 2 x 5 pin header | CAN and RS-232 | Reference |
| DB9 | DE-9 connector | Full-handshake RS-232 | Reference |
| DIO | 2 x 8 pin header | SPI, GPIO, optional DIO UARTs | Reference |
| LCD | 2 x 7 pin header | HD44780-compatible LCD interface | Reference |
| mikroBUS | 2 x 8 pin header | Mikroe Click board socket | Reference |
| MicroUSB | MicroUSB | USB console | Reference |
| PC/104 | 104-pin PC/104 header | ISA-compatible PC/104 bus | Reference |
| Power | Removable terminal blocks | 5 VDC and 8-48 VDC inputs | Reference |
| USB | Dual USB Type-A | USB 2.0 host ports | Reference |
| XBEE | 2 x 10 pin header | Digi XBEE / NimbeLink-style socket | Reference |
ADC Header
The ADC header supports five 0-30 VDC ADC channels. Channels 1-3 also support 0-20 mA current loop sampling.
iio_attr -c 2198000.adc voltage0
iio_attr -c 2198000.adc voltage1
iio_attr -c 2198000.adc voltage5
iio_attr -c 2198000.adc voltage8
iio_attr -c 2198000.adc voltage9
| Pin | Signal |
|---|---|
| 1 | 2198000.adc/voltage0 |
| 2 | GND |
| 3 | 2198000.adc/voltage1 |
| 4 | GND |
| 5 | 2198000.adc/voltage5 |
| 6 | GND |
| 7 | 2198000.adc/voltage8 |
| 8 | GND |
| 9 | 2198000.adc/voltage9, WAKE_UP# |
| 10 | GND |
Battery Connector
The battery connector holds a removable CR1632 coin cell for the battery-backed RTC. Insert the cell from the top of the holder with the positive lead oriented as marked on the board.
COM2 Header
The COM2 header is a 0.1 inch pitch 2 x 5 header supporting RS-485, RS-422, and RS-232.
| Pin | Signal |
|---|---|
| 1 | ttyS11 RS-485+ |
| 2 | ttyS9 RS-232 RXD |
| 3 | ttyS9 RS-232 TXD |
| 4 | ttyS12 RS-485+ |
| 5 | GND |
| 6 | ttyS11 RS-485- |
| 7 | ttyS9 RS-232 RTS |
| 8 | ttyS9 RS-232 CTS |
| 9 | ttyS12 RS-485- |
| 10 | NC |
COM3 Header
The COM3 header is a 0.1 inch pitch 2 x 5 header supporting CAN and RS-232.
| Pin | Signal |
|---|---|
| 1 | CAN2_H |
| 2 | ttyS10 RS-232 RXD |
| 3 | ttyS10 RS-232 TXD |
| 4 | CAN1_H |
| 5 | GND |
| 6 | CAN2_L |
| 7 | ttyS10 RS-232 RTS |
| 8 | ttyS10 RS-232 CTS |
| 9 | CAN1_L |
| 10 | NC |
DB9 Connector
The DB9 connector provides a full-handshake RS-232 port.
| Pin | Signal |
|---|---|
| 1 | ttyS8 RS-232 DCD |
| 2 | ttyS8 RS-232 RXD |
| 3 | ttyS8 RS-232 TXD |
| 4 | ttyS8 RS-232 DTR |
| 5 | GND |
| 6 | ttyS8 RS-232 DSR |
| 7 | ttyS8 RS-232 RTS |
| 8 | ttyS8 RS-232 CTS |
| 9 | ttyS8 RS-232 RI |
DIO Header
The DIO header is a 0.1 inch pitch 2 x 8 header with SPI, GPIO, and optional DIO UART functions. All pins are 5 V tolerant except SPI output pins. SPI input pins are 5 V tolerant.
| Pin | IO Type | Signal |
|---|---|---|
| 1 | FPGA 3.3-V LVTTL+QS3861 | GPIO chip 50004010.fpga_gpio IO 1 |
| 2 | GND | |
| 3 | FPGA 3.3-V LVTTL+QS3861 | GPIO chip 50004010.fpga_gpio IO 2 |
| 4 | Open drain | Current sink output, chip 209c000.gpio IO 30 |
| 5 | FPGA 3.3-V LVTTL+QS3861 | GPIO chip 50004010.fpga_gpio IO 3 / ttyS14 TX |
| 6 | FPGA 3.3-V LVTTL | spidev4.0 CS / GPIO chip 50004010.fpga_gpio IO 11 |
| 7 | FPGA 3.3-V LVTTL+QS3861 | GPIO chip 50004010.fpga_gpio IO 4 / ttyS14 RX |
| 8 | FPGA 3.3-V LVTTL+QS3861 | GPIO chip 50004010.fpga_gpio IO 5 |
| 9 | FPGA 3.3-V LVTTL+QS3861 | GPIO chip 50004010.fpga_gpio IO 6 / ttyS15 TX |
| 10 | FPGA 3.3-V LVTTL+QS3861 | spidev4.0 MISO / GPIO chip 50004010.fpga_gpio IO 10 |
| 11 | FPGA 3.3-V LVTTL+QS3861 | GPIO chip 50004010.fpga_gpio IO 7 / ttyS15 RX |
| 12 | FPGA 3.3-V LVTTL | spidev4.0 MOSI / GPIO chip 50004010.fpga_gpio IO 15 |
| 13 | FPGA 3.3-V LVTTL+QS3861 | GPIO chip 50004010.fpga_gpio IO 8 / ttyS14 TXEN |
| 14 | FPGA 3.3-V LVTTL | spidev4.0 CLK / GPIO chip 50004010.fpga_gpio IO 14 |
| 15 | FPGA 3.3-V LVTTL+QS3861 | GPIO chip 50004010.fpga_gpio IO 9 / ttyS15 TXEN |
| 16 | 3.3 V |
The DIO UARTs are enabled through the FPGA syscon mux:
tshwctl --address 0x08 --poke32 0x6000
Ethernet Connectors
The TS-7250-V3 provides two 10/100 Ethernet ports. Linux exposes these as eth0 and eth1.
LCD Header
The LCD header is a 0.1 inch pitch 2 x 7 header designed around HD44780-compatible LCD modules such as the embeddedTS LCD-LED. The Debian images include lcdmesg.
lcdmesg "line 1" "line 2"
echo -e "line 1\nline 2\n" | lcdmesg
| Pin | IO Type | Signal |
|---|---|---|
| 1 | 5 V | |
| 2 | GND | |
| 3 | CPU 3.3 V | LCD_RS, GPIO chip 20a4000.gpio IO 21 |
| 4 | CPU 3.3 V | LCD_BIAS, PWM duty cycle controlled by FPGA syscon register 0x1c |
| 5 | CPU 3.3 V | LCD_EN, GPIO chip 20a4000.gpio IO 20 |
| 6 | CPU 3.3 V | LCD_WR, GPIO chip 20a4000.gpio IO 19 |
| 7 | CPU 3.3 V+QS3861 | LCD_D1, GPIO chip 20a4000.gpio IO 9 |
| 8 | CPU 3.3 V+QS3861 | LCD_D0, GPIO chip 20a4000.gpio IO 10 |
| 9 | CPU 3.3 V+QS3861 | LCD_D3, GPIO chip 20a4000.gpio IO 11 |
| 10 | CPU 3.3 V+QS3861 | LCD_D2, GPIO chip 20a4000.gpio IO 12 |
| 11 | CPU 3.3 V+QS3861 | LCD_D5, GPIO chip 20a4000.gpio IO 15 |
| 12 | CPU 3.3 V+QS3861 | LCD_D4, GPIO chip 20a4000.gpio IO 16 |
| 13 | CPU 3.3 V+QS3861 | LCD_D7, GPIO chip 20a4000.gpio IO 17 |
| 14 | CPU 3.3 V+QS3861 | LCD_D6, GPIO chip 20a4000.gpio IO 18 |
mikroBUS Header
The mikroBUS header is a 0.1 inch pitch 2 x 8 socket supporting the Mikroe Click board ecosystem. It provides 3.3 V, 5 V, SPI, GPIO, ADC, PWM, UART, and I2C. All I/O on this header is FPGA 3.3-V LVTTL.
By default, pins use their non-GPIO functions. FPGA syscon register 0x08 can change the muxing:
# Make all mikroBUS header pins GPIO
memtool mw -l 0x50004008 0xF0
# Set only SPI to GPIO
memtool mw -l 0x50004008 0x10
| Pin | Name | Description |
|---|---|---|
| 1 | AN | FPGA ADC / GPIO chip 50004054.fpga_gpio IO 1 |
| 2 | RST | GPIO chip 50004054.fpga_gpio IO 0 |
| 3 | CS | spidev5.0 CS / GPIO chip 50004054.fpga_gpio IO 5 |
| 4 | SCK | spidev5.0 CLK / GPIO chip 50004054.fpga_gpio IO 4 |
| 5 | MISO | spidev5.0 MISO / GPIO chip 50004054.fpga_gpio IO 3 |
| 6 | MOSI | spidev5.0 MOSI / GPIO chip 50004054.fpga_gpio IO 2 |
| 7 | 3.3V | 3.3 V |
| 8 | GND | Ground |
| 9 | PWM | FPGA PWM / GPIO chip 50004054.fpga_gpio IO 6 |
| 10 | INT | GPIO chip 50004054.fpga_gpio IO 7 |
| 11 | RX | ttyS13 RX |
| 12 | TX | ttyS13 TX |
| 13 | SCL | /dev/i2c-4 SCL |
| 14 | SDA | /dev/i2c-4 SDA |
| 15 | 5V | 5 V |
| 16 | GND | Ground |
MicroSD Connector
The microSD connector is available as U-Boot device mmc1 and as Linux media when a card is inserted. U-Boot searches it after USB and before the on-board eMMC.
MicroUSB Connector
The microUSB connector provides the supervisory microcontroller USB serial console. Linux hosts typically enumerate it as /dev/ttyACM0 or under /dev/serial/by-id/.
PC104 Header
The PC/104 header exposes the FPGA ISA-compatible bus. Use the Linux fpgaisa files or pc104_peekpoke utility rather than directly manipulating FPGA registers.
| Access file | Description |
|---|---|
io8 | 8-bit I/O cycles |
io16 | 16-bit I/O cycles |
ioalt16 | 16-bit I/O cycles using the alternate TS pinout |
mem8 | 8-bit memory cycles |
mem16 | 16-bit memory cycles |
memalt16 | 16-bit memory cycles using the alternate TS pinout |
Do not use third-party PC/104 power supplies. The -12 V and -5 V rails are incompatible with this PC/104 implementation.
Power Connectors
The TS-7250-V3 provides two power inputs on removable 2-pin terminal blocks. Only one power input may be connected at a time.
| Connector | Input range |
|---|---|
| CN2 | 5 VDC |
| CN12 | 8-48 VDC |
The PCB is marked with polarity under the removable terminal blocks. A typical power supply should provide at least 10 W.
Some PCB silkscreens show 8-28V on the high-voltage input, but every PCB revision from Rev. A forward supports 8-48 VDC.
USB Ports
The TS-7250-V3 has two USB Type-A host ports. The lower host port can optionally be routed to the XBEE header for USB cellular modems.
# Route USB to XBEE
gpioset 209c000.gpio 11=1
# Route USB to the lower Type-A host port
gpioset 209c000.gpio 11=0
USB power can also be controlled:
gpioset 20a4000.gpio 0=0 # Turn off USB power
gpioset 20a4000.gpio 0=1 # Turn on USB power
XBEE Header
The CN20 header is a 2 mm pitch 2 x 10 socket supporting XBEE form-factor modules including Digi radios and NimbeLink-style cellular modems.
Review the target module datasheet before use. Some XBEE-form-factor modules vary from the mechanical and electrical conventions used by other vendors.
For USB cellular radios, route USB to the socket:
gpioset 209c000.gpio 11=1
Only enable the power rail that matches the module:
# For 3.3 V modules
gpioset 50004040.fpga_gpio 4=1
# For 4 V modules
gpioset 50004040.fpga_gpio 11=1
| Pin | IO Type | Signal |
|---|---|---|
| 1 | VCC, XBEE_3.3V or NIMBEL_4.7V | |
| 2 | CPU 3.3 V | ttymxc3 RXD |
| 3 | CPU 3.3 V | ttymxc3 TXD |
| 4 | GND | |
| 5 | Open drain | GPIO chip 209c000.gpio IO 10 |
| 6 | NIMBEL_4.7V | |
| 7 | USB_XBEE_P | |
| 8 | USB_XBEE_N | |
| 9 | GND | |
| 10 | GND | |
| 11 | GND | |
| 12 | CPU 3.3 V | ttymxc3 CTS# |
| 13 | Open drain | GPIO chip 20a4000.gpio IO 2 |
| 14 | 3.3 V VREF | |
| 15 | GND | |
| 16 | GND | |
| 17 | NC | |
| 18 | NC | |
| 19 | NC | |
| 20 | Open drain | GPIO chip 209c000.gpio IO 31 |
Specifications
IO Specifications
| IO Type | Voltage max | Absolute max | Source current | Sink current | VIL | VIH |
|---|---|---|---|---|---|---|
| CPU 3.3V | 3.3 V | 3.6 V | 0.99 V | 2.31 V | ||
| CPU 3.3V+QS3861 | 5 V | 7 V | ||||
| FPGA 3.3-V LVTTL | 3.3 V | 3.6 V | 6 mA | 6 mA | ||
| FPGA 3.3-V LVTTL+QS3861 | 5 V | 7 V | 6 mA | 6 mA | ||
| PCA9555 | 5.3 V | 6 V | 25 mA max per IO, 100 mA per octal bank, 160 mA total | 8-24 mA | 0.3 V | 2.31 V |
Power Consumption
The i.MX6UL CPU can change frequency as needed to reduce power or provide more processing headroom. Ethernet power savings can be improved by briefly bringing interfaces up and down during startup:
ifconfig eth0 up
ifconfig eth1 up
ifconfig eth0 down
ifconfig eth1 down
ifconfig wlan0 up # only needed if Wi-Fi is present
The following tests used the 5 V input, no external connections except power, eMMC boot, and an idle prompt unless otherwise noted.
| Test | Avg. (W) | Peak (W) |
|---|---|---|
| Idle | 0.77 | 1.29 |
CPU fully loaded with stress-ng --matrix 0 -t 60m | 1.03 | 1.45 |
CPU idle, single Ethernet port up and active with iperf | 1.17 | 1.75 |
| CPU fully loaded and single Ethernet port up and active | 1.40 | 2.03 |
| Supervisory microcontroller sleep mode, ARM CPU off | 0.055 | 0.063 |
Power Input Specifications
The TS-7250-V3 supports the 5 VDC input on CN2 and the 8-48 VDC input on CN12. The 8-48 VDC input can also come from the PC/104 connector's +12V signal.
| Input | Min range | Max range |
|---|---|---|
| 5 VDC | 4.7 VDC | 5.2 VDC |
| 8-48 VDC | 8 VDC | 48 VDC |
The CN12 power connector silkscreen may show 8-28V; every PCB revision from Rev. A forward supports the full 8-48 VDC range.
Backup / Restore
While all of our products ship with images pre-loaded in to any supplied media, there are many situations where new images may need to be written. For example, to restore a device to its factory settings or apply a customized image/filesystem for application deployment. Additionally, specific units may be used for development and that unit's disk images need to be replicated to other units to be deployed in the field.
We offer a number of different ways to accomplish both capturing images to be written to other units, and the actual writing process itself. See the section on our USB Image Replicator tool to capture and/or write images, as well as details on manual processes to capture and write images on each of this device's media.
Software Images
LTS Images
| Image Date | Image Files | Changes |
|---|---|---|
| 20240409 | headless minimal |
|
| 20230914 | headless minimal |
|
| 20230809 | headless minimal |
|