TS-7250-V3

From embeddedTS Manuals
Note: This manual is incomplete at this time and is subject to change without warning while the TS-7250-V3 is in Engineering Sampling phase.
TS-7250-V3
ts-7250-v3.gif
Product Page
Documentation
Schematic
FTP Path
Processor
NXP i.MX6UL
696 MHz Arm® Cortex®-A7
i.MX6UL Product Page
CPU Documentation

Overview

The TS-7250-V3 is a PC104 form factor SBC with a PC104 bus, mikroBus, Digi XBEE header, soldered down eMMC flash, dual Ethernet, microSD, and wifi. This board also provides a migration path from the TS-7250-V2 and TS-7250 series systems.

Getting Started

A Linux workstation is recommended and assumed for development using this documentation. For users in Windows or OSX, we recommend virtualizing Linux using VMWare or similar to make the full power of Linux available. The developer should be comfortable with Linux to work with embedded Linux on the target platform. Most of our platforms run Debian, which is recommended for ease of use if there is no personal distribution preference.

The main reasons that Linux is useful are:

  • Linux filesystems on the microSD card can be accessed on the PC.
  • More ARM cross-compilers are available.
  • If recovery is needed, a bootable medium can be written.
  • A network filesystem can be served.
  • Builds such as Linux kernel, Buildroot, Yocto, and distro-seed will not work from WSL1/2 on a case-insensitive filesystem.
WARNING: Be sure to take appropriate Electrostatic Discharge (ESD) precautions. Disconnect the power source before moving, cabling, or performing setup procedures. Inappropriate handling may cause damage to the board.

Connect to the Console

This board can get console from either the DB9 port as RS-232 for users in the enclosure, or the microUSB connector as a CDC-ACM through the onboard supervisory microcontroller.

On the DB9 port this uses 115200 baud 8n1 with no flow control. The built in USB serial device is hard coded to the correct baud/mode and does not require configuration.

On Windows it will show up as an additional COM port, assigned an incremental COM number. This can be opened with any terminal emulator such as Putty, Hyperterminal, Realterm, or Teraterm.

Our recommended path is typically using this under a Linux OS. If there is only one USB serial device connected to the Linux workstation it will be /dev/ttyACM0, or if there are other serial devices run:

ls /dev/serial/by-id/

This will list the device like "/dev/serial/by-id/usb-embeddedTS_TS-7250-V3_Console_8BDBE9584E4B-if00". Use a terminal emulator such as picocom/minicom/screen:

sudo picocom /dev/ttyACM0

or

sudo picocom /dev/serial/by-id/usb-embeddedTS_TS-7250-V3_Console_8BDBE9584E4B-if00

The picocom tool will output some serial setting information and then "Terminal ready". Any messages after this point will be from the device via the serial output. The terminal is now ready and power can be applied in order to boot up the device.

Note: Under Linux it is recommended to run this command once:
echo "ATTR{idVendor}==\"35b0\",  ENV{ID_MM_DEVICE_IGNORE}=\"1\"" > /etc/udev/rules.d/75-embeddedts-mm.rules
This prevents modemmanager from probing the ACM device as a cellular modem.

Console from Windows

Putty is a small simple client available for download here. Open up Device Manager to determine your console port. See the putty configuration image for more details.

Device Manager 2 Putty Configuration 2

Console from MACOSX

There are many serial terminal applications for MACOSX, two commonly used applications are 'picocom', and 'screen'. These examples demonstrate these applications and assume that the serial device is "/dev/tty.usbmodem00D069C0FFEE1". Be sure to replace the serial device string with that of the device on your workstation.

'picocom' is a very small and simple client.

picocom -b 115200 /dev/tty.usbmodem00D069C0FFEE1


'screen' is a terminal multiplexer which happens to have serial support.

screen /dev/tty.usbmodem00D069C0FFEE1 115200

Powering up

The TS-7250-V3 receives power through either the 5 VDC 2-pin terminal block connector near the DB9, or the 2 pin 8-48 VDC terminal block near the mikroBUS connector. Only a single power input may be connected at any time.

If a power supply is ordered with the TS-7250-V3 it will include the correct terminal block connected to the power supply. Otherwise the terminal block will ship with the unit.

Note: The polarity is marked underneath the removable terminal blocks.

Once power is applied the device will output information via the built in USB console.

The first output is from U-Boot:


Trying to boot from MMC1


-Boot 2020.01-40202-gb266a329da (Feb 03 2023 - 21:37:24 +0000)

CPU:   Freescale i.MX6UL rev1.2 696 MHz (running at 396 MHz)
CPU:   Automotive temperature grade (-40C to 125C) at 44C
Reset cause: POR
Model: embeddedTS i.MX6UL TS-7250-V3
Board: TS-7250-V3 REV C
FPGA: Rev 52 (b6aaace8)
DRAM:  1 GiB
MMC:   FSL_SDHC: 0, FSL_SDHC: 1
Loading Environment from MMC... OK
In:    serial
Out:   serial
Err:   serial
Net:   
Warning: ethernet@20b4000 using MAC address from ROM
eth1: ethernet@20b4000
Warning: ethernet@2188000 using MAC address from ROM
, eth0: ethernet@2188000
Press ESC twice to abort autoboot in 1 second(s)
Note: The message "*** Warning - bad CRC, using default environment" can be safely ignored. This indicates that u-boot scripts are not being customized. Typing "env save" will hide these messages, but this is not needed.
Note: The message "using MAC address from ROM" is indicating that the board is using the preprogrammed MAC address as intended. All boards are assigned 2 unique MAC addresses.

This u-boot and its environment is loaded from the SPI flash (/dev/mmcblk0boot0). This "(/dev/mmcblk0boot0)" is a hardware partition that is independent of the main flash on the emmc (/dev/mmcblk0). From here, u-boot will follow u-boots standard Distro boot command. This will check for boot files on the first USB mass storage, and will by default find a bootable image on eMMC. From here the board will boot to our default #Debian image.

First Linux Boot

When booting with the default settings, a shipped board will boot to the eMMC. The eMMC by default are pre-programmed with our default Debian 11 Bullseye image. After Linux boots, it will ask the user to log in with a username and password. This uses "root" as the username with no password. This can be changed after logging in with the command "passwd" to set an account password.

The Linux prompt can use standard IO commands to interact with hardware, or can be used to begin application development.

Booting From USB

This board supports booting to an OS image written to a USB drive for development. For the most reliable boot we recommend eMMC boot.

Note: This can be run from the board while booted to eMMC, but this should not rewrite the same USB stick that the system has used to boot. Rewriting an image while it is used as the boot media will result in a corrupt image.

This requires a Linux system to write the USB image.

Check lsblk or dmesg to find your USB drive, but the following examples will assume /dev/sdc.

wget http://files.embeddedTS.com/ts-arm-sbc/ts-7250-v3-linux/distributions/debian/tsimx6ul-debian-bullseye-latest.tar.xz
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 tsimx6ul-debian-bullseye-latest.tar.xz -C /mnt/usb/
sudo umount /mnt/usb/

If this USB is plugged into USB on startup, it will be chosen instead of the onboard eMMC. For example:

U-Boot 2020.01-00009-g99e2080fad (Mar 12 2020 - 08:43:22 -0700)

CPU:   Freescale i.MX6UL rev1.2 696 MHz (running at 396 MHz)
CPU:   Automotive temperature grade (-40C to 125C) at 34C
Reset cause: POR
Model: Technologic Systems i.MX6UL TS-7250-V3
Board: TS-7250-V3
DRAM:  512 MiB
MMC:   FSL_SDHC: 0
Loading Environment from MMC... *** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   
Warning: ethernet@20b4000 using MAC address from ROM
eth1: ethernet@20b4000
Warning: ethernet@2188000 using MAC address from ROM
, eth0: ethernet@2188000
Press ESC twice to abort autoboot in 1 second(s)
starting USB...
Bus usb@2184000: USB EHCI 1.00
Bus usb@2184200: USB EHCI 1.00
scanning bus usb@2184000 for devices... 1 USB Device(s) found
scanning bus usb@2184200 for devices... 2 USB Device(s) found
       scanning usb for storage devices... 1 Storage Device(s) found

Device 0: Vendor: SanDisk  Rev: 0001 Prod: Extreme         
            Type: Removable Hard Disk
            Capacity: 59836.1 MB = 58.4 GB (122544516 x 512)
... is now current device
Scanning usb 0:1...
Found U-Boot script /boot/boot.scr
675 bytes read in 8 ms (82 KiB/s)
root ## Executing script at 82000000
5348808 bytes read in 145 ms (35.2 MiB/s)
33863 bytes read in 8 ms (4 MiB/s)
Booting Debian from usb 0:1...
root ## Flattened Device Tree blob at 83000000
   Booting using the fdt blob at 0x83000000
   Loading Device Tree to 9ef68000, end 9ef73446 ... OK

Starting kernel ...

[    6.242409] cgroup: cgroup2: unknown option "nsdelegate,memory_recursiveprot"
[    6.251458] cgroup: cgroup2: unknown option "nsdelegate"


Welcome to Debian GNU/Linux 11 (bullseye)

TS-7250-V3 Models

The TS-7250-V3 is available in these variations:

Model Description
TS-7250-V3-SXW9I
  • 1GB DDR3 RAM
  • 16GB eMMC flash
  • Dual Ethernet
  • Cellular Socket
  • Mikrobus Socket
  • Accelerometer/Magnetometer
  • I2C GPIO Expander on PC/104
TS-7250-V3-SMW8I
  • 512MB DDR3 RAM
  • 4GB eMMC Flash
  • Dual Ethernet
  • Cellular Socket
  • Mikrobus Socket
  • Accelerometer/Magnetometer
  • I2C GPIO Expander on PC/104
TS-7250-V3-SMN2I
  • 512MB DDR3 RAM
  • 4GB eMMC Flash
  • Dual Ethernet
  • Cellular Socket
  • Mikrobus Socket
TS-7250-V3-SMN1I
  • 512MB DDR3 RAM
  • 4GB eMMC Flash
  • Single Ethernet

There are also variations not detectable in software:

Suffix Description
ST Stack through PC/104 connector. The standard model has no pins extending from the bottom of the board, but a stackthrough PC/104 allows connecting peripherals beneath the SBC.
CC Conformal Coated. This provides some protection against harsh operating environments.

U-Boot

U-Boot is a bootloader and comes preinstalled on this board. The U-Boot bootloader is loaded in the eMMC hardware boot partitions in /dev/mmcblk0boot0. U-Boot sets up the hardware and then loads the OS from the available storage devices. U-Boot allows booting images from the microSD, eMMC, NFS, or USB. Most users will not need to customize u-boot further, and can proceed to the #Debian sections for information on application development.

Entering U-Boot shell

The U-Boot shell is a powerful tool. It allows for modification of the environment, and the ability to run commands directly. By default, there is only one way to enter the shell: Pressing the "Esc" key twice followed by a 1-second window, this will bring you to a shell prompt provided by U-Boot. If pressing the Esc key twice is found incoming on the serial terminal within this time window, then U-Boot will drop to its shell. This behavior can be modified by setting the U-Boot environment variable bootdelay. This variable if modified will force the timeout for pressing the Esc key to be however many seconds the variable is set to. If it is set to 0, then the Esc prompt is skipped.

Note: Use caution when setting "bootdelay", a value of 0 means it is no longer possible to enter the U-Boot shell. We do not recommend utilizing "bootdelay" in this manner, as we do not support it. Be sure to read the U-Boot Documentation for further details U-Boot Documentation

U-Boot Distro Boot

U-boot by default uses u-boots distro bootcmd to determine how to boot. As shipped the board will boot to the preprogrammed eMMC with our #Debian image.

By default this will look at:

Order U-boot device name Description
1 usb0 First detected USB mass storage device
2 mmc1 MicroSD card
3 mmc0 Onboard eMMC storage
4 dhcp DHCP Option [1]
5 pxe PXE File [2]
  1. DHCP can advertise a TFTP server (next-path, root-path, and filename) to point to a u-boot script.
  2. DHCP can advertise a TFTP server (next-path, root-path, and filename) with a PXE file to control boot.

The default boot order can be left for most users, but boot can be optimized for one boot device by stopping at u-boot and running:

# Boot straight to eMMC:
env set boot_targets 'mmc0';
env save

# Boot to usb, then mmc only
env set boot_targets 'usb0 mmc0';
env save

# Set back to default boot order
env set boot_targets 'usb0 mmc1 mmc0 dhcp pxe'
env save

U-boot will search the boot media on either the 1st partition, or if the disk is partitioned with GPT instead of MBR it will search the "bootable" partition. It will then search for these files:

Order Search for Paths Description
1 extlinux /extlinux/extlinux.conf, /boot/extlinux/extlinux.conf Menu conf file of kernels
2 U-boot script /boot.scr.uimg, /boot.scr, /boot/boot.scr.uimg, /boot/boot.scr u-boot script with instructions to load the OS
3 EFI Binary efi/boot/bootarm.efi EFI binary (such as grub)

Our Debian images use a u-boot script in /boot/boot.scr.uimg.

U-Boot Environment

The U-Boot environment is stored in the on-board eMMC flash in the /dev/mmcblk0boot0 partition.

# Print all environment variables
env print -a

# Sets the variable bootdelay to 5 seconds
env set bootdelay 5

# Variables can also contain commands
env set hellocmd 'led 0 off; echo Hello world; led 1 on;'

# Execute commands saved in a variable
env run hellocmd

# Commit env changes to the spi flash
# Otherwise changes are lost
env save

# Restore env to default
env default -a

# Remove a variable
env delete hellocmd

U-Boot Commands

# The most important command is 
help
# This can also be used to see more information on a specific command
help i2c

# Boot a Linux zImage loaded at $loadaddr
bootz
# Boot in to a Linux zImage at $loadaddr, skip initrd, specifies
# the FDT address to Linux knows where to find the device tree
bootz ${loadaddr} - ${fdtaddr}

# Get a DHCP address
dhcp
# This sets ${ipaddr}, ${dnsip}, ${gatewayip}, ${netmask}
# and ${ip_dyn} which can be used to check if the dhcp was successful

# These commands are used for scripting:
false # do nothing, unsuccessfully
true # do nothing, successfully

# This command can set fuses in the processor
# Setting fuses can brick the unit, will void the warranty,
# and should not be done in most cases
fuse

# GPIO can be manipulated from U-Boot.  Keep in mind that the IOMUX 
# in U-Boot is only setup enough to boot the device, so not all pins will
# be set to GPIO mode out of the box.  Boot to the full operating system
# for more GPIO support.
# GPIO are specified in bank and IO in this manual.  U-Boot uses a flat numberspace,
# so for bank 2 DIO 25, this would be number (32*1)+25=89
# The formula thus being (32*(bank-1)+dio)=flattened_dio
# Note that on some products, bank 1 is the first bank
# Set 2_25 low
gpio clear 83
# Set 2_25 high
gpio set 83
# Read 2_25
gpio input 83

# This command is used to copy a file from most devices
# Load kernel from SD
load mmc 0:1 ${loadaddr} /boot/zImage
# Load Kernel from eMMC
load mmc 1:1 ${loadaddr} /boot/zImage
# Load kernel from USB
usb start
load usb 0:1 ${loadaddr} /boot/zImage
# Load kernel from SATA (NOT SUPPORTED ON THE TS-7250-V3)
sata init
load sata 0:1 ${loadaddr} /boot/zImage

# View the FDT from U-Boot
load mmc 0:1 ${fdtaddr} /boot/imx6ul-ts7250v3.dtb
fdt addr ${fdtaddr}
fdt print

# It is possible to blindly jump to any memory location
# This is similar to bootm, but it does not require
# the use of the U-Boot header
load mmc 0:1 ${loadaddr} /boot/custombinary
go ${loadaddr}

# Browse fat, ext2, ext3, or ext4 filesystems:
ls mmc 0:1 /

# Test memory.
mtest

# Check for new SD card
mmc rescan
# Read SD card size
mmc dev 1
mmcinfo
# Read eMMC Size
mmc dev 0
mmcinfo

# The NFS command is like 'load', but used over the network
dhcp
env set serverip 192.168.0.11
nfs ${loadaddr} 192.168.0.11:/path/to/somefile

# Test ICMP
dhcp
ping 192.168.0.11

# Reboot
reset

# SPI access is through the SF command
# Be careful with sf commands since
# this is where U-Boot and the FPGA bitstream exist
# Improper use can render the board unbootable
sf probe

# Delay in seconds
sleep 10

# Load HUSH scripts that have been created with mkimage
load mmc 0:1 ${loadaddr} /boot/boot.scr
source ${loadaddr}

# Most commands have return values that can be used to test
# success, and HUSH scripting supports comparisons like
# test in Bash, but much more minimal
if load mmc 1:1 ${fdtaddr} /boot/zImage;
	then echo Loaded Kernel
else
	echo Could not find kernel
fi

# Print U-Boot version/build information
version

Booting From NFS

Note: The following instructions assume the NFS server is running Debian.

U-Boot's NFS support can be used to load a kernel, device tree binary, and root filesystem. The default scripts include an example NFS boot script. Because of the way U-Boot tries to infer server data, the script we use will bypass this, making it more straightforward to use an NFS root that will not be heavily dependent on a particular network configuration.

First, on the NFS server side, be sure to verify and if needed modify /etc/default/nfs-kernel-server such that the server is running NFS -V 2. The nfs-kernel-server file should resemble the following:

# Number of servers to start up
RPCNFSDCOUNT="8 --no-nfs-version 4 -V 2"

# Runtime priority of server (see nice(1))
RPCNFSDPRIORITY=0

# Options for rpc.mountd.
# If you have a port-based firewall, you might want to set up
# a fixed port here using the --port option. For more information, 
# see rpc.mountd(8) or http://wiki.debian.org/SecuringNFS
# To disable NFSv4 on the server, specify '--no-nfs-version 4' here
RPCMOUNTDOPTS="--manage-gids --no-nfs-version 4"

# Do you want to start the svcgssd daemon? It is only required for Kerberos
# exports. Valid alternatives are "yes" and "no"; the default is "no".
NEED_SVCGSSD=""

# Options for rpc.svcgssd.
RPCSVCGSSDOPTS=""

Once verified that the file /etc/default/nfs-kernel-server looks like the above, then from the U-Boot shell run the following commands:

# Set this to your NFS root path
env set nfsroot <IP ADDRESS>:/path/to/nfs/rootfs/
env save

To boot to NFS root once the server details are set:

# Boot to NFS once
run nfsboot;

# To make the NFS boot the persistent default
env set bootcmd run nfsboot;
env save
Note: You must have NFS version 2 on your server, as it is disabled by default on new distributions. Be sure to read the U-Boot Documentation for further details U-Boot Documentation

.

U-Boot Development

Note: This section is incomplete at this time and is subject to change without warning while the TS-7250-V3 is in its Engineering Sampling phase.

We do provide our U-Boot sources, but we do not recommend rebuilding a custom U-Boot binary, as it can leave the system in an unbootable state.

If proceeding with building a custom U-Boot, use the "v2020.01-ts" branch from our github repo: https://github.com/embeddedTS/u-boot-imx this can be executed with the following command:

git clone https://github.com/embeddedTS/u-boot-imx.git -b v2020.01-ts u-boot-ts7250v3

When compiling, we recommend using ONLY this cross-compiler, the use of any other compiler may cause issues or may leave the system in an unbootable state! Specifically, we have experienced RAM problems when using a more recent cross compiler to build this version of U-Boot. The tarball can be extracted with the following:

mkdir /opt/toolchains/ts7250v3/
tar -xf tsimx6ul-glibc-gnueabihf-4.9.4.tar.xz -C /opt/toolchains/ts7250v3/

Once the tarball has been properly extracted set up the following variables and run the build script:

export ARCH=arm
export CROSS_COMPILE=/path/to/folder/bin/arm-linux-gnueabihf-

After the environment variables have been set up as shown above the build is now ready to be executed:

cd /path/to/u-boot-imx
./build-imx6ul.sh ts7250v3

Debian

Debian 12 - Bookworm

Debian 12 - Getting Started

This Debian release is available in 2 flavors with various packages.

Image Estimated Size Description
tsimx6ul_debian_12_headless-latest.tar.xz 681 MiB
  • Includes 5.10 kernel with tsimx6ul_defconfig that includes broad driver support
  • Base Debian with common utils
  • Common embedded tools (i2c, can, gpio, iio, serial tools, etc)
  • Includes hardware support
  • Networking tools (ethernet, wifi, bluetooth)
  • Includes Development tools
debian-armhf-bookworm-minimal-latest.tar.bz2 263 MiB
  • Includes 5.10 kernel with tsimx6ul_minimal_defconfig that includes bare minimum driver support and kernel options required by Debian.
  • Includes base Debian rootfs adding only what is required for Ethernet support.

The default login is root with no password.

This image can be written to a USB drive, or to the eMMC. For development, a USB thumbdrive will be simplest. If a bootable USB drive is connected this will take priority over other boot media Plug in a USB drive and check the last output from "dmesg" to get the USB disk. For example, this may be /dev/sdc.

# Erase all older partitions
sudo sgdisk --zap-all /dev/sdc
# Create one GPT Linux partition
sudo sgdisk -n 0:0:0 -t 0:8300 /dev/sdc
# Create a filesystem and mount
sudo mkfs.ext4 /dev/sdc1
sudo mkdir /mnt/usb/
sudo mount /dev/sdc1 /mnt/usb/
# Extract downloaded image:
sudo tar --numeric-owner -xf tsimx6ul_debian_12_headless-latest.tar.xz -C /mnt/usb/
sudo chmod 755 /mnt/usb/
sudo umount /mnt/usb/

These commands will also work while booted from a USB drive to rewrite the eMMC. Instead of /dev/sdc you would use /dev/mmcblk0, and instead of /dev/sdc1 you would use /dev/mmcblk0p1.

Debian 12 - 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

Debian 12 - WIFI 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.

Debian 12 - WIFI 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.

Debian 12 - 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.

Debian 12 - 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.

Debian 12 - Starting Automatically

Bookwoorm Startup Scripts

Debian 12 - 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

Debian 12 - Compile the Kernel

Linux-5.10.y

A compatible armhf cross compiler is needed for building the 5.10 kernel. We recommend using the cross compiler available in Debian distributions. It is also possible to use our Buildroot repository to build a compatible cross compiler.


Download and Configure

These steps assume a host Linux workstation with an appropriate cross compiler. 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.


Prerequisites

If using our instructions for using Docker to handle the cross compiler, then the Docker environment needs to be entered first:

# Create a place to store the kernel:
mkdir -p ~/Projects/tsimx6ul/kernel/
cd ~/Projects/tsimx6ul/kernel/
docker-debian-bookworm

If the Docker container is not being used, a number of host tools are required to be installed on the workstation:

# 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
Note: The above 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.

Download kernel repo on a host Linux workstation:

# Do a shallow clone of the sources
git clone --depth 1 -b linux-5.10.y https://github.com/embeddedTS/linux-lts

cd linux-lts/


Configure environment variables needed for building. This specifies the architecture, the cross compiler that is being used, and to set up building the kernel modules for the WILC3000 Wi-Fi/BLE module:

export CROSS_COMPILE=arm-linux-gnueabihf-  # This may be different if using a different compiler!
export ARCH=arm
export WILC=y


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 (this is built later):

git clone -b linux4microchip-2021.10-1 https://github.com/embeddedTS/wilc3000-external-module/


Next, set the default configuration for this platform. Note that a minimal defconfig and a full-feature defconfig are available. The minimal defconfig contains options for supporting the device and a few common peripherals and technologies. While the full defconfig includes much more support for things like USB devices, a more broad range of netfilter/iptables filter module support, etc.

make tsimx6ul_defconfig

# The minimal defconfig can alternately be used with:
# make tsimx6ul_minimal_defconfig


Build and Install

Note: If using the Docker container to cross compile, be sure to exit the container after the build script below completes! The tarball will be located in the linux-lts/ folder that was created.

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}"


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
Note: 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.


Linux-6.6.y

A compatible armhf cross compiler is needed for building the 6.6 kernel. We recommend using the cross compiler available in Debian distributions. It is also possible to use our Buildroot repository to build a compatible cross compiler.


Download and Configure

These steps assume a host Linux workstation with an appropriate cross compiler. 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.


Prerequisites

If using our instructions for using Docker to handle the cross compiler, then the Docker environment needs to be entered first:

# Create a place to store the kernel:
mkdir -p ~/Projects/tsimx6ul/kernel/
cd ~/Projects/tsimx6ul/kernel/
docker-debian-bookworm

If the Docker container is not being used, a number of host tools are required to be installed on the workstation:

# 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
Note: The above 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.

Download kernel repo on a host Linux workstation:

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


Configure environment variables needed for building. This specifies the architecture, the cross compiler that is being used, and to set up building the kernel modules for the WILC3000 Wi-Fi/BLE module:

export CROSS_COMPILE=arm-linux-gnueabihf-  # This may be different if using a different compiler!
export ARCH=arm
export WILC=y


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 (this is built later):

git clone -b linux4microchip-2024.04 https://github.com/embeddedTS/wilc3000-external-module/


Next, set the default configuration for this platform. Note that a minimal defconfig and a full-feature defconfig are available. The minimal defconfig contains options for supporting the device and a few common peripherals and technologies. While the full defconfig includes much more support for things like USB devices, a more broad range of netfilter/iptables filter module support, etc.

make tsimx6ul_defconfig

# The minimal defconfig can alternately be used with:
# make tsimx6ul_minimal_defconfig


Build and Install

Note: If using the Docker container to cross compile, be sure to exit the container after the build script below completes! The tarball will be located in the linux-lts/ folder that was created.

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}"


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
Note: 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.

Debian 11 - Bullseye

Debian Bullseye Description

Debian 11 - Getting Started and writing an Image

Once installed, the default user is "root" with no password.

This image can be written to a USB drive, or to the eMMC. For development, a USB thumbdrive will be simplest. If a bootable USB drive is connected this will take priority over other boot media. Plug in a USB drive and check the last output from "dmesg" to get the USB disk. For example, this may be /dev/sdc.

# Erase all older partitions
sudo sgdisk --zap-all /dev/sdc
# Create one GPT Linux partition
sudo sgdisk -n 0:0:0 -t 0:8300 /dev/sdc
# Create a filesystem and mount
sudo mkfs.ext4 /dev/sdc1
sudo mkdir /mnt/usb/
sudo mount /dev/sdc1 /mnt/usb/
# Extract downloaded image:
sudo tar --numeric-owner -xf tsimx6ul-debian-bullseye-latest.tar.xz -C /mnt/usb/
sudo chmod 755 /mnt/usb/
sudo umount /mnt/usb/

These commands will also work while booted from a USB drive to rewrite the eMMC. Instead of /dev/sdc you would use /dev/mmcblk0, and instead of /dev/sdc1 you would use /dev/mmcblk0p1.

Debian 11 - Configuring Network

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

Some common examples are shown below.

DHCP on eth0. Create the file: /etc/network/interfaces.d/eth0

auto eth0
allow-hotplug eth0
iface eth0 inet dhcp

Static IP on eth0. Create the file /etc/network/interfaces.d/eth0

auto eth0
iface eth0 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


Debian 11 - WIFI Client

Note: 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.

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.

Debian 11 - WIFI Access Point

Note: 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.

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.

Debian 11 - 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.

Debian 11 - 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
/bin/ls /etc/ssh/ssh_host*key >/dev/null 2>&1  || ssh-keygen -A
passwd root # Set any password

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

Debian 11 - 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.

Debian 11 - Cross Compiling

Debian only provides their cross compiler for their distribution. Our examples will set up a Docker for Debian to use for development. If using Debian 11 Bullseye directly, or through a VM then the docker usage can be skipped.

Create a file called "Dockerfile" with these contents:

FROM debian:bullseye

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 \
    locales \
    lzop \
    make \
    multistrap \
    ncurses-dev \
    pkg-config \
    python \
    python3 \
    python3-pip \
    python3-pexpect \
    qemu-user-static \
    rsync \
    socat \
    runit \
    texinfo \
    u-boot-tools \
    unzip \
    vim \
    wget \
    xz-utils

# To make a more readable PS1 to show we are in the Docker
ENV debian_chroot debian_bullseye
RUN echo "PS1='\${debian_chroot}\\[\033[01;32m\\]@\\H\[\\033[00m\\]:\\[\\033[01;34m\\]\\w\\[\\033[00m\\]\\$ '" >> /etc/bash.bashrc

# Set up locales.  Needed by yocto.
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

In the same directory as the file named "Dockerfile" run:

docker build --tag armhf-bullseye-toolchain .

When this has finished the docker can be used with:

docker run --rm -it --volume $(pwd):/work armhf-bullseye-toolchain bash

This will map the current directory to /work.

At this point the Debian Docker is ready to compile armhf binaries. For example, create a hello world in your home folder at ~/hello.c

#include <stdio.h>
int main(){
    printf("Hello World\n");
}

To compile this enter the docker with:

docker run -it --volume $(pwd):/work armhf-bullseye-toolchain bash
# Then from the docker:
cd /work/
arm-linux-gnueabihf-gcc hello.c -o hello

Check "file hello" to verify the binary type:

debian_bullseye@b720b8ba6c1e:/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]=fc6389ca8da310bb5d0b87e5998b59894c078d9f, for GNU/Linux 3.2.0, not stripped

This can also be used to develop against dynamic libraries from Debian. The armhf packages can be installed in the Docker. For example, to link against curl:

# Enter the Docker:
docker run -it --volume $(pwd):/work armhf-bullseye-toolchain bash
cd /work/

apt-get install libcurl4-openssl-dev:armhf
# Download curl's simple.c example
wget https://raw.githubusercontent.com/bagder/curl/master/docs/examples/simple.c
arm-linux-gnueabihf-gcc simple.c -o simple -lcurl

The "simple" binary is now built for armhf and links dynamically to curl.

This will only retain the armhf libcurl package until the docker is exited. To make the changes permanent, add the package to the Dockerfile and rerun:

docker build --tag armhf-bullseye-toolchain .

Debian 11 - Backup the image

To create backups / restore images, it is recommended to first boot to USB. A disk should not be backed up or written while it is used to boot.

If your image is a single partition it can be backed up as a tar file. This allows the media to easily expand to any disk size.

Create a backup tar

mkdir /mnt/emmc
mount /dev/mmcblk0p1 /mnt/emmc/
tar -cJf /root/backup-of-image.tar.xz -C /mnt/emmc/
umount /mnt/emmc/

Write a tar image to the eMMC

sgdisk --zap-all /dev/mmcblk0
sgdisk -n 0:0:0 -t 0:8300 /dev/mmcblk0
mkfs.ext4 /dev/mmcblk0p1
mkdir /mnt/emmc
mount /dev/mmcblk0p1 /mnt/emmc/
tar -xf /root/backup-of-image.tar.xz -C /mnt/emmc/
umount /mnt/emmc/

Debian 11 - Compile the Kernel

A compatible armhf cross compiler is needed for building the 5.10 kernel. We recommend using the cross compiler available in Debian distributions. It is also possible to use our Buildroot repository to build a compatible cross compiler.


Download and Configure

These steps assume a host Linux workstation with an appropriate cross compiler. 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.


Prerequisites

If using our instructions for using Docker to handle the cross compiler, then the Docker environment needs to be entered first:

# Create a place to store the kernel:
mkdir -p ~/Projects/tsimx6ul/kernel/
cd ~/Projects/tsimx6ul/kernel/
docker-debian-bookworm

If the Docker container is not being used, a number of host tools are required to be installed on the workstation:

# 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
Note: The above 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.

Download kernel repo on a host Linux workstation:

# Do a shallow clone of the sources
git clone --depth 1 -b linux-5.10.y https://github.com/embeddedTS/linux-lts

cd linux-lts/


Configure environment variables needed for building. This specifies the architecture, the cross compiler that is being used, and to set up building the kernel modules for the WILC3000 Wi-Fi/BLE module:

export CROSS_COMPILE=arm-linux-gnueabihf-  # This may be different if using a different compiler!
export ARCH=arm
export WILC=y


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 (this is built later):

git clone -b linux4microchip-2021.10-1 https://github.com/embeddedTS/wilc3000-external-module/


Next, set the default configuration for this platform. Note that a minimal defconfig and a full-feature defconfig are available. The minimal defconfig contains options for supporting the device and a few common peripherals and technologies. While the full defconfig includes much more support for things like USB devices, a more broad range of netfilter/iptables filter module support, etc.

make tsimx6ul_defconfig

# The minimal defconfig can alternately be used with:
# make tsimx6ul_minimal_defconfig


Build and Install

Note: If using the Docker container to cross compile, be sure to exit the container after the build script below completes! The tarball will be located in the linux-lts/ folder that was created.

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}"


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
Note: 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.

Ubuntu

Ubuntu 23.04 - Lunar

Ubuntu 23.04 - Getting Started

This Ubuntu release is available in 2 flavors with various packages.

Image Estimated Size Description
tsimx6ul_ubuntu_23_04_headless-latest.tar.xz 834 MiB
  • Includes 5.10 kernel with tsimx6ul_defconfig that includes broad driver support
  • Base Ubuntu with common utils
  • Common embedded tools (i2c, can, gpio, iio, serial tools, etc)
  • Includes hardware support
  • Networking tools (ethernet, wifi, bluetooth)
  • Includes Development tools
  • Includes X11 that launches matchbox and xterm on startup
  • Includes touchscreen support
tsimx6ul_ubuntu_23_04_minimal-latest.tar.xz 197 MiB
  • Includes 5.10 kernel with tsimx6ul_minimal_defconfig that includes bare minimum driver support and kernel options required by Ubuntu.
  • Includes base Ubuntu rootfs adding only what is required for Ethernet support.

The default login is "user/user" which includes sudo permissions.

To write this to an SD card, first partition the SD card to have one large ext3, or ext4 partition. See the guide here for more information. Once it is formatted, extract this tar with:

# Assuming your SD card is /dev/sdc with one partition
mkfs.ext4 /dev/sdc1
mkdir /mnt/sd/
sudo mount /dev/sdc1 /mnt/sd/
sudo tar --numeric-owner -xjf  tsimx6ul_ubuntu_23_04_headless-latest.tar.xz -C /mnt/sd
sudo umount /mnt/sd
sync

To rewrite the eMMC, boot to the SD card. You cannot rewrite the emmc while it is mounted elsewhere, or used to currently boot the system. Once booted to the SD, run:

mkfs.ext4 /dev/mmcblk2p1
mkdir /mnt/emmc
mount /dev/mmcblk2p1 /mnt/emmc
wget -qO- https://files.embeddedts.com/ts-arm-sbc/ts-7250-v3-linux/distributions/ubuntu/tsimx6ul_ubuntu_23_04_headless-latest.tar.xz | tar --numeric-owner -xJ -C /mnt/emmc/
umount /mnt/emmc
sync

Ubuntu 23.04 - Networking

The network in Ubuntu is configured netplan. For complete documentation, see Netplan'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/netplan/ethernet.yaml and add:

network:
  version: 2
  renderer: networkd
  ethernets:
    end0:
      dhcp4: true
      dhcp6: true

Static IP on end0. Edit the file /etc/netplan/ethernet.yaml and add:

network:
  version: 2
  renderer: networkd
  ethernets:
    end0:
     dhcp4: no
     addresses: [192.168.0.50/24]
     gateway4: 192.168.0.1
     nameservers:
       addresses: [8.8.8.8,8.8.4.4]

After creating the yaml file, set the appropriate permissions and apply the netplan:

sudo chmod 600 /etc/netplan/*.yaml
sudo netplan apply

Ubuntu 23.04 - WIFI Client

Wireless configuration under Ubuntu, similar to Ethernet, also uses netplan for configuration. For example, create /etc/netplan/wifi.yaml:

network:
  version: 2
  renderer: networkd
  wifis:
    wlan0:
      dhcp4: yes
      dhcp6: yes
      access-points:
        "yourssid":
          password: yourpassphrase"

After creating the yaml file, set the appropriate permissions and apply the netplan:

sudo chmod 600 /etc/netplan/*.yaml
sudo netplan apply

Ubuntu 23.04 - WIFI 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.

Ubuntu 23.04 - Installing New Software

Ubuntu provides the apt-get system which lets you manage pre-built applications. Before you do this you need to update Ubuntu's list of package versions and locations. This assumes you have a valid network connection to the internet.

apt-get update

For example, lets say you wanted to install openjdk for Java support. You can use the apt-cache command to search the local cache of Debian's packages.

root@ts-imx6:~# apt-cache search openjdk
jvm-7-avian-jre - lightweight virtual machine using the OpenJDK class library
freemind - Java Program for creating and viewing Mindmaps
icedtea-7-plugin - web browser plugin based on OpenJDK and IcedTea to execute Java applets
default-jdk - Standard Java or Java compatible Development Kit
default-jdk-doc - Standard Java or Java compatible Development Kit (documentation)
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)
icedtea-7-jre-jamvm - Alternative JVM for OpenJDK, using JamVM
openjdk-7-dbg - Java runtime based on OpenJDK (debugging symbols)
openjdk-7-demo - Java runtime based on OpenJDK (demos and examples)
openjdk-7-doc - OpenJDK Development Kit (JDK) documentation
openjdk-7-jdk - OpenJDK Development Kit (JDK)
openjdk-7-jre - OpenJDK Java runtime, using Hotspot Zero
openjdk-7-jre-headless - OpenJDK Java runtime, using Hotspot Zero (headless)
openjdk-7-jre-lib - OpenJDK Java runtime (architecture independent libraries)
openjdk-7-source - OpenJDK Development Kit (JDK) source files
uwsgi-app-integration-plugins - plugins for integration of uWSGI and application
uwsgi-plugin-jvm-openjdk-7 - Java plugin for uWSGI (OpenJDK 7)
uwsgi-plugin-jwsgi-openjdk-7 - JWSGI plugin for uWSGI (OpenJDK 7)                                              

In this case you will likely want openjdk-7-jre to provide a runtime environment, and possibly openjdk-7-jdk to provide a development environment.

Once you have the package name you can use apt-get to install the package and any dependencies. This assumes you have a network connection to the internet.

apt-get install openjdk-7-jre
# You can also chain packages to be installed
apt-get install openjdk-7-jre nano vim mplayer

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

Ubuntu 23.04 - 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
Note: 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.

Note: 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.

Ubuntu 23.04 - 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.

Buildroot

The full-featured Debian image may be too cumbersome for some applications. Applications that require faster bootup time or a smaller root filesystem will benefit greatly from using a lighter distribution like Buildroot. Using Buildroot for generating images makes it easy to keep software up to date, both userspace and kernel. Additionally, the use of Buildroot allows for building full images completely from source, with semi-reproducable builds, and full software license reports.

To assist customers heading down this path, we maintain our own Buildroot br2-external tree. This tree includes upstream Buildroot as a submodule, which eases updating between Buildroot releases. See the Buildroot manual for more information on Buildroot and br2-external trees.

In order to provide an easy transition from a larger Linux distribution to Buildroot, we provide and maintain two levels of configurations:

  • The base configuration for each device brings in hardware support to get the unit booted, but offers minimal software support and relies mostly on tools provided by BusyBox.
  • An "extra packages" defconfig that can be merged in with any of the base configurations in order to provide many additional packages to create an environment that is more consistent with larger Linux distributions.

The larger Buildroot configuration averages about 10 seconds of boot time, much of which is spent on networking. The base configurations can reduce this time significantly.

Our Buildroot br2-external currently uses the linux-5.10.y branch of our Linux LTS kernel repository for the majority of its supported platforms.


Note: Note that our base configurations include that device's utilities package where possible. Normally, these utilities (e.g. tshwctl, tsmicroctl, etc.) list the git hash of the build source in the help output. However, due to the Buildroot process, the git hash in these utilities reflects the git hash of Buildroot-ts, NOT of the utilities repository. There is no way to work around this without building the utilities outside of Buildroot.

Buildroot - Installing

When building Buildroot from source, the output files can be used to create a bootable microSD card and a bootable eMMC for the TS-7250-V3. The output files are also compatible with our USB Image Replicator.

The default configuration was designed to be as close to our stock Debian distribution. This includes our utilities like tsmicroctl, drivers, firmware, and software for the Wi-Fi and Bluetooth module, etc.

Buildroot - Building

Buildroot is intended to be completely cross-compiled from a host Linux workstation. This process creates a cross-compiler which is then used to build all target applications, kernel, etc., and then output a bootable image / tarball. The following instructions will create a bootable image / tarball for the target system:

Clone the repository:

git clone --recurse-submodules https://github.com/embeddedTS/buildroot-ts.git
cd buildroot-ts/

Configure the build:

# The following command uses a Buildroot script to merge two config files.
# The extra_packages_defconfig includes more usual packages to match our stock images
./buildroot/support/kconfig/merge_config.sh technologic/configs/extra_packages_defconfig technologic/configs/ts7250v3_defconfig

# A smaller base image can be made with bare hardware support using:
# make ts7250v3_defconfig

At this point, the default configuration can be modified if desired:

make menuconfig

And finally, start the build process:

make


The Buildroot process can take a large amount of time to build depending on available system resources. Note that if any changes occur in the config file, it is recommended to clean the build tree and start the process over. Buildroot ccache is not enabled by default, but can be to help speed up repeated builds. See the Buildroot manual for more information about ccache and Buildroot.

Once it is finished building, Buildroot will output a filesystem tarball to buildroot/output/images/rootfs.tar.xz. This file can be used with the Installing Buildroot instructions to get this tarball booted on the target device.


Buildroot - Cross Compiling

In order to generate a cross-compiler from Buildroot, first configure the target build as outlined in the first steps of the build instructions. Once configured, a separate make command can be issued to generate a tarball package of the cross-compiler. This can be unpacked to any location on the host Linux workstation's filesystem and then used to cross-compile additional applications for the target. The build, setup, and use of the cross-compiler can be done with the following steps:

# Be sure the target is configured first!
# The following command will output the cross-compiler package as well as build the target image completely if not built already
make sdk

# Unpack the tarball to new directory in the users home directory
# Note that the tarball name may be slightly different depending on how the toolchain is configured in Buildroot
mkdir ~/buildroot-toolchain
tar xf buildroot/output/images/arm-buildroot-linux-gnueabihf_sdk-buildroot.tar.gz -C ~/buildroot-toolchain/

# Update the path information for the toolchain (must be done when the tarball is unpacked, or if the root folder of the toolchain is moved!)
# Note that, as above, the path for the compiler may be slightly different depending on how the toolchain is configured in Buildroot
~/buildroot-toolchain/arm-buildroot-linux-gnueabihf_sdk-buildroot/relocate-sdk.sh

# Create a simple Hello World application source
cat << EOF > hello.c
#include <stdio.h>
void main(void) { printf("Hello!\n"); }
EOF

# Build a binary from the Hello World source that can be run on the target device
~/buildroot-toolchain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/arm-linux-gcc hello.c -o hello

# This cross compiler can be added to the user's PATH variable for easy access
export PATH=$PATH:~/buildroot-toolchain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
arm-linux-gcc hello.c -o hello

The hello binary can then be copied to the target device and executed on it.

Note that the make sdk command can be run at any time to generate the toolchain tarball. Even after Buildroot has generated the output image.


Buildroot is extremely flexible in its generation and use of a cross-compiler. See the Buildroot manual for more information on advanced use of the Buildroot generated toolchain as well as using Buildroot's generated cross-compiler as an external compiler for Buildroot.


Buildroot - Configuring Network

Buildroot implements the ip, ifconfig, route, etc., commands to manipulate the settings of interfaces. The first Ethernet interface is set up to come up automatically with our default configuration. The interfaces can also be manually set up:

# Bring up the CPU network interface
ifconfig eth0 up

# Set an IP address (assumes 255.255.255.0 subnet mask)
ifconfig eth0 192.168.0.50

# Set a specific subnet
ifconfig eth0 192.168.0.50 netmask 255.255.0.0

# Configure a default route. This is the server that provides an internet connection.
route add default gw 192.168.0.1

# Edit /etc/resolv.conf for the local DNS server
echo "nameserver 192.168.0.1" > /etc/resolv.conf

Most commonly, networks will offer DHCP which can be set up with one command:

# To setup the default CPU Ethernet port
udhcpc -i eth0
# All Ethernet ports can be made active and request DHCP addresses with:
udhcpc


To have network settings take effect on startup in Buildroot, edit /etc/network/interfaces:

# interface file auto-generated by Buildroot

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp
  pre-up /etc/network/nfs_check
  wait-delay 15

Note that the default network startup may timeout on some networks, e.g. network protocols such as STP can delay packet movement. This can be resolved in Buildroot by adding network configuration options to fail after a number of attempts (rather than a timeout) or retry for a DHCP lease indefinitely. For example, adding one of the following lines under the iface eth0 inet dhcp section:

  • udhcpc_opts -t 0 to infinitely retry
  • udhcpc_opts -t 5 to fail after five attempts.

See the man page for interfaces(5) for further information on the syntax of the interfaces file and all of the options that can be passed.

For more information on network configuration in general, Debian provides a great resource here that can be readily applied to Buildroot in most cases.


Buildroot - Installing New Software

Buildroot does not include a package manager by default (though it is possible to enable one). This means installing software directly on the platform can be cumbersome and is not the intended path when using Buildroot. It is recommended to modify the Buildroot configuration to include additional packages. See the Building Buildroot section for information on modifying the configuration to build additional packages.

If a desired package is not available in Buildroot, there are a number of options available moving forward. It is possible to add packages to the build process, though this does require some knowledge of Buildroot internals. Another option is to use the cross compiler that is output by Buildroot in order to compile packages on a host system and then copy them over to the target. It is also possible to install a toolchain directly on the device, and compile applications natively. The last option is the least recommended as it greatly increases the final image size and adds unnecessary complexity.


Buildroot - Setting Up SSH

The default configuration has Dropbear set up. Dropbear is a lightweight SSH 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 set. The default configuration does not set a password for the root user, nor are any other users configured.

passwd root

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.


Buildroot - Starting Automatically

Buildroot defaults to using the BusyBox init system, and all of our provided configurations use this as well. The following custom startup script uses this format. For information on other init systems that Buildroot can use, as well as creating startup scripts for these, see the Buildroot manual.

The most straightforward way to add an application to startup is to create a startup script. This example startup script that will toggle the red LED on during startup, and off during shutdown. In this case the script is named customstartup which can be changed as needed.

Create the file /etc/init.d/S99customstartup with the following contents. Be sure to set the script as executable!

#! /bin/sh
# /etc/init.d/customstartup

case "$1" in
  start)
    echo 1 > /sys/class/leds/red-led/brightness
    ## If you are launching a daemon or other long running processes
    ## this should be started with
    # nohup /usr/local/bin/yourdaemon &
    ;;
  stop)
    # if you have anything that needs to run on shutdown
    echo 0 > /sys/class/leds/red-led/brightness
    ;;
  *)
    echo "Usage: customstartup start|stop" >&2
    exit 3
    ;;
esac
  
exit 0
Note: The $PATH variable is not set up by default in init scripts so this will either need to be done manually or the full path to your application must be included.

Buildroot provides numerous mechanisms to create this file in the target filesystem at build time. See the Buildroot manual for more information on this.

This script will be automatically called at startup and shutdown thanks to the file location and naming. However, it can also be manually started or stopped:

/etc/init.d/S99customstartup start
/etc/init.d/S99customstartup stop

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/filesytem 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 sections below for details 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.


Image Replicator

This platform supports our Image Replicator tool. The Image Replicator tool is intended for use by developers as a means to write bootable images or filesystems on to a device's media (SD / eMMC / SATA / etc.) as part of their production or preparation process. In addition to writing media, the Image Replicator tool is capable of capturing images from a device's media and preparing them to be written to other devices.

The Image Replicator tool is a USB disk image that can be booted on a target device to capture or write its media directly without the need for a host workstation. The USB disk image is based on Buildroot and contains a set of scripts which handle the capture and write process. The process and its scripts are flexible and can be used as-is or adapted in to larger production processes to format and load data on to devices. The single USB drive can be used to capture images from a device, and then can be inserted in to other devices to write those same images on to other devices. The capture process is not necessary if it is not needed. Images for the target device can be copied to the USB drive, booted on compatible units, and have the target images written to that unit's media.


Image Capture Process

The image capture process performs the following steps. For more detailed information, see the Image Capture section below.

  1. If no valid images exist on the disk, image capture starts.
  2. For each valid media present on the unit, a bit for bit copy of the source is made.
  3. This image is mounted, sanitized (to remove unneeded files and allow safe copying of the image to other units), and saved as either a disk image or a tarball depending on the partition layout of the source disk.
  4. All images and tarballs are compressed, with both the output files having their MD5 hash saved as well as all of the files contained in the root partition having their MD5 hashes saved to a file for later verification.

The captured images and tarballs are named such that the USB Image Replicator disk can be immediately used to boot another unit and have it perform the Image Write process to write that unit's media with the captured images.

Note: When using this process, the USB drive used for the Image Replicator must be sized large enough to handle multiple compressed images as well as the uncompressed copy of the media image actively being worked with. If the image capture process runs out of space, the process will indicate a failure.


Image Write Process

The image write process performs the following steps. For more details information see the Image Write section below.

  1. For each valid media present on the unit, find the first valid source image file for it.
  2. If a source image exists for a media that is not present on the unit, then the process indicates a failure.
  3. If the source image is a tarball, format the target disk with an appropriate filesystem, and unpack it to the target disk, verifying all files against the MD5 hash file list after they are written.
  4. If the source image is a disk image, write that to the target disk. If an MD5 file for the disk image exists, read back the written disk and compare it to the hash.


Creating a USB Image Replicator Disk

Image Replicator USB disk images can be found below:

Disk image: ts7250v3-usb-image-replicator.dd.xz

Tarball: ts7250v3-usb-image-replicator-rootfs.tar.xz

Two types of USB Image Replicator images are available for this platform, a tarball and an actual disk image. They both have the same contents and are intended to provide different methods to write the Image Replicator tool to a USB disk.

Disk Image (.dd.xz)
The disk image is easier to write from different workstation OSs, will auto-expand to the full disk length on its first boot, and is intended to be used for image capture (and later image writing) due to its small size and auto-expansion process. We recommend this route for users who may not have access to a Linux workstation or need to capture images from a golden unit first.
Tarball Image (.tar.xz)
The tarball image is easiest to write from a Linux workstation, but requires creating a partition table on the USB disk (if one does not already exist), formatting the filesystem, and unpacking the tarball. It can readily be used for for both image capture and writing, but is the easiest route when image capture is not needed due to the auto-expansion process.


Note: It is recommended to use USB drives with solid-state media for this process. Slower USB drives, especially those with spinning media, may take too long to enumerate and the bootloader will not boot the Image Replicator disk. Additionally, the use of low quality, damaged, and/or worn out USB drives may cause unexpected errors that appear unrelated to the USB drive itself. If there are issues using the Image Replicator, we recommend first trying a new, fresh, high-quality USB drive from a trusted named brand.


Disk Image

This process uses a small disk image that can be written to a USB device. This disk image uses an ext3 filesystem which expands on its first boot to the full length of the disk before beginning the image capture process. This disk is recommended for users who may not have access to a Linux workstation or who need to capture images from a golden unit.

It is possible to use the disk image for just image writing, however, in order to ensure full disk space is available it is recommended to write the disk image to a USB drive, boot it on a unit, let the image capture process complete, insert the USB drive in to a workstation, and then remove the captured image files before copying in the desired image files for the target unit from the workstation.


Writing Disk Image From a Linux Workstation

The disk image can be written via the command line with the dd command (replace /dev/sdX with the correct USB device node):

xzcat <platform>-usb-image-replicator.dd.xz | dd of=/dev/sdX bs=1M conv=fsync

Graphical tools also exist for this purpose, for example balenaEtcher[1] offers this functionality.


Writing Disk Image From a Windows Workstation

A number of tools exist for writing an image to a USB drive, including (but not limited to) balenaEtcher[1] and Win32DiskImager[2]


Writing Disk Image From a MacOS Workstation

We recommend using a tool such as balenaEtcher[1] to write disk images.


  1. 1.0 1.1 1.2 embeddedTS is not affiliated with this tool. balenaEtcher version 1.5.101 tested in Windows 10 on 20220216
  2. embeddedTS is not affiliated with this tool. Win32DiskImager 1.0.0 tested in Windows 10 on 20220216. Cannot handle compressed images, must first decompress disk image.


Tarball

This process is easiest on a Linux workstation, but can be performs on other operating systems as well so long as they can support a compatible filesystem, the xz compression algorithm, as well as the tarball archive format. Note that in many cases, one of our computing platforms running our stock Linux image can be used if a Linux workstation is not available. After writing the tarball to a USB disk, the full length of the USB disk would be available to copy source images to in order to write them to other units.

The image replicator and scripts require a minimum of 50 MB; this plus the size of any target disk images or tarballs to be used dictates the minimum USB disk size required. The USB drive should have only a single partition, which is formatted ext2[1] / 3 / 4[2] or FAT32/vfat[3] Note that other filesystems are not compatible with U-Boot and therefore cannot be used.


Writing Tarball From a Linux Workstation

# This assumes USB drive is /dev/sdc:
sudo mkfs.ext3 /dev/sdc1
sudo mkdir /mnt/usb/
sudo mount /dev/sdc1 /mnt/usb/
sudo tar --numeric-owner -xf /path/to/<platform>-usb-image-replicator-rootfs.tar.xz -C /mnt/usb/
sudo umount /mnt/usb/


Writing Tarball From a Windows Workstation

It is recommended to use a third party tool, as native Windows archive tools have been observed to not work correctly. Tools such as 7-Zip[4] or PeaZip[5] are known working. It may also be possible to use Windows Subsystem for Linux following the Linux Workstation instructions above, but this has not been tested.

Note that some Windows tools may attempt to use the whole disk, rather than create a partition table. A partition table with a single partition is required for U-Boot support.

With a formatted USB disk, the archive can be unpacked to the root folder of the USB disk. Be sure to not unpack the tarball contents in to a new folder on the drive as this will not be bootable.


  1. The ext2 filesystem has a max file size limit as low at 16 GiB. This may cause issues for Image Capture.
  2. Use of ext4 may require additional options. U-Boot on some platforms does not support the 64-bit addressing added as the default behavior in recent revisions of mkfs.ext4. If using e2fsprogs 1.43 or newer, the options -O ^64bit,^metadata_csum may need to be used with ext4 for proper compatibility. Older versions of e2fsprogs do not need these options passed, nor are they needed for ext2 / 3.
  3. The FAT32 (supported by vfat in Linux) filesystem has a max file size limit of 4 GiB. This may cause issues for Image Capture.
  4. embeddedTS is not affiliated with this tool. 7-Zip 21.07 tested in Windows 10 on 20220222
  5. embeddedTS is not affiliated with this tool. PeaZip 7.2.0 tested in Windows 10 on 20220222

Running the Image Replicator Tool

Once a USB drive is formatted with the Image Replicator tool (see Creating a USB Image Replicator Disk for the correct files and process), boot to this USB drive (note that the Image Replicator already sets up the correct U-Boot boot scripts to boot to the USB drive, see the aforementioned section for details on how to make U-Boot call the scripts on the USB drive). This will start either image capture if no disk images/tarballs are present on the USB drive, or image write if there are disk images/tarballs present on the USB drive.

The Image Replicator tool, while in progress, will flash the green LED once per second while the red LED remains solidly lit. Upon completion, the red LED turns off and the green LED will slowly blink to indicate success, while a blinking red LED with the green LED off indicates a failure.

On each boot, startup scripts will check if the single partition of the USB drive can be expanded and do so if possible. If this process fails, then any further operations will not be run and the LEDs will blink to indicate a failure.


Image Capture

If no valid images to write exist on the booted USB Image Replicator drive, the image capture process starts automatically.

Note that while in progress, the USB Image Replicator drive is mounted read-write. It is not advised to remove power or disconnect the USB Image Replicator drive until the whole process has completed.

To help diagnose failures, files in /tmp/logs/ contain output from each capture process.

For each media present on the unit (SD / eMMC / SATA / etc.), the image capture process will do the following:

  1. Copy the entire media image to an appropriately named file on the USB Image Replicator drive, e.g. sdimage.dd. No data is written to the source media and it is never mounted. The source disk can follow the stock partition layout, or implement a customized one.
  2. Perform an fsck on the Linux rootfs partition in the image file. Note that, if deviating from the standard partition layout, it may be necessary to modify the scripts handling the capture process.
  3. Mount the Linux rootfs partition from the image file and sanitize the filesystem. The sanitization process removes temporary files (e.g. /log/ files), unique files (e.g. ssh private key files, machine ID files), adds a file to indicate that it is a custom image with the date as its contents, etc. The full list of operations can be found in this script. It may be necessary to modify this file for unique situations.
  4. If the media's partition layout uses only a single partition, the filesystem is packed in to a tarball on the USB Image Replicator drive which is appropriately named and compressed, e.g. sdimage.tar.xz. The image file is then unmounted and removed from the USB Image Replicator drive.
  5. If the media's partition layout uses multiple partitions, the image file is then unmounted, an md5sum of the image file taken, it is compressed and appropriately named on the USB Image Replicator drive, e.g. emmcimage.dd.xz, and then an md5sum of the compressed image is taken.

Note that when using this process, the USB Image Replicator drive that is used must be sized large enough to handle multiple compressed images as well as the uncompressed copy of the media image actively being worked with. If the image capture process runs out of space, the process will indicate a failure via the LEDs.

The images files captured are saved to the root folder of the USB Image Replicator drive. Upon completion, it is safe to remove power or unplug the USB drive.

For more details on the image capture process, see this script.


Image Write

This process is used to write existing images to media on a target unit. If appropriately named disk images or tarballs (see table below) are present in the root folder of the USB Image Replicator drive when booted, then the startup scripts will start the image writing process. The latest disk images we provide for our platforms can be downloaded from our FTP site, see the backup and restore section for links to these files.

Note that the USB Image Replicator drive remains read-only through the entire process but target devices may be mounted or actively written. It is not advised to remove power or disconnect the USB Image Replicator drive until the whole process has completed.

To help diagnose failures, files in /tmp/logs/ contain output from each writing process.

The Image Replicator script expects disk images or tarballs to have specific names to match the target media. The script will attempt to match tarball and then disk image names (in the order they are listed in the table below) for each target media, using the first file that is found to write to the target media. Note that symlinks can be used on the USB Image Replicator disk if formatted with a filesystem that supports symlinks. This can be used, for example, to write the same tarball to both SD and eMMC from only a single copy of the source tarball.

Upon completion, it is safe to remove power or unplug the USB drive.

For more details on the image write process, see this script.

The following table is the list of valid file names and how they are processed:

Target media Accepted filenames Description
SD Card

/sdimage.tar.xz

/sdimage.tar.bz2

/sdimage.tar.gz

/sdimage.tar

Tar of the filesystem. This will repartition the SD card to a single partition and extract this tarball to the filesystem. If present, a file named /md5sums.txt in the tarball will have its contents checked against the whole filesystem after the tarball is extracted. This md5sums.txt file is optional and can be omitted, but it must not be blank if present. This file is present in our official images and is created during image capture with the Image Replicator tool.

/sdimage.dd.xz

/sdimage.dd.bz2

/sdimage.dd.gz

/sdimage.dd

Disk image of the media. This will be written to the SD card block device directly. If present on the USB Image Replicator drive, a file named /sdimage.dd.md5 will be used to verify the data written to the SD card against this checksum. This file is provided with our official images and is created during image capture with the Image Replicator tool.
eMMC

/emmcimage.tar.xz

/emmcimage.tar.bz2

/emmcimage.tar.gz

/emmcimage.tar

Tar of the filesystem. This will repartition the eMMC to a single partition and extract this tarball to the filesystem. If present, a file named /md5sums.txt in the tarball will have its contents checked against the whole filesystem after the tarball is extracted. This md5sums.txt file is optional and can be omitted, but it must not be blank if present. This file is present in our official images and is created during image capture with the Image Replicator tool.

/emmcimage.dd.xz

/emmcimage.dd.bz2

/emmcimage.dd.gz

/emmcimage.dd

Disk image of the media. This will be written to the eMMC block device directly. If present on the USB Image Replicator drive, a file named /emmcimage.dd.md5 will be used to verify the data written to the SD card against this checksum. This file is provided with our official images and is created during image capture with the Image Replicator tool.
U-Boot

/u-boot-dtb.bin

/SPL

U-Boot binary blob. This will be written to the bootloader area of eMMC. Note that both files are required for U-Boot, if either file is missing then the Image Replicator tool will not write either of them. If the file /SPL.md5 is present on the USB drive, this will be used to verify the data written to disk.

Building the Image Replicator from Source

The Image Replicator tool uses Buildroot to create the bootable USB disk image and tarball. See the project repository on github for information on compatibility and instructions on building: https://github.com/embeddedTS/buildroot-ts


microSD Card

Note: Our Image Replicator tool can be used to automate this process.


Download your distribution tarball:

These instructions assume an SD card with one partition. Most SD cards ship this way by default, but if there are modified partitions, a utility such as 'gparted' or 'fdisk' may be needed to remove the existing partition table and recreate it with a single partition.

Note: That the partition table must be "MBR" or "msdos", as the "GPT" partition table format is NOT supported by U-Boot.

Using other OSs

At this time, we're unable to provide assistance with writing SD cards for our products from non-Linux based operating systems. We acknowledge however, that there are methods to write images and files from a variety of difference operating systems. If a native installation of Linux is unavailable, we recommend using a Virtual Machine. See the Getting Started section for links to common virtualization software and Linux installation.

Using a Linux workstation

An SD card can be written to allow it to be bootable. Download the above file and write this from a Linux workstation using the information below. A USB SD adapter can be used to access the card; or if the workstation supports direct connection of SD cards, that can be used instead. Once inserted in to the workstation, it is necessary to discover which /dev/ device corresponds with the inserted SD card before the image can be written.

Option 1: using 'lsblk'


Newer distributions include a utility called 'lsblk' which allows simple identification of the intended card.

Note: This command may need to be run as the root user:
$ lsblk

NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sdY      8:0    0   400G  0 disk 
├─sdY1   8:1    0   398G  0 part /
├─sdY2   8:2    0     1K  0 part 
└─sdY5   8:5    0     2G  0 part [SWAP]
sr0     11:0    1  1024M  0 rom  
sdX      8:32   1   3.9G  0 disk 
├─sdX1   8:33   1   7.9M  0 part 
├─sdX2   8:34   1     2M  0 part 
├─sdX3   8:35   1     2M  0 part 
└─sdX4   8:36   1   3.8G  0 part

In this case the, SD card is 4GB, so sdX is the target device and already contains 4 partitions. Note that sdX is not a real device, it could be sda, sdb, mmcblk0, etc. embeddedTS is not responsible for any damages cause by using the improper device node for imaging an SD card. The instructions below to write to the device will destroy the partition table and any existing data!

Option 2: Using 'dmesg'


After plugging in the device, the 'dmesg' command can be used to list recent kernel events. When inserting a USB adapter, the last few lines of 'dmesg' output will be similar to the following (note that this command may need to be run as the root user):

$ dmesg
...
scsi 54:0:0:0: Direct-Access     Generic  Storage Device   0.00 PQ: 0 ANSI: 2
sd 54:0:0:0: Attached scsi generic sg2 type 0
sd 54:0:0:0: [sdX] 3862528 512-byte logical blocks: (3.97 GB/3.84 GiB)
...

In this case, sdX is shown as a 3.97GB card with a single partition. Note that sdX is not a real device, it could be sda, sdb, mmcblk0, etc. embeddedTS is not responsible for any damages cause by using the improper device node for imaging an SD card. The instructions below to write to the device will destroy the partition table and any existing data!

Running these commands will write the SD card to our default latest image.

# Verify nothing else has the disk mounted with 'mount'
# If partitions are mounted automatically, they can be unmounted with
sudo umount /dev/sdX1

sudo mkfs.ext3 /dev/sdX1
sudo mkdir /mnt/sd
sudo mount /dev/sdX1 /mnt/sd/
wget https://files.embeddedTS.com/ts-arm-sbc/ts-7250-v3-linux/distributions/debian/tsimx6ul-debian-bullseye-latest.tar.xz

sudo tar -xJf tsimx6ul-debian-bullseye-latest.tar.xz -C /mnt/sd
sudo umount /mnt/sd
sync
Note: The ext4 filesystem can be used instead of ext3, but it may require additional options. U-Boot does not support the 64bit addressing added as the default behavior in recent revisions of mkfs.ext4. If using e2fsprogs 1.43 or newer, the options "-O ^64bit,^metadata_csum" must be used with ext4 for proper compatibility. Older versions of e2fsprogs do not need these options passed nor are they needed for ext3.


After the image is written, the files can all be verified on disk against the original files created in the tarball. Reinsert the disk to verify any block cache is gone, then run the following:

mount /dev/sdX1 /mnt/sd
cd /mnt/sd/
sudo md5sum --quiet -c md5sums.txt
umount /mnt/sd
sync

The 'md5sum' command will report any differences between files and their checksums. Any differences are an indication of failure to write data or a damaged disk.

Booted from SD

Note: Our Image Replicator tool can be used to automate this process.


These instructions assume the TS-7250-V3 is booted from SD card all the way to Linux. They also assume that the eMMC is unmodified, with a single partition. If the partition table has been modified, a utility such as 'gparted' or 'fdisk' may be needed to remove the existing partition table and recreate it with a single partition. Note that the partition table must be "MBR" or "msdos", the "GPT" partition table format is not supported by U-Boot.

# Verify nothing else has the partition mounted
umount /dev/mmcblk1p1

mkfs.ext3 /dev/mmcblk1p1
mount /dev/mmcblk1p1 /mnt/emmc
wget https://files.embeddedTS.com/ts-arm-sbc/ts-7250-v3-linux/distributions/debian/tsimx6ul-debian-bullseye-latest.tar.xz
tar -xf tsimx6ul-debian-bullseye-latest.tar.xz -C /mnt/emmc
umount /mnt/emmc
sync
Note: The ext4 filesystem can be used instead of ext3, but it may require additional options. U-Boot does not support the 64bit addressing added as the default behavior in recent revisions of mkfs.ext4. If using e2fsprogs 1.43 or newer, the options "-O ^64bit,^metadata_csum" must be used with ext4 for proper compatibility. Older versions of e2fsprogs do not need these options passed nor are they needed for ext3.

Once written, the files on disk can be verified to ensure they are the same as the source files in the archive. To do so, run the following commands:

mount /dev/mmcblk1p1 /mnt/emmc
cd /mnt/emmc/
md5sum --quiet -c md5sums.txt
cd -
umount /mnt/emmc
sync

The 'md5sum' command will report any differences between files and their checksums. Any differences are an indication of failure to write data or a damaged disk.

Features

ADC

This board supports 5 channels of 12-bit ADC using an integrated ADC in the i.MX6UL CPU. All channels can sample 0-30VDC, but channels 1-3 can optionally sample 0-20mA as a current loop. To minimize noise, the ADC pins use a dedicated analog ground available on the even pins of the header. See the #ADC Header section for more details.

These ADCs are accessed through the IIO layer in Linux. This provides ADC samples up to 6ksps between all channels. The simplest API for slow speed acquisition is through iio_attr:

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-30VDC 0-20mA
3 AN_CH2 2198000.adc voltage1 0-30VDC 0-20mA
5 AN_CH3 2198000.adc voltage5 0-30VDC 0-20mA
7 AN_CH4 2198000.adc voltage8 0-30VDC N/A
8 AN_CH5 2198000.adc voltage9 0-30VDC N/A

The current loops are enabled/disabled 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

The libiio library provides simple access to the IO. The fastest API is in C which will get about 6ksps.

/* Build with gcc adc-test.c -o adc-test -liio 
 * Gets ~6ksps
 * 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;
}

The python bindings currently achieve about 2ksps with similar code.

#!/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

The Wi-Fi option for this platform also includes a Bluetooth 5.0 LE module. Support for Bluetooth is provided by the BlueZ project. BlueZ has support for many different profiles for HID, A2DP, and many more. Refer to the BlueZ documentation for more information. Please see our BLE Examples page for information on installing the latest BlueZ release, getting started, and using demo applications.

Both Wi-Fi and Bluetooth can be active at the same time on this platform. Note however, that either the Wi-Fi interface needs to be not brought up if Wi-Fi is unused, or it needs to actively connect to an access point or act as an access point. The Bluetooth module can be activated with the following commands:

For Bluez versions found on Debian Stretch and below:

# Enable Bluetooth, and load the firmware
echo BT_POWER_UP > /dev/wilc_bt
sleep 1
echo BT_DOWNLOAD_FW > /dev/wilc_bt
sleep 1

# Attach the BLE device to the system, increase the baud, and enable flow control
hciattach /dev/ttymxc2 any 115200 noflow
sleep 1
hcitool cmd 0x3F 0x0053 00 10 0E 00 01
stty -F /dev/ttymxc2 921600 crtscts

# Note that no other HCI commands should be used! In older versions of BlueZ, HCI commands exist alongside bluetoothd, however HCI commands can interfere with the bluetoothd stack.


For newer versions of BlueZ found on Debian Buster or newer, or newer versions of BlueZ built from source:

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 running at 921600 baud with flow control, and is fully set up ready to be controlled by various components of 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

Please note that the Bluetooth module requires the modem control lines CTS and RTS as flow control when running at higher baud rates. It is possible to run the module at the initial 115200 baud if the flow control lines are unwanted.

The module supports some other commands as well:

# 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 that use the Linux SocketCAN implementation. These are available on the #COM3 Header

These interfaces can be brought up with:

ip link set can0 up type can bitrate 1000000
ip link set can1 up type can bitrate 1000000


At this point, the port can be used with standard SocketCAN libraries. In Debian, we provide the utilities 'cansend' and 'candump' to test the ports or as a simple packet send/receive tool. In order to test the port, tie CAN_H to the CAN_H pin of the bus, doing the same for the CAN_L pin. Then use the following commands:

candump can0
# This command will echo all data received on the bus to the terminal

cansend can0 7Df#03010c
#This command will send out the above CAN packet to the bus


The above example packet is designed to work with the Ozen Elektronik myOByDic 1610 ECU simulator to read the RPM speed. In this case, the ECU simulator would return data from candump with:

 <0x7e8> [8] 04 41 0c 60 40 00 00 00 
 <0x7e9> [8] 04 41 0c 60 40 00 00 00 

In the output above, columns 6 and 7 are the current RPM value. This shows a simple way to prove out the communication before moving to another language.

The following example sends the same packet and parses the same response in C:

#include <stdio.h>
#include <pthread.h>
#include <net/if.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <assert.h>
#include <linux/can.h>
#include <linux/can/raw.h>

int main(void)
{
	int s;
	int nbytes;
	struct sockaddr_can addr;
	struct can_frame frame;
	struct ifreq ifr;
	struct iovec iov;
	struct msghdr msg;
	char ctrlmsg[CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE(sizeof(__u32))];
	char *ifname = "can0";
 
	if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
		perror("Error while opening socket");
		return -1;
	}
 
	strcpy(ifr.ifr_name, ifname);
	ioctl(s, SIOCGIFINDEX, &ifr);
	addr.can_family  = AF_CAN;
	addr.can_ifindex = ifr.ifr_ifindex;
 
	if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		perror("socket");
		return -2;
	}
 
 	/* For the ozen myOByDic 1610 this requests the RPM guage */
	frame.can_id  = 0x7df;
	frame.can_dlc = 3;
	frame.data[0] = 3;
	frame.data[1] = 1;
	frame.data[2] = 0x0c;
 
	nbytes = write(s, &frame, sizeof(struct can_frame));
	if(nbytes < 0) {
		perror("write");
		return -3;
	}

	iov.iov_base = &frame;
	msg.msg_name = &addr;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = &ctrlmsg;
	iov.iov_len = sizeof(frame);
	msg.msg_namelen = sizeof(struct sockaddr_can);
	msg.msg_controllen = sizeof(ctrlmsg);  
	msg.msg_flags = 0;

	do {
		nbytes = recvmsg(s, &msg, 0);
		if (nbytes < 0) {
			perror("read");
			return -4;
		}

		if (nbytes < (int)sizeof(struct can_frame)) {
			fprintf(stderr, "read: incomplete CAN frame\n");
		}
	} while(nbytes == 0);

	if(frame.data[0] == 0x4)
		printf("RPM at %d of 255\n", frame.data[3]);
 
	return 0;
}

See the Kernel's CAN documentation here. Other languages have bindings to access CAN such as Python, Java using JNI.

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

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.

CPU

This device uses the i.MX6UL CPU, running at 696 MHz, based upon a Cortex-A7 core and targeting low power consumption.

Refer to NXP's documentation for more detailed information on the i.MX6UL.

GPIO

The i.MX6UL CPU and FPGA GPIO are exposed using a kernel character device. This interface provides a set of files and directories for interacting with GPIO which can be used from any language that interact with special files in linux using ioctl() or similar. For our platforms, we pre-install the "libgpiod" library and binaries. Documentation on these tools can be found here. This section only covers using these userspace tools and does not provide guidance on using the libgpiod library in end applications. Please see the libgpiod documentation for this purpose.

A user with suitable permissions to read and write /dev/gpiochip* files can immediately interact with GPIO pins. For example, to read DIO header pin 1

gpioget 50004010.fpga_gpio 1

Multiple pins in the same chip can be read simultaneously by passing multiple pin numbers separated by spaces.

The GPIO commands let you specify a /dev/gpiochip device, a bank "number" that refers to teh gpio chip, or a gpiochip "label". We recommend using the label instead of the number or device for portability between kernels. The label will work consistently no matter the driver probe order.

To write to a pin, the 'gpioset' command is used. For example, set MIKRO_RESET# to low, then high:

gpioset 50004054.fpga_gpio 0=0
gpioset 50004054.fpga_gpio 0=1

Multiple pins in the same chip can be set simultaneously by passing multiple pin=value pairs separated by spaces.

If a call with 'gpioset' or 'gpioget' fails with "Device or resource busy," that means that specific GPIO is claimed by another device. The command 'cat /sys/kernel/debug/gpio' can be used to get a list of all of the system GPIO and what has claimed them.

The "gpioinfo" tool can be used to get information on GPIO on the board, and "gpiofind" can be used to look up IO by their schematic net names. For example:

root@tsimx6ul:~# gpiofind I2C_3_DAT
gpiochip2 5

From the gpiochip number "gpiodetect" can be used to determine the label name:

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)

It is also possible to use the output from gpiofind with gpioset/get to directly look up a pin from its schematic net name:

# Set LCD_PIN12 high
gpioset `gpiofind LCD_PIN12`=1
# Set LCD_PIN12 low
gpioset `gpiofind LCD_PIN12`=0
Schematic Net Name Chip Label Line Location
AN_CH1 209c000.gpio 0 ADC Pin 1
AN_CH2 209c000.gpio 1 ADC Pin 3
I2C_1_CLK 209c000.gpio 2 Onboard I2C
I2C_1_DAT 209c000.gpio 3 Onboard I2C
EN_SD_CARD_3.3V [1] 209c000.gpio 4 Onboard
AN_CH3 209c000.gpio 5 ADC Pin 5
ETH_MDIO 209c000.gpio 6 Onboard
ETH_MDC 209c000.gpio 7 Onboard
AN_CH4 209c000.gpio 8 ADC Pin 7
AN_CH5 209c000.gpio 9 ADC Pin 9
SEL_XBEE_USB [2] 209c000.gpio 11 Onboard USB MUX
FPGA_RESET [3] 209c000.gpio 13 Onboard
MAGNET_IRQ 209c000.gpio 14 Onboard
CONSOLE_TXD 209c000.gpio 16 Onboard
CONSOLE_RXD 209c000.gpio 17 Onboard
EN_RED_LED# [4] 209c000.gpio 18 Onboard LED5
EN_GRN_LED# [4] 209c000.gpio 19 Onboard LED2
EN_XBEE_USB [1] 209c000.gpio 21 Onboard
UART3_TXD 209c000.gpio 24 Bluetooth UART
UART3_RXD 209c000.gpio 25 Bluetooth UART
UART3_CTS# 209c000.gpio 26 Bluetooth UART
UART3_RTS# 209c000.gpio 27 Bluetooth UART
UART4_TXD 209c000.gpio 28 Modem UART
UART4_RXD 209c000.gpio 29 Modem UART
EN_DIO_FET 209c000.gpio 30 DIO Header pin 4
NIM_PWR_ON 209c000.gpio 31 Onboard
ENET1_RX_DATA0 20a0000.gpio 0 Onboard
ENET1_RX_DATA1 20a0000.gpio 1 Onboard
ENET1_RX_EN 20a0000.gpio 2 Onboard
ENET1_TX_DATA0 20a0000.gpio 3 Onboard
ENET1_TX_DATA1 20a0000.gpio 4 Onboard
ENET1_TX_EN 20a0000.gpio 5 Onboard
ENET1_TX_CLK 20a0000.gpio 6 Onboard
ENET1_RX_ER 20a0000.gpio 7 Onboard
ENET2_RX_DATA0 20a0000.gpio 8 Onboard
ENET2_RX_DATA1 20a0000.gpio 9 Onboard
ENET2_RX_EN 20a0000.gpio 10 Onboard
ENET2_TX_DATA0 20a0000.gpio 11 Onboard
ENET2_TX_DATA1 20a0000.gpio 12 Onboard
ENET2_TX_EN 20a0000.gpio 13 Onboard
ENET2_TX_CLK 20a0000.gpio 14 Onboard
ENET2_RX_ER 20a0000.gpio 15 Onboard
EMMC_CMD 20a0000.gpio 16 Onboard eMMC
EMMC_CLK 20a0000.gpio 17 Onboard eMMC
EMMC_D0 20a0000.gpio 18 Onboard eMMC
EMMC_D1 20a0000.gpio 19 Onboard eMMC
EMMC_D2 20a0000.gpio 20 Onboard eMMC
EMMC_D3 20a0000.gpio 21 Onboard eMMC
EN_USB_5V [5] 20a4000.gpio 0 Onboard
WIFI_IRQ 20a4000.gpio 1 Onboard
UART4_CTS# 20a4000.gpio 3 Modem UART
WDOG# 20a4000.gpio 4 Onboard
I2C_3_DAT 20a4000.gpio 5 I2C data
I2C_3_CLK 20a4000.gpio 6 I2C clock
ISA_RESET 20a4000.gpio 7 PC104 B2
ISA_IOCHK 20a4000.gpio 8 PC104 A1
LCD_PIN7 20a4000.gpio 9 LCD Header pin 7
LCD_PIN8 20a4000.gpio 10 LCD Header pin 8
LCD_PIN9 20a4000.gpio 11 LCD Header pin 9
LCD_PIN10 20a4000.gpio 12 LCD Header pin 10
CAN_1_TXD 20a4000.gpio 13 Onboard CAN
CAN1_RXD_3V 20a4000.gpio 14 Onboard CAN
LCD_PIN11 20a4000.gpio 15 LCD Header pin 11
LCD_PIN12 20a4000.gpio 16 LCD Header pin 12
LCD_PIN13 20a4000.gpio 17 LCD Header pin 13
LCD_PIN14 20a4000.gpio 18 LCD Header pin 14
LCD_WR# 20a4000.gpio 19 LCD Header pin 6
LCD_EN 20a4000.gpio 20 LCD Header pin 5
LCD_RS 20a4000.gpio 21 LCD Header pin 3
SYS_RESET# [6] 20a4000.gpio 22 Onboard
SD_CMD 20a4000.gpio 23 Onboard MicroSD
SD_CLK 20a4000.gpio 24 Onboard MicroSD
SD_D0 20a4000.gpio 25 Onboard MicroSD
SD_D1 20a4000.gpio 26 Onboard MicroSD
SD_D2 20a4000.gpio 27 Onboard MicroSD
SD_D3 20a4000.gpio 28 Onboard MicroSD
FPGA_FLASH_SELECT [1] 20a8000.gpio 0 Onboard
DETECT_94-120 [1] 20a8000.gpio 1 Onboard
EIM_AD_08 20a8000.gpio 2 EIM Interface to FPGA
EIM_AD_09 20a8000.gpio 3 EIM Interface to FPGA
EIM_AD_10 20a8000.gpio 4 EIM Interface to FPGA
EIM_AD_11 20a8000.gpio 5 EIM Interface to FPGA
EIM_AD_12 20a8000.gpio 6 EIM Interface to FPGA
EIM_AD_13 20a8000.gpio 7 EIM Interface to FPGA
EIM_AD_14 20a8000.gpio 8 EIM Interface to FPGA
EIM_AD_15 20a8000.gpio 9 EIM Interface to FPGA
EIM_IRQ 20a8000.gpio 10 EIM Interface to FPGA
EIM_BLK 20a8000.gpio 11 EIM Interface to FPGA
SPI_3_CS# 20a8000.gpio 12 WIFI SPI bus
SPI_3_CLK 20a8000.gpio 13 WIFI SPI bus
SPI_3_MOSI 20a8000.gpio 14 WIFI SPI bus
SPI_3_MISO 20a8000.gpio 15 WIFI SPI bus
EIM_WAIT# 20a8000.gpio 16 EIM Interface to FPGA
EIM_CS0# 20a8000.gpio 17 EIM Interface to FPGA
EIM_OE# 20a8000.gpio 18 EIM Interface to FPGA
EIM_WE# 20a8000.gpio 19 EIM Interface to FPGA
EIM_LBA# 20a8000.gpio 20 EIM Interface to FPGA
EIM_AD_00 20a8000.gpio 21 EIM Interface to FPGA
EIM_AD_01 20a8000.gpio 22 EIM Interface to FPGA
EIM_AD_02 20a8000.gpio 23 EIM Interface to FPGA
EIM_AD_03 20a8000.gpio 24 EIM Interface to FPGA
EIM_AD_04 20a8000.gpio 25 EIM Interface to FPGA
EIM_AD_05 20a8000.gpio 26 EIM Interface to FPGA
EIM_AD_06 20a8000.gpio 27 EIM Interface to FPGA
EIM_AD_07 20a8000.gpio 28 EIM Interface to FPGA
GYRO_INT 20ac000.gpio 0 Accelerometer/gyro interrupt
FPGA_IRQ 20ac000.gpio 1 EIM Interface to FPGA
EN_EMMC_3.3V [1] 20ac000.gpio 2 Onboard eMMC
GPIO_DVFS [7] 20ac000.gpio 3 Onboard
WIFI_RESET# 20ac000.gpio 5 Onboard WIFI
EN_WIFI_PWR 20ac000.gpio 6 Onboard WIFI
EN_CL_1 20ac000.gpio 7 ADC Current loop enable
EN_CL_2 20ac000.gpio 8 ADC Current loop enable
EN_CL_3 20ac000.gpio 9 ADC Current loop enable
DIO_PIN1 50004010.fpga_gpio 1 DIO Header pin 1
DIO_PIN3 50004010.fpga_gpio 2 DIO Header pin 3
DIO_PIN5 50004010.fpga_gpio 3 DIO Header pin 5
DIO_PIN7 50004010.fpga_gpio 4 DIO Header pin 7
DIO_PIN8 50004010.fpga_gpio 5 DIO Header pin 8
DIO_PIN9 50004010.fpga_gpio 6 DIO Header pin 9
DIO_PIN11 50004010.fpga_gpio 7 DIO Header pin 11
DIO_PIN13 50004010.fpga_gpio 8 DIO Header pin 13
DIO_PIN15 50004010.fpga_gpio 9 DIO Header pin 15
DIO_SPI_MISO [8] 50004010.fpga_gpio 10 SPI MISO
DIO_SPI_CS# [9] 50004010.fpga_gpio 11 DIO Header pin 6
SILAB_CLK [1] 50004010.fpga_gpio 12 Onboard
SILAB_DAT [1] 50004010.fpga_gpio 13 Onboard
DIO_SPI_CLK [9] 50004010.fpga_gpio 14 DIO Header pin 14
DIO_SPI_MOSI [9] 50004010.fpga_gpio 15 DIO Header pin 12
ISA_AEN [10] 50004040.fpga_gpio 0 PC104 Header pin A11
ISA_BALE 50004040.fpga_gpio 1 PC104 Header pin B28
ISA_TC 50004040.fpga_gpio 2 PC104 Header pin B27
ISA_ENDX 50004040.fpga_gpio 3 PC104 Header pin B08
EN_NIMBEL_3V3 50004040.fpga_gpio 4 Nimbel VIN voltage
ISA_IORDY 50004040.fpga_gpio 5 PC104 Header pin A10
ISA_REFRESH 50004040.fpga_gpio 6 PC104 Header pin B19
ISA_DRQ1 50004040.fpga_gpio 7 PC104 Header pin B18
ISA_DACK1 50004040.fpga_gpio 8 PC104 Header pin B17
ISA_DRQ2 50004040.fpga_gpio 9 PC104 Header pin B06
ISA_DACK2 50004040.fpga_gpio 10 PC104 Header pin B26
EN_NIMBEL_4V 50004040.fpga_gpio 11 Nimbel VIN voltage
ISA_DRQ3 50004040.fpga_gpio 12 PC104 Header pin B16
ISA_DACK3 50004040.fpga_gpio 13 PC104 Header pin B15
MIKRO_RESET# 50004054.fpga_gpio 0 mikroBUS Header pin 2
MIKRO_AN 50004054.fpga_gpio 1 mikroBUS Header pin 1
MIKRO_INT 50004054.fpga_gpio 2 mikroBUS Header pin 15
MIKRO_180 [11] 50004054.fpga_gpio 3 Onboard
MIKRO_PWM [12] 50004054.fpga_gpio 4 mikroBUS Header pin 16
MIKRO_SPI_CS# [13] 50004054.fpga_gpio 5 mikroBUS Header pin 3
MIKRO_SPI_CLK [13] 50004054.fpga_gpio 6 mikroBUS Header pin 4
MIKRO_SPI_MISO [13] 50004054.fpga_gpio 7 mikroBUS Header pin 5
MIKRO_SPI_MOSI [13] 50004054.fpga_gpio 8 mikroBUS Header pin 6
MIKRO_TXD [14] 50004054.fpga_gpio 9 mikroBUS Header pin 13
MIKRO_RXD [14] 50004054.fpga_gpio 10 mikroBUS Header pin 14
MIKRO_I2C_DAT [15] 50004054.fpga_gpio 11 mikroBUS Header pin 11
MIKRO_I2C_CLK [15] 50004054.fpga_gpio 12 mikroBUS Header pin 12
ISA_DAT00 [10] 5000405c.fpga_gpio 0 PC104 Header pin A9
ISA_DAT01 [10] 5000405c.fpga_gpio 1 PC104 Header pin A8
ISA_DAT02 [10] 5000405c.fpga_gpio 2 PC104 Header pin A7
ISA_DAT03 [10] 5000405c.fpga_gpio 3 PC104 Header pin A6
ISA_DAT04 [10] 5000405c.fpga_gpio 4 PC104 Header pin A5
ISA_DAT05 [10] 5000405c.fpga_gpio 5 PC104 Header pin A4
ISA_DAT06 [10] 5000405c.fpga_gpio 6 PC104 Header pin A3
ISA_DAT07 [10] 5000405c.fpga_gpio 7 PC104 Header pin A2
ISA_DAT08 [10] 5000405c.fpga_gpio 8 PC104 Header pin C11
ISA_DAT09 [10] 5000405c.fpga_gpio 9 PC104 Header pin C12
ISA_DAT10 [10] 5000405c.fpga_gpio 10 PC104 Header pin C13
ISA_DAT11 [10] 5000405c.fpga_gpio 11 PC104 Header pin C14
ISA_DAT12 [10] 5000405c.fpga_gpio 12 PC104 Header pin C15
ISA_DAT13 [10] 5000405c.fpga_gpio 13 PC104 Header pin C16
ISA_DAT14 [10] 5000405c.fpga_gpio 14 PC104 Header pin C17
ISA_DAT15 [10] 5000405c.fpga_gpio 15 PC104 Header pin C18
ISA_ADD_00 [10] 50004064.fpga_gpio 0 PC104 Header pin A31
ISA_ADD_01 [10] 50004064.fpga_gpio 1 PC104 Header pin A30
ISA_ADD_02 [10] 50004064.fpga_gpio 2 PC104 Header pin A29
ISA_ADD_03 [10] 50004064.fpga_gpio 3 PC104 Header pin A28
ISA_ADD_04 [10] 50004064.fpga_gpio 4 PC104 Header pin A27
ISA_ADD_05 [10] 50004064.fpga_gpio 5 PC104 Header pin A26
ISA_ADD_06 [10] 50004064.fpga_gpio 6 PC104 Header pin A25
ISA_ADD_07 [10] 50004064.fpga_gpio 7 PC104 Header pin A24
ISA_ADD_08 [10] 50004064.fpga_gpio 8 PC104 Header pin A23
ISA_ADD_09 [10] 50004064.fpga_gpio 9 PC104 Header pin A22
ISA_ADD_10 [10] 50004064.fpga_gpio 10 PC104 Header pin A21
ISA_ADD_11 [10] 50004064.fpga_gpio 11 PC104 Header pin A20
ISA_ADD_12 [10] 50004064.fpga_gpio 12 PC104 Header pin A19
ISA_ADD_13 [10] 50004064.fpga_gpio 13 PC104 Header pin A18
ISA_ADD_14 [10] 50004064.fpga_gpio 14 PC104 Header pin A17
ISA_ADD_15 [10] 50004064.fpga_gpio 15 PC104 Header pin A16
ISA_ADD_16 [10] 5000406c.fpga_gpio 0 PC104 Header pin A15
ISA_ADD_17 [10] 5000406c.fpga_gpio 1 PC104 Header pin A14
ISA_ADD_18 [10] 5000406c.fpga_gpio 2 PC104 Header pin A13
ISA_ADD_19 [10] 5000406c.fpga_gpio 3 PC104 Header pin A12
ISA_IOR [10] 5000406c.fpga_gpio 4 PC104 Header pin B14
ISA_IOW [10] 5000406c.fpga_gpio 5 PC104 Header pin B13
ISA_MEMR [10] 5000406c.fpga_gpio 6 PC104 Header pin B12
ISA_MEMW [10] 5000406c.fpga_gpio 7 PC104 Header pin B11
ISA_CN_D03 2-0020 0 PC104 Header pin D03
ISA_CN_D04 2-0020 1 PC104 Header pin D04
ISA_CN_D05 2-0020 2 PC104 Header pin D05
ISA_CN_D06 2-0020 3 PC104 Header pin D06
ISA_CN_D07 2-0020 4 PC104 Header pin D07
ISA_CN_D08 2-0020 5 PC104 Header pin D08
ISA_CN_D09 2-0020 6 PC104 Header pin D09
ISA_CN_D10 2-0020 7 PC104 Header pin D10
ISA_CN_D11 2-0020 8 PC104 Header pin D11
ISA_CN_D12 2-0020 9 PC104 Header pin D12
ISA_CN_D13 2-0020 10 PC104 Header pin D13
ISA_CN_D14 2-0020 11 PC104 Header pin D14
ISA_CN_D15 2-0020 12 PC104 Header pin D15
  1. 1.0 1.1 1.2 1.3 1.4 1.5 1.6 This is controlled automatically on startup to give the SD card a clean reset, but otherwise thsi should not be toggled manually.
  2. default is input with pulldown (0). If driven high, this muxes the bottom port of the dual high USB port to the XBEE header. If low or input, no USB is present on this header. This USB is needed for cell modules, but interferes with older serial modules where these usb pins are reserved. See your XBEE/Nimbelink device's datasheet to verify if USB is needed.
  3. This is handled automatically on startup
  4. 4.0 4.1 See /sys/class/leds/ for interacting with these LEDs rather than toggling the GPIO directly.
  5. High by default. This allows power cycling USB perihperals in the field.
  6. Ethernet PHY and USB hub reset. This is automatically controlled during startup and should not be toggled.
  7. This is under control of the kernel and should never be manually adjusted
  8. This pin is input only
  9. 9.0 9.1 9.2 This pin cannot be controlled as a GPIO until Syscon 0x08 bit 10 is set
  10. 10.00 10.01 10.02 10.03 10.04 10.05 10.06 10.07 10.08 10.09 10.10 10.11 10.12 10.13 10.14 10.15 10.16 10.17 10.18 10.19 10.20 10.21 10.22 10.23 10.24 10.25 10.26 10.27 10.28 10.29 10.30 10.31 10.32 10.33 10.34 10.35 10.36 10.37 10.38 10.39 10.40 This pin cannot be controlled as a GPIO until Syscon 0x08 bit 8 is set
  11. This is used to detect if the Mikrobus socket is reversed.
  12. This pin cannot be controlled as a GPIO until Syscon 0x08 bit 6 is set
  13. 13.0 13.1 13.2 13.3 This pin cannot be controlled as a GPIO until Syscon 0x08 bit 4 is set
  14. 14.0 14.1 This pin cannot be controlled as a GPIO until Syscon 0x08 bit 7 is set
  15. 15.0 15.1 This pin cannot be controlled as a GPIO until Syscon 0x08 bit 5 is set

eMMC Interface

The i.MX6UL SD card controller supports the MMC specification, the TS-7250-V3 includes a soldered down eMMC IC to provide on-board flash media.

Our default software image contains 2 partitions:

Device Contents
/dev/mmcblk0 eMMC block device
/dev/mmcblk0boot0 eMMC boot partition
/dev/mmcblk0boot1 eMMC boot partition
/dev/mmcblk0p1 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/mmcblk0. The eMMC 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 GiB. 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 '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.

WARNING: 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.
Note: 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-7100 utilities github repository.

WARNING: Do not run this on the p1 revision boards

The script must be run when boot from any media other than eMMC, such as 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/mmcblk0

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 two 10/100 ethernet controllers 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

The i.MX6UL CPU Ethernet supports 1588 PTP (PTPv1 & PTPv2).

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

FPGA Registers

The TS-7250-V3 FPGA is connected to the CPU over the WEIM bus. This provides 8-bit, 16-bit, or 32-bit access to the FPGA mapped at 0x5000_0000.

For example, to read the FPGA information at the first register of the syscon:

root@ts-imx6ul:~# memtool md -l 0x50004000+4
50004000: 00000006

FPGA Top Level Decode

Offset Description
0x0000 UART 16550 #0
0x0100 Opencore SPI controller #0
0x0120 Opencore SPI controller #1
0x0180 FPGA ADC 0
0x0188 Opencore I2C Controller #0
0x1a8 FPGA PWM 0
0x4000 FPGA Syscon


FPGA 16550

This FPGA includes a 16550 UART peripheral that can be used as a standard Linux serial port. It is not recommended to interact directly with these registers.

FPGA SPI

This FPGA includes a pair of SPI master devices based on the opencore SPI controller supporting up to 19.8MHz speeds. This supports onboard peripherals with the DIO header, and the mikrobus header.

Bus CS Description
0 0 DIO Header /dev/spidev4.0
1 #FRAM
2 FPGA SPI Flash [1]
1 0 Mikrobus /dev/spidev5.0
  1. It is not recommended to access this (/dev/mtdblock0) flash unless suggested by support.

The /dev/spidev* devices can be accessed from Linux's userspace. See the kernel spidev documentation for more information on interfacing with the SPI peripherals from C.

Other languages also have bindings to interface with spidev:

This controller is based on the opencore, but includes modifications to ignore "RX_NEGEDGE/TX_NEGEDGE" bits (CTRL 9/10), and instead add CPOL and CPHA (CTRL 14/15) to support all SPI 4 modes.

FPGA Syscon

The FPGA syscon is the main system control block of the FPGA. Contained in this region is the FPGA GPIO, PWM, and IRQ control. It is not recommended to interact directly with these registers unless directed to do so by other manual sections.

Some registers are dual purpose, having separate read and write functionality; while others may only have write functionality. Registers that do not read and write the same are indicated with "(RD)" and "(WR)" notation. All other registers read and write the same data set. Any unlisted register addresses are Reserved / Undefined.

Offset Bits Description
0x00 (RD) 31:0 Revision and Info Register.
0x08 31:13 Reserved (Write 0)
14 dio_uart7_en (RW) (reset 0). If set, UART 7 (ttyS15) is available on the DIO header.
13 dio_uart6_en (RW) (reset 0). If set, UART 6 (ttyS14) is available on the DIO header.
12 revc_strap (RO) (1 indicates REV C or later)
11 isa_bclk_en (RW) (reset 0). If set, enables a 7MHz clock on B20/BCLK.
10 dio_spi_gpio_en (RW) (reset 0). If set, DIO Header SPI pins are GPIO
9 uart_use_rts_for_txen (RW) (reset 0). If set, Instead of automatic TXEN, RTS is used on all TXEN instead.
8 isa_gpio_en (RW) (reset 0). If set all ISA pins are GPIO.
7 mikro_uart_gpio_en (RW) (reset 0). If set, mikrobus txd/rxd are GPIO
6 mikro_pwm_gpio_en (RW) (reset 0). If set, mikrobus PWM is GPIO
5 mikro_i2c_gpio_en (RW) (reset 0). If set, mikrobus I2C pins are GPIO
4 mikro_spi_gpio_en (RW) (reset 0). If set, mikrobus SPI pins are GPIO
3:0 Board Strap value
0x10 (RD) 15:0 DIO bank 0 Pin State
0x10 (WR) 15:0 DIO bank 0 Data Set
0x12 (WR) 15:0 DIO bank 0 Output Enable Set
0x14 (RD) 15:0 DIO bank 0 Data
0x14 (WR) 15:0 DIO bank 0 Data Clear
0x16 (RD) 15:0 DIO bank 0 Output Enable
0x16 (WR) 15:0 DIO bank 0 Output Enable Clear
0x1C (WR) 15:4 Reserved
3:0 Duty cycle of LCD_BIAS
0x20 (WR) 31:0 Fractional clock generator [1]
0x24 (RD) 31:0 IRQ Status
0x24 (WR) 31:0 Fractional PWM generator
0x40 (RD) 15:0 DIO bank 1 Pin State
0x40 (WR) 15:0 DIO bank 1 Data Set
0x42 (WR) 15:0 DIO bank 1 Output Enable Set
0x44 (RD) 15:0 DIO bank 1 Data
0x44 (WR) 15:0 DIO bank 1 Data Clear
0x46 (RD) 15:0 DIO bank 1 Output Enable
0x46 (WR) 15:0 DIO bank 1 Output Enable Clear
0x48 31:0 IRQ mask
0x50 31:0 ISA memory window
0x54 (RD) 15:0 DIO bank 2 Pin State
0x54 (WR) 15:0 DIO bank 2 Data Set
0x56 (WR) 15:0 DIO bank 2 Output Enable Set
0x58 (RD) 15:0 DIO bank 2 Data
0x58 (WR) 15:0 DIO bank 2 Data Clear
0x5a (RD) 15:0 DIO bank 2 Output Enable
0x5a (WR) 15:0 DIO bank 2 Output Enable Clear
0x5c (RD) 15:0 DIO bank 3 Pin State
0x5c (WR) 15:0 DIO bank 3 Data Set
0x5e (WR) 15:0 DIO bank 3 Output Enable Set
0x60 (RD) 15:0 DIO bank 3 Data
0x60 (WR) 15:0 DIO bank 3 Data Clear
0x5a (RD) 15:0 DIO bank 3 Output Enable
0x62 (WR) 15:0 DIO bank 3 Output Enable Clear
0x64 (RD) 15:0 DIO bank 4 Pin State
0x64 (WR) 15:0 DIO bank 4 Data Set
0x66 (WR) 15:0 DIO bank 4 Output Enable Set
0x68 (RD) 15:0 DIO bank 4 Data
0x68 (WR) 15:0 DIO bank 4 Data Clear
0x6a (RD) 15:0 DIO bank 4 Output Enable
0x6a (WR) 15:0 DIO bank 4 Output Enable Clear
0x6c (RD) 15:0 DIO bank 5 Pin State
0x6c (WR) 15:0 DIO bank 5 Data Set
0x6e (WR) 15:0 DIO bank 5 Output Enable Set
0x70 (RD) 15:0 DIO bank 5 Data
0x70 (WR) 15:0 DIO bank 5 Data Clear
0x72 (RD) 15:0 DIO bank 5 Output Enable
0x72 (WR) 15:0 DIO bank 5 Output Enable Clear
  1. Note that this is also used for UART clock generation.

FPGA IRQs

Bit Description
31:21 Reserved
20 I2C IRQ
19 ADC sample ready
18 mikro_int
17 PC104 IRQ9
16 PC104 IRQ7
15 PC104 IRQ6
14 PC104 IRQ5
13 PC104 IRQ3
12:11 Reserved
10 SPI core #1 IRQ
9 SPI core #0 IRQ
8:0 UART 8:0 IRQs

FPGA ADC

The TS-7250-V3 includes a simple low speed ADC to support the analog input on mikrobus modules.

  • 304hz sample rate
  • 8-bits resolution
  • 10kohm input impedance
  • 0-6V input range
  • 3% absolute accuracy

This ADC is accessed using the IIO subsystem. One time samples can be read from /sys/:

root@tsimx6:~# cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw 
127
root@tsimx6:~# cat /sys/bus/iio/devices/iio\:device0/in_voltage0_scale
25.781250000

The raw value is 0-255, and the scale value converts this to real world values. For example, 127*25.78125=3274.21875mV.

Faster samples, up to 304hz, are available using the buffer interface.

root@tsimx6:~# echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage0_en
root@tsimx6:~# iio_readdev iio:device0 | hexdump -C
WARNING: High-speed mode not enabled
00000000  80 00 00 00 00 00 00 00  4c a8 d9 e8 b7 33 83 15  |........L....3..|
00000010  7f 00 00 00 00 00 00 00  df 8f 22 e9 b7 33 83 15  |.........."..3..|
00000020  7f 00 00 00 00 00 00 00  b7 aa 55 e9 b7 33 83 15  |..........U..3..|
00000030  80 00 00 00 00 00 00 00  e9 d7 86 e9 b7 33 83 15  |.............3..|

In this case, column 1 that starts with 80 is the ADC value. Columns 2-8 are 0 padding. Columns 9-16 are the 64-bit ns timestamp. This is accurate to approximately 10us.

The ADC buffers can also be filled using libiio directly:

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
#include <iio.h>

static bool stop;
static void handle_sig(int sig)
{
	stop = true;
}

int main (int argc, char **argv)
{
	const struct iio_data_format *fmt;
	static struct iio_context *ctx;
	static struct iio_buffer *rxbuf;
	struct iio_channel *chn;
	struct iio_device *dev;

	/* Catch ctrl+c */
	signal(SIGINT, handle_sig);

	ctx = iio_create_default_context();
	assert(ctx);

	iio_context_set_timeout(ctx, 10000);

	assert(iio_context_get_devices_count(ctx) > 0);
	dev = iio_context_find_device(ctx, "50000180.mikro_adc");
	assert(dev);

	chn = iio_device_get_channel(dev, 0); /* voltage0 */
	assert(chn);

	iio_channel_enable(chn);
	fmt = iio_channel_get_data_format(chn);
	assert(fmt);

	rxbuf = iio_device_create_buffer(dev, 256, false);
	assert(rxbuf);

	printf("%s\n", iio_channel_get_id(chn));
	while (!stop) {
		ssize_t nbytes;
		uint8_t *dat;

		nbytes = iio_buffer_refill(rxbuf);
		if (nbytes < 0) {
			printf("Error refilling buf %d\n",(int) nbytes);
			break;
		}
		for (dat = iio_buffer_first(rxbuf, chn); 
			dat < (uint8_t *)iio_buffer_end(rxbuf);
			dat += iio_buffer_step(rxbuf)) {
			printf("%d\n", dat[0]);
			//printf("%.0f,\n", (float)dat[0] * fmt->scale);
		}
	}

	iio_buffer_destroy(rxbuf);
	iio_channel_disable(chn);
	iio_context_destroy(ctx);

	return 0;
}

For most users we recommend using the existing linux drivers, but the hardware core can be found at 0x50000180. It is a single 32-bit register:

Bits Reset value Description
31:24 0x55 Reserved
23:16 0x0 sample
15:3 0x0 Reserved
2 0 sample_missed [1]
1 0 sample_ready [2]
0 1 standby_en
  1. This means when the core updated sample_ready to 1, it was already 1. Cleared on read.
  2. Cleared on read

FPGA I2C

The TS-7250-V3 supports the opencore I2C controller which is used for Mikrobus I2C.

The Mikrobus i2c port is /dev/i2c-4. Scan for devices with:

i2cdetect 4

This will show what addresses ack on the bus. Linux i2c-tools (i2cdetect, i2cget, i2cset) can be used to interface with devices, or custom clients can be written.

FPGA PWM

The TS-7250-V3 includes a PWM core that supports 10-bit duty/period, a 79.2mhz input clock, and 12 values of input clock shift.

Linux supports this API through the /sys/ interface using file I/O. First export the pwm channel to enable it:

# Export PWM channel 0
echo 0 > /sys/class/pwm/pwmchip0/export
File Description
/sys/class/pwm/pwmchip0/pwm0/period Period in nanoseconds. Must be bigger than the duty cycle or writes will fail. Can only change when the pwm is disabled.
/sys/class/pwm/pwmchip0/pwm0/duty_cycle Duty cycle in nanoseconds. Can change at any time, must be less than period.
/sys/class/pwm/pwmchip0/pwm0/enable When 1, pwm is outputting. When 0, outputs idle state of the PWM.
/sys/class/pwm/pwmchip0/pwm0/polarity When "normal", idle high and duty cycle low. When "inversed", idle low and duty cycle high. A valid period must be set before this can be changed.

For example, for a 50hz signal with 25% duty cycle:

# Set Period to 20ms
echo 20000000 > /sys/class/pwm/pwmchip0/pwm0/period
# Set duty cycle to 5ms
echo 5000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
# Enable PWM and output 50hz signal
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

# Duty cycle can be changed while it is enabled
echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle

The Linux PWM API will attempt to arrive at the exact period at the cost of the duty cycle resolution. For the most possible duty cycle resolution use one of the max period ns values from the table below.

Shift PWM Input Frequency (hz) Max Period (ns) Max Period (hz)
0 79200000 12917 77419
1 39600000 25833 38710
2 19800000 51667 19355
3 9900000 103333 9677
4 4950000 206667 4839
5 2475000 413333 2419
6 1237500 826667 1210
7 618750 1653333 605
8 309375 3306667 302
9 154687 6613333 151
10 77343 13226795 76
11 38671 26453932 38

If period is set to one of these values, the full 10 bits of duty cycle is available. Past that, the Linux API will use the closest available value. Debug output can be enabled with:

echo "file pwm-ts.c +p" > /sys/kernel/debug/dynamic_debug/control

If this is enabled, the kernel can output additional information after setting a frequency:

echo 0 > /sys/class/pwm/pwmchip0/export
# 10ms period:
echo 10000000 > /sys/class/pwm/pwmchip0/pwm0/period
# 5ms duty cycle:
echo 5000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
dmesg | tail

This will output:

[   75.758146] ts-pwm 500001a8.mikro_pwm: cycle=1293661 shift=10 cnt=773
[   75.758184] ts-pwm 500001a8.mikro_pwm: shift=10 cnt=773 duty_cnt=387

The last value in cnt indicates how much resolution is available for the duty cycle at this given period. In the best case there are 10 bits (0-2047) to specify duty cycle, but this above example is 0-773 to arrive at this particular period. You can determine the duty cycle increments with period / cnt. From the above example:

10000000 / 773 = 12936.61

The duty cycle can then be configured in increments of 12936ns. Smaller values will round to the closest value.

This PWM will allow a max speed of 79.2MHz / 3 = 26.4MHz, but this will sacrifice all of the available duty cycle except an on/50%/off. The slowest speed is highest divisor at 38hz.

While the Linux driver is recommended for most users, the PWM core is located at 0x500001a8.

Offset Bits Description
0x0 15:2 Reserved
1 Inversed (0 = idle high, duty cycle low), (1 = idle low, duty cycle high)
0 Enabled
0x2 15:10 Reserved
9:0 Period
0x4 15:10 Reserved
9:0 Duty Cycle
0x6 15:4 Reserved
3:0 shift (Clock frequency = 79200000 / (1 >> shift))

PC104 Bus

The TS-7250-V3 includes an ISA bus for compatibility with PC104 peripherals. Arm itself has not traditionally had an ISA bus as part of its architecture, so this behaves differently than an x86 where ISA is typically used.

To access the PC104 bus in userspace, open, read, and write the files here:

/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 an alternate pinout
mem8 8-bit strobes on MEMR/MEMW
mem16 16-bit strobes on MEMR/MEMW
memalt16 16-bit strobes on MEMR/MEMW with an alternate pinout

Any programming language can interface with these using standard file IO. Open a file descriptor to one or more of these files, and seek to the offset of the address you are accessing. Use read/write calls to access data on these busses.

For 16-bit accesses the address must always be aligned to an even byte, and reads/writes must always access multiple of 2 bytes at a time.

For C, or languages with a foreign function interface, we provide a library / header which can be used to handle these accesses.

For python, a simple pc104.py can access the bus:

#!/usr/bin/env python3

import os

class PC104:
    ISA_PATH = "/sys/bus/platform/devices/50004050.fpgaisa/"

    def __init__(self):
        self.io8fd = os.open(self.ISA_PATH + "io8", os.O_RDWR | os.O_SYNC)
        self.io16fd = os.open(self.ISA_PATH + "io16", os.O_RDWR | os.O_SYNC)
        self.io16altfd = os.open(self.ISA_PATH + "ioalt16", os.O_RDWR | os.O_SYNC)
        self.mem8fd = os.open(self.ISA_PATH + "mem8", os.O_RDWR | os.O_SYNC)
        self.mem16fd = os.open(self.ISA_PATH + "mem16", os.O_RDWR | os.O_SYNC)
        self.mem16altfd = os.open(self.ISA_PATH + "memalt16", os.O_RDWR | os.O_SYNC)

    def _read(self, fd, addr, size):
        os.lseek(fd, addr, os.SEEK_SET)
        return os.read(fd, size)

    def _write(self, fd, addr, data):
        os.lseek(fd, addr, os.SEEK_SET)
        os.write(fd, data)

    # IO 8-bit methods
    def io_8_read(self, addr):
        return int.from_bytes(self._read(self.io8fd, addr, 1))

    def io_8_write(self, addr, val):
        self._write(self.io8fd, addr, val.to_bytes(1))

    # IO 16-bit methods
    def io_16_read(self, addr):
        return int.from_bytes(self._read(self.io16fd, addr, 2))

    def io_16_write(self, addr, val):
        self._write(self.io16fd, addr, val.to_bytes(2))

    # IO 16-bit alternate methods
    def io_16_alt_read(self, addr):
        return int.from_bytes(self._read(self.io16altfd, addr, 2))

    def io_16_alt_write(self, addr, val):
        self._write(self.io16altfd, addr, val.to_bytes(2))

    # Memory 8-bit methods
    def mem_8_read(self, addr):
        return int.from_bytes(self._read(self.mem8fd, addr, 1))

    def mem_8_write(self, addr, val):
        self._write(self.mem8fd, addr, val.to_bytes(1))

    # Memory 16-bit methods
    def mem_16_read(self, addr):
        return int.from_bytes(self._read(self.mem16fd, addr, 2))

    def mem_16_write(self, addr, val):
        self._write(self.mem16fd, addr, val.to_bytes(2))

    # Memory 16-bit alternate methods
    def mem_16_alt_read(self, addr):
        return int.from_bytes(self._read(self.mem16altfd, addr, 2))

    def mem_16_alt_write(self, addr, val):
        self._write(self.mem16altfd, addr, val.to_bytes(2))

    def close(self):
        os.close(self.io8fd)
        os.close(self.io16fd)
        os.close(self.io16altfd)
        os.close(self.mem8fd)
        os.close(self.mem16fd)
        os.close(self.mem16altfd)


if __name__ == "__main__":
    pc104 = PC104()
    print(f'PC/104 io8 0x161 = {hex(pc104.io_8_read(0x161))}')

There is also a command line utility to access the pc104 bus:

root@tsimx6:~# pc104_peekpoke 
Usage pc104_peekpoke <io/mem> <8/16/alt16> <address> [value]
	Eg: pc104_peekpoke io 8 0x140

For kernel access to the PC104 bus see the header here. Using the existing kernel driver will handle locking between userspace and any kernel drivers such as PC104 based UARTs.

On PC104 peripherals these typically have IRQs such as IRQ5/IRQ6/IRQ7. On ARM these are mapped to other IRQ numbers. Using the fpga_intc driver, interrupts on 14/15/16 correspond with the pins on the PC104 header labelled IRQ 5, 6, and 7.

We strongly recommend using the methods described above for accessing the PC104 bus. For users needing to understand the implementation, the registers are described below.

The FPGA presents a 32-bit memory window at 0x50004050 which follows this format:

Bits Description
31 Busy/Go [1]
30 1 = IO, 0 = MEM [2]
29 1 = 8-bit, 0 = 16-bit
28 1 = read cycle, 0 = write cycle
27 0 = standard pinout, 1 = TS Pinout [3]
26:0 Address or Data [4]
  1. On read, a 1 indicates the existing transaction is already busy. A 0 indicates it is available. If written to 0 then bits 26:0 specify address. If written to 1 this starts the bus cycle.
  2. Cycle type indicates if we use IOW/IOR pins, or MEMR/MEMW.
  3. The TS pinout is used on some platforms to remove the need for the 40-pin connnector while still supporting 16-bit peripherals. This does not affect 8-bit accesses. When enabled this uses these pins for the upper 16-bit.
    PC104 pin Data bit
    B4 8
    B17 9
    B18 10
    B25 11
    B20 12
    B26 13
    B27 14
    B28 15
  4. Accepts a written addr when BUSY/GO = 0. This is data on a write when Busy/GO=1, or on a read after BUSY/GO reads 0 after being written to 1.

PC104 Bus Timing

The TS-7250-V3 PC104 cycles are approximately 1us per access in total, on either 8 or 16-bit. The timing is not user configurable, but is known to work with most typical ISA compatible devices such as a 16550, SJA1000, and more.

PC104 Bus MMAP

On some previous PC104 implementations the PC104 bus was able to be directly memory mapped. While it is not possible on this hardware to access the bus the same way, we do have a compatibility layer that is able to expose the PC104 bus as if it were memory mapped.

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>

#include "pc104.h"

volatile uint8_t *bus;

int main(int argc, char **argv)
{
	uint8_t i = 0;

	bus = pc104_mmap_init();
	assert(bus != NULL);
	if(bus[0x140] != 0x9b) {
		fprintf(stderr, "TS-RELAY8 not found\n");
	}
	while(1) {
		bus[0x142] = i++;
		usleep(10 * 1000);
	}

}

Accesses below 0x100000 cause IO accesses. Accesses from 0x100000 to 0x200000 cause MEM accesses. This code turns these mmap accesses into pc104_io/mem_8/16 calls.

This example must be compiled with:

gcc -marm test.c pc104.c -o test

PC104 Peripherals

The TS-7250-V3 is compatible with most of our PC104 devices. We do not test third party PC104 devices.

WARNING: Do not use third party PC104 power supplies. The -12 and -5V rails are incompatible with our PC104 implementation.

This list is of our current PC104 peripherals. While none are electrically incompatible, some may require additional driver work in order to function on this platform.

Product Compatibility Notes
TS-ADC16 yes
TS-ADC24 No
TS-BAT3 No
TS-BAT10 yes Current example code does not support IRQs.
TS-DIO24 yes Current example code does not support IRQs.
TS-DIO64 yes Current example code does not support IRQs.
TS-ISO485 yes
TS-MULTI104 yes
TS-RELAY8 yes
TS-SER1 yes
TS-SER2 yes
TS-SER4 yes
TS-12W yes prevents board sleep modes
TS-13W yes prevents board sleep modes
TS-5620 TBD
TS-9422 yes
TS-9500 No
TS-9600 TBD
TS-9700 yes
TS-IRIDIUM TBD
TS-ETH2 TBD
TS-ETH10 TBD
TS-ETH100 TBD
TS-RF2-AERO TBD
TS-RF2-CF TBD
TS-7KV No
TS-RF2-ZIGBEE TBD
TS-CAN1 TBD
TS-NVRAM TBD
TS-NVRAM2 TBD
TS-MODEM2 TBD
TS-ADC16
TS-ADC16
Product Page
alt 16-bit IO

The TS-ADC16 provides 16 channels of 16-bit analog to digital conversion at 4 channels of 12-bit digital to analog conversion, 4 digital inputs, 4 16-bit edge counters, and 1 digital output. On the TS-7250-V3 this will support a max sample rate of 16khz with 2 channels enabled.

Refer to the TS-ADC16 manual for register / hardware documentation.

This example assumes only JP3 is installed on the TS-ADC16

# Verify the TS-ADC16 is detected.  This should return 0x3E in the lower byte
pc104_peekpoke io alt16 0x100

The TS-ADC16 can FIFO up to 512 samples before it overruns and stops the state machine. For continuous samples the max speed is limited by how fast Linux is able to access this FIFO. If the CPU load is too great that this cannot empty the FIFO fast enough, the ADCDLY should be increased for consistent sampling.

To make Linux more deterministic the governor should be changed to performance:

echo "performance" > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor

The default "ondemand" scheduler is beneficial for power savings, but when the system detects a load and it changes clock speed this can stop Linux from scheduling other processes too long that the FIFO can overflow.

The below sample c code will pull data from the FIFO as fast as possible and output it to stdout in binary form.

#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <unistd.h>
#include <time.h>
#include <math.h>

#include "pc104.h"

#define TSADC16_BASE 0x100
#define SAMPLE_SIZE 1024

/* 
 * The TS-7250-V3 
 * 1/32000000=31.35ns increments
 2000 * 31.35ns = 62.7us.  1/0.0000627 = 15949hz sample rate
 */
#define ADCDLY 2000

/* 
 * We always sample from the lowest channel to the max channel 
 * https://docs.embeddedTS.com/TS-ADC16#ADC_pins
 */
#define MAX_CHAN_PAIR 0

int main(int argc, char **argv)
{
	uint16_t reg;
	int i, fifocnt, ret;

	if(isatty(fileno(stdout))) {
		fprintf(stderr, "Pipe to a file for raw ADC data\n");
		return 1;
	}

	pc104_init();
	if((pc104_io_16_alt_read(TSADC16_BASE) & 0xff) != 0x3E) {
		fprintf(stderr, "TS-ADC16 not detected");
		return 1;
	}

	assert(MAX_CHAN_PAIR < 8);

	/* Set ADCDLY */
	pc104_io_16_alt_write(TSADC16_BASE + 0x4, ((uint32_t)ADCDLY) >> 16);
	pc104_io_16_alt_write(TSADC16_BASE + 0x6, ((uint32_t)ADCDLY) & 0xffff);

	/* Set ADCCFG
	 * Trigger with SYSCOM bit
	 * Single ended
	 * 0v to 5V */
	reg = 0x160 | (MAX_CHAN_PAIR << 1);
	pc104_io_16_alt_write(TSADC16_BASE + 0x2, reg);
	pc104_io_16_alt_write(TSADC16_BASE + 0x2, reg | 0x1);
	do {
		reg = pc104_io_16_alt_read(TSADC16_BASE + 0x8);
		fifocnt = reg >> 6;
		if(!fifocnt) {
			reg = pc104_io_16_alt_read(TSADC16_BASE + 0x2);
			if(reg & 0x1)
				continue;
			fprintf(stderr, "Increase ADCDLY, can't keep up\n");
			return 1;
		}

		for (i = 0; i < fifocnt; i++) {
			uint16_t buffer = pc104_io_16_alt_read(TSADC16_BASE + 0xa);
			ret = write(1, &buffer, sizeof(uint16_t));
			assert(ret == sizeof(uint16_t));
		}
	} while(1);

	return 0;
}

This will continuously output raw binary data from the lowest to highest enabled ADC channel.


TS-BAT10
TS-BAT10
Product Page
8-bit IO

The TS-BAT10 is a 2000mAH 5V battery backup.

Refer to the TS-BAT10 manual for hardware documentation.

This example will charge the battery when it is low and has power, or shutdown when it does not have power and 3.3V is low.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <unistd.h>

#include "pc104.h"

#define TSBAT10_BASE 0x110
 
typedef enum
{
	SW1_OPEN = 1 << 7,
	OVER33V = 1 << 6,
	VIN_OK = 1 << 5,
	BATTERY_EN = 1 << 4,
	BAT1CHARGING = 1 << 3,
	BAT1CHARGED = 1 << 2,
	BAT2CHARGING = 1 << 1,
	BAT2CHARGED = 1 << 0
} StatusFlags;
 
typedef enum
{
	RESERVED = 1 << 7,
	BAT2SLOWCHARGE = 1 << 6,
	BAT1SLOWCHARGE = 1 << 5,
	BAT2CHARGEEN = 1 << 4,
	BAT1CHARGEEN = 1 << 3,
	BAT2TIMERDISABLE = 1 << 2,
	BAT1TIMERDISABLE = 1 << 1,
	SOFTDISABLE = 1 << 0
} ConfigurationFlags;

int main(int argc, char **argv)
{
	uint8_t conf;
	pc104_init();

	if(argc > 1) { /* Poweroff only */
		pc104_io_8_write(TSBAT10_BASE, SOFTDISABLE);
		return 0;
	}

	conf = BAT1CHARGEEN | BAT2CHARGEEN;
	while(1) {
		uint8_t status;
		status = pc104_io_8_read(TSBAT10_BASE);

		if(status & BATTERY_EN) {
			printf("Battery disabled by JP4\n");
			goto SLEEP;
		}

		if(!(status & VIN_OK)) {
			/* If we dont have 5v, and 3.3V is low, shut down*/
			if(!(status & OVER33V)) {
				printf("TS-BAT10 is low & has no VIN.  Shutting down!\n");
				system("shutdown -h now");
			} else { 
				printf("VIN not ok, but 3.3V not low yet\n");
			}
		} else {
			if(!(status & OVER33V)) {
				/* If battery is not > 3.3V, charge */
				printf("Battery is low, VIN present.  Charging.\n");
				pc104_io_8_write(TSBAT10_BASE, conf);
			} else {
				printf("VIN OK, but battery is charged\n");
			}
		}
SLEEP:		
		sleep(5);
	}

	return 0;
}

Compile and install this with:

gcc tsbat10.c pc104.c -o tsbat10
cp tsbat10 /usr/local/bin/

Under systemd create a service file at /etc/systemd/system/tsbat10.service

[Unit]
Description=TS-BAT10 daemon

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

[Install]
WantedBy=multi-user.target

Enable and start this service with:

systemctl enable --now tsbat10.service
TS-DIO24
TS-DIO24
Product Page
8-bit IO

The TS-DIO24 provides 24 0-5V digital I/O. The I/O connector is an Opto-22 compatible interface that provides 16 I/O points configurable as input or output (24 mA as outputs) as well as 4 dedicated outputs capable of driving 48 mA and 4 dedicated outputs capable of sinking 1 Amp

Refer to the TS-DIO24 manual for register / hardware documentation:

The TS-DIO24 currently does not have a kernel driver which would be needed to use the interrupts. The Digital inputs/outputs however can be used from userspace without a driver.

This example assumes no jumpers are installed on the TS-DIO24.

# Verify the TS-DIO24 is detected.  This should return 0x54
pc104_peekpoke io 8 0x100

# Set A0 to 5V and turn off the rest of A
pc104_peekpoke io 8 0x105 0x1

# Set PORT B to to high outputs, and set PORT C to an input
pc104_peekpoke io 8 0x106 0xff # PORT B Data
pc104_peekpoke io 8 0x104 0x2 # B output, C input

# Read PORT C
pc104_peekpoke io 8 0x107
TS-DIO64
TS-DIO64
Product Page
8-bit IO

The TS-DIO64 provides 64 digital I/O points (32 inputs plus 32 open drain outputs) through two 34-pin locking connectors that are compatible with ribbon cables. The outputs are capable of sinking 200mA for pins 1-22, or 23-32 can sink up to 400mA. These sinking outputs support up to 40V.

Refer to the TS-DIO64 manual for register / hardware documentation:

The Digital inputs/outputs can be used from userspace without a driver.

This example assumes no jumpers are installed on the TS-DIO64.

# Verify the board is present.  Should return 0xA4
pc104_peekpoke io 8 0x100

# Enable the sink on pin 1, and all other outputs off
pc104_peekpoke io 8 0x104 0x1 # Pins 1-7
pc104_peekpoke io 8 0x105 0x0 # Pins 9-16
pc104_peekpoke io 8 0x106 0x0 # Pins 17-24
pc104_peekpoke io 8 0x107 0x0 # Pins 25-32

# Read IO
pc104_peekpoke io 8 0x108 # Pins 1-7
pc104_peekpoke io 8 0x109 # Pins 9-16
pc104_peekpoke io 8 0x10a # Pins 17-24
pc104_peekpoke io 8 0x10b # Pins 25-32
TS-ISO485
TS-ISO485
Product Page
8-bit IO

The TS-ISO485 provides two isolated half duplex RS-485 ports, or two isolated RS-422 ports. Refer to the TS-ISO485 manual for more information about the hardware UART usage.

This peripheral implements 16550A based UARTs which require a kernel driver. Under Linux this requires a device tree change. See the Kernel compile section for more details about getting set up the compile the kernel.

In general for 16550 UARTs we recommend these kernel config options:

CONFIG_HZ_PERIODIC=y
CONFIG_HZ_1000=y
CONFIG_SERIAL_8250_TS=m

Once booted the driver can be loaded with modprobe: ``` modprobe 8250_ts ```

The default kernel is ideal for low power, but has some tradeoffs with latency. A 16550 only has a 16 byte FIFO and needs to be serviced with relatively low latency, so a the above options make the latency far more deterministic. These changes are necessary to run at 115200 and not drop any data.

This below example will set up the serial devices for a TS-ISO485 with the IRQ2 and IRQ4 jumpers installed. Open the device tree at arch/arm/boot/dts/imx6ul-ts7250v3.dtsi. Add the highlighted section to the device tree and recompile.

pc104bus: fpgaisa@50 {
    ...
	#address-cells = <0x1>;
	#size-cells = <0x1>;

	/* TS-ISO485 COMA */
	ts16550@3e8 {
		compatible = "technologic,ts16550";

		reg = <0x3e8 8>;
		interrupt-parent = <&fpga_intc>;
		interrupts = <15 IRQ_TYPE_LEVEL_HIGH>; // PC/104 IRQ 6
	};

	/* TS-ISO485 COMB */
	ts16550@2e8 {
		compatible = "technologic,ts16550";

		reg = <0x2e8 8>;
		interrupt-parent = <&fpga_intc>;
		interrupts = <16 IRQ_TYPE_LEVEL_HIGH>; // PC/104 IRQ 7
	};
};

On the next boot check the "dmesg" output to verify it loaded:

root@tsimx6:~# dmesg | grep ts16550
...
[    2.259413] ts16550 50004050.fpgaisa:ts16550@3e8: Adding 16550 UART ttyS0
[    2.268166] ts16550 50004050.fpgaisa:ts16550@2e8: Adding 16550 UART ttyS1
...

Now that these are loaded:

Device Description
/dev/ttyS0 COMA
/dev/ttyS1 COMB

In this mode COMA/COMB are full duplex RS-485 (RS-422). See the TS-ISO485 manual for mroe details on the HD jumpers to use half duplex.

TS-MULTI104
TS-MULTI-104
Product Page
8-bit IO

The TS-MULTI-104 supports serial based Multitech Socketmodems for cellular support.

This peripheral implements 16550A based UARTs which require a kernel driver. Under Linux this requires a device tree change. See the Kernel compile section for more details about getting set up the compile the kernel.

In general for 16550 UARTs we recommend these kernel config options:

CONFIG_HZ_PERIODIC=y
CONFIG_HZ_1000=y
CONFIG_SERIAL_8250_TS=m

Once booted the driver can be loaded with modprobe: ``` modprobe 8250_ts ```

The default kernel is ideal for low power, but has some tradeoffs with latency. A 16550 only has a 16 byte FIFO and needs to be serviced with relatively low latency, so a the above options make the latency far more deterministic. These changes are necessary to run at 115200 and not drop any data.

This below example will set up the serial devices for a TS-MULT-104 with the IRQ6, COM1, and 1.8MHz jumpers installed. Open the device tree at arch/arm/boot/dts/imx6ul-ts7250v3.dtsi. Add the highlighted section to the device tree and recompile.

pc104bus: fpgaisa@50 {
    ...
	#address-cells = <0x1>;
	#size-cells = <0x1>;

	/* TS-MULTI-104 */
	ts16550@3f8 {
		compatible = "technologic,ts16550";

		reg = <0x3f8 8>;
		interrupt-parent = <&fpga_intc>;
		interrupts = <15 IRQ_TYPE_LEVEL_HIGH>; // IRQ 6
	};
};

On the next boot check the "dmesg" output to verify it loaded:

root@tsimx6:~# dmesg | grep ts16550
[    2.259413] ts16550 50004050.fpgaisa:ts16550@3f8: Adding 16550 UART ttyS0

The device node /dev/ttyS0 can be used to access this UART. As a simple test: picocom -b 115200 /dev/ttyS0

Type in "ATI" and press enter. This should return the cell radio name, and "OK".

For cell modems requiring the 921600 baud rate, remove the 1.8MHz jumper and in the device tree add uartclk = <14745600>; after specifying the interrupts. This will allow Linux to request the baud rate 921600.

The TS-MULTI-104 manual has a second on setting up pppd with tmobile, but refer to your specific modem's documentation for more information on setting up a connection specific to your modem/carrier.

The power to this modem is also under user control. By default, the modem will be powered.

# Turn off Cell modem power
pc104_peekpoke io 8 0x140 0x1

# Turn modem back on:
pc104_peekpoke io 8 0x140 0x0
TS-RELAY8
TS-Relay8
Product Page
8-bit IO


Note: On TS-7250-V3 PCB Rev. B and older, the TS-RELAY8 relays will energize briefly immediately on poweron due to a hardware bug. This has been addressed in Rev. C and newer TS-7250-V3 hardware. When using older PCB revisions, ensure your application can tolerate several ms of the relays being energized at poweron.


The TS-RELAY8 includes 8 SPDT relays. These are capable of switching up to 5A at 30VDC or 30VAC.

See sections of the TS-RELAY8 guide for register / jumper documentation:

This example assumes no jumpers are installed on the TS-RELAY8.

# Verify the TS-RELAY8 is detected.  This should return 0x9b
pc104_peekpoke io 8 0x140

# Turn on just RLY1
pc104_peekpoke io 8 0x142 0x1

#Turn on RLY1 and RLY4
pc104_peekpoke io 8 0x142 0x9

# Turn on all relays.
pc104_peekpoke io 8 0x142 0xFF

# Turn off all relays (default state)
pc104_peekpoke io 8 0x142 0x0
TS-SER1
TS-SER1
Product Page
8-bit IO

The TS-SER1 provides a single RS-232 port.

This peripheral implements 16550A based UARTs which require a kernel driver. Under Linux this requires a device tree change. See the Kernel compile section for more details about getting set up the compile the kernel.

In general for 16550 UARTs we recommend these kernel config options:

CONFIG_HZ_PERIODIC=y
CONFIG_HZ_1000=y
CONFIG_SERIAL_8250_TS=m

Once booted the driver can be loaded with modprobe: ``` modprobe 8250_ts ```

The default kernel is ideal for low power, but has some tradeoffs with latency. A 16550 only has a 16 byte FIFO and needs to be serviced with relatively low latency, so a the above options make the latency far more deterministic. These changes are necessary to run at 115200 and not drop any data.

This below example will set up the serial devices for a TS-SER1 with the IRQ6 and COM3 jumpers installed. Open the device tree at arch/arm/boot/dts/imx6ul-ts7250v3.dtsi. Add the highlighted section to the device tree and recompile.

pc104bus: fpgaisa@50 {
    ...
	#address-cells = <0x1>;
	#size-cells = <0x1>;

	/* TS-SER1 */
	ts16550@3e8 {
		compatible = "technologic,ts16550";

		reg = <0x3e8 8>;
		interrupt-parent = <&fpga_intc>;
		interrupts = <15 IRQ_TYPE_LEVEL_HIGH>;
	};
};

On the next boot check the "dmesg" output to verify it loaded:

root@tsimx6:~# dmesg | grep ts16550
[    2.259413] ts16550 50004050.fpgaisa:ts16550@3e8: Adding 16550 UART ttyS0

The device node /dev/ttyS0 can be used to access this UART.

TS-SER2
TS-SER2
Product Page
8-bit IO

The TS-SER2 provides two RS-232, RS-485 half duplex or full duplex (RS-422) ports and a parallel port. Refer to the TS-SER2 manual for hardware documentation. The parallel port is currently not supported on this platform.

This peripheral implements 16550A based UARTs which require a kernel driver. Under Linux this requires a device tree change. See the Kernel compile section for more details about getting set up the compile the kernel.

In general for 16550 UARTs we recommend these kernel config options:

CONFIG_HZ_PERIODIC=y
CONFIG_HZ_1000=y
CONFIG_SERIAL_8250_TS=m

Once booted the driver can be loaded with modprobe: ``` modprobe 8250_ts ```

The default kernel is ideal for low power, but has some tradeoffs with latency. A 16550 only has a 16 byte FIFO and needs to be serviced with relatively low latency, so a the above options make the latency far more deterministic. These changes are necessary to run at 115200 and not drop any data.

This below example will set up the serial devices for a TS-SER2 with JP 14 through JP18, COMB 6, and COMA 5 jumpers installed. Open the device tree at arch/arm/boot/dts/imx6ul-ts7250v3.dtsi. Add the highlighted section to the device tree and recompile.

pc104bus: fpgaisa@50 {
	compatible = "technologic,pc104-bus";
	reg = <0x50 0x4>;

	ranges = <0 0 0x1000>;
	reset-gpio = <&gpio3 7 0>;

	#address-cells = <0x1>;
	#size-cells = <0x1>;

	/* TS-SER2 COMA */
	ts16550@2e8 {
		compatible = "technologic,ts16550";

		reg = <0x2e8 8>;
		interrupt-parent = <&fpga_intc>;
		interrupts = <14>;
	};

	/* TS-SER2 COMB */
	ts16550@3e8 {
		compatible = "technologic,ts16550";

		reg = <0x3e8 8>;
		interrupt-parent = <&fpga_intc>;
		interrupts = <15>;
	};
};

On the next boot check the "dmesg" output to verify it loaded:

root@tsimx6:~# dmesg | grep ts16550
[    2.259413] ts16550 50004050.fpgaisa:ts16550@2e8: Adding 16550 UART ttyS0
[    2.268166] ts16550 50004050.fpgaisa:ts16550@3e8: Adding 16550 UART ttyS1

Now that these are loaded:

Device Description
/dev/ttyS0 COMA
/dev/ttyS1 COMB

In this mode COMA/COMB are full duplex RS-485 (RS-422). See the TS-ISO485 manual for mroe details on the HD jumpers to use half duplex.

TS-SER4
TS-SER4
Product Page
8-bit IO

The TS-SER4 supports 4 UARTs as RS-232, RS-485, or RS-422. Refer to the TS-SER4 manual for hardware documentation.

This peripheral implements 16550A based UARTs which require a kernel driver. Under Linux this requires a device tree change. See the Kernel compile section for more details about getting set up the compile the kernel.

In general for 16550 UARTs we recommend these kernel config options:

CONFIG_HZ_PERIODIC=y
CONFIG_HZ_1000=y
CONFIG_SERIAL_8250_TS=m

Once booted the driver can be loaded with modprobe: ``` modprobe 8250_ts ```

The default kernel is ideal for low power, but has some tradeoffs with latency. A 16550 only has a 16 byte FIFO and needs to be serviced with relatively low latency, so a the above options make the latency far more deterministic. These changes are necessary to run at 115200 and not drop any data.

This below example will set up the serial devices for a TS-SER4 with the IRQ2, IRQ4 (selects IRQ6), and COM1 jumpers installed. Open the device tree at arch/arm/boot/dts/imx6ul-ts7250v3.dtsi. Add the highlighted section to the device tree and recompile.

pc104bus: fpgaisa@50 {
    ...
	#address-cells = <0x1>;
	#size-cells = <0x1>;

	/* COMA */
	ts16550@3f8 {
		compatible = "technologic,ts16550";

		reg = <0x3f8 8>;
		interrupt-parent = <&fpga_intc>;
		interrupts = <15 IRQ_TYPE_LEVEL_HIGH>;
	};

	/* COMB */
	ts16550@2f8 {
		compatible = "technologic,ts16550";

		reg = <0x2f8 8>;
		interrupt-parent = <&fpga_intc>;
		interrupts = <15 IRQ_TYPE_LEVEL_HIGH>;
	};

	/* COMC */
	ts16550@3e8 {
		compatible = "technologic,ts16550";

		reg = <0x3e8 8>;
		interrupt-parent = <&fpga_intc>;
		interrupts = <15 IRQ_TYPE_LEVEL_HIGH>;
	};

	/* COMD */
	ts16550@2e8 {
		compatible = "technologic,ts16550";

		reg = <0x2e8 8>;
		interrupt-parent = <&fpga_intc>;
		interrupts = <15 IRQ_TYPE_LEVEL_HIGH>;
	};
};

On the next boot check the "dmesg" output to verify it loaded:

root@tsimx6:~# dmesg | grep ts16550
[    2.259413] ts16550 50004050.fpgaisa:ts16550@3f8: Adding 16550 UART ttyS0
[    2.268166] ts16550 50004050.fpgaisa:ts16550@2f8: Adding 16550 UART ttyS1
[    2.278539] ts16550 50004050.fpgaisa:ts16550@3e8: Adding 16550 UART ttyS2
[    2.287455] ts16550 50004050.fpgaisa:ts16550@2e8: Adding 16550 UART ttyS3

Now that these are loaded:

Device Description
/dev/ttyS0 COMA
/dev/ttyS1 COMB
/dev/ttyS3 COMC
/dev/ttyS4 COMD
TS-12W
TS-12W
Released Mar. 2004
Product Page

The TS-12W provides 2.4 A on PC/104 5V pin and supports a VIN of 10-30 VDC. The TS-7250-V3 has a built in regulator supporting 8-48 VDC which is recommended instead, but for legacy products with existing mechanical requirements the TS-12W will continue to work. Powering the system from PC/104 does prevent the device's low power sleep mode from functioning.

Refer to the TS-12W manual for more information on hardware features.

TS-13W
TS-13W
Released June 2009
Product Page

The TS-13W provides 2.6 A on PC/104 5V pin and supports a VIN of 8-30 VDC. The TS-7250-V3 has a built in regulator supporting 8-48 VDC which is recommended instead, but for legacy products with existing mechanical requirements the TS-13W will continue to work. Powering the system from PC/104 does prevent the device's low power sleep mode from functioning.

Refer to the TS-13W manual for more information on hardware features.

TS-9422
TS-9422
Product Page
8-bit IO

The TS-9422 is a POST code output board designed for x86 systems. ARM does not use POST to indicate its boot status, but this board does work to display two hexadecimal values written to 0x80 on the ISA bus.

WARNING: Make sure the board mounting holes line up on PC104. Unlike most PC104 boards this faces away from the main CPU in order to remain visible while in a stack.
# Shows 55 on the PC104 bus
pc104_peekpoke io 8 0x80 0x55


TS-9700
TS-9700
Product Page
8-bit IO

The TS-9700 provides 8 channels of 12-bit ADC which support 0-2.5V, 0-10V, or 0-20mA. Optionally this board can include 4x 0-5V DAC channels.

Refer to the TS-9700 manual for register / hardware documentation.

This example assumes addr 0x160 selected by having JP1/2/3 removed.

The TS-9700 identifies as 0x97:

pc104_peekpoke io 8 0x161

The TS-9700 is accessed in userspace with this sample C code.

#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <unistd.h>

#include "pc104.h"
#define TS9700_BASE 0x160

void ts9700_set_dac(uint8_t channel, uint16_t val)
{
	assert(channel <= 3);
	assert(val <= 0xFFF);
	pc104_io_8_write(TS9700_BASE + 0x4, val & 0xff);
	pc104_io_8_write(TS9700_BASE + 0x5, (channel << 6) | (val >> 8));

	while((~pc104_io_8_read(TS9700_BASE + 0x6)) & (1 << 7)){
		usleep(10); /* Wait until the DAC is ready */
	}
}

uint16_t ts9700_single_sample(uint8_t channel)
{
	uint16_t sample;

	assert (channel < 8);
	pc104_io_8_write(TS9700_BASE, channel | (1 << 4));
	while((~pc104_io_8_read(TS9700_BASE)) & (1 << 7)){
		usleep(9); /* Wait until the sample is ready */
	}
	sample = pc104_io_8_read(TS9700_BASE + 0x2);
	sample |= ((uint16_t)pc104_io_8_read(TS9700_BASE + 0x3) << 8);
	return sample;
}

int main(int argc, char **argv)
{
	int i;

	pc104_init();

	/* Verify the TS-9700 is detected */
	if(pc104_io_8_read(TS9700_BASE + 0x1) != 0x97) {
		fprintf(stderr, "TS-9700 not detected");
		return 1;
	}

	for (i = 0; i < 8; i++)
		printf("chan%d=0x%X\n", i, ts9700_single_sample(i));

	ts9700_set_dac(0, 0); // Set channel 0 to 0V
	ts9700_set_dac(1, 4095); // Set to 5V (max)
	ts9700_set_dac(2, 2048); // Set to 2.5V
	ts9700_set_dac(3, 819); // Set to ~1V

	return 0;
}

Compile this on the unit with:

gcc ts9700.c tspc104.c -o ts9700

FRAM

This device supports an optional non-volatile Ferroelectric RAM (FRAM) device. The Fujitsu MB85RS16N is a 2 KiB device, in a configuration not unlike an SPI EEPROM. However, the nature of FRAM means it is non-volatile, incredibly fast to write, and is specified with 1 trillion read/write cycles per each byte and a 200 year data retention. The device is connected to Linux and presents itself as a flat file that can be read and written like any standard Linux file.

The EEPROM file can be found at /sys/bus/spi/devices/spi4.1/eeprom.

Note: FRAM is not present on the TS-7250-V3-SMN1I or TS-7250-V3-SMN2I configurations.

I2C

The i.MX6UL supports I2C at 100 kHz, or using fast mode for 400 kHz operation. This board uses two CPU I2C busses for onboard ICs.

Device Address Description
/dev/i2c-0 0x1e #Magnetometer
0x54 #Silabs
0x68 #RTC
0x6a #IMU
/dev/i2c-1 0x20 NXP pca9555 GPIO expander (Chip 2-0020)
0x64 ATSHA204 [1]
  1. Not populated by default

IMU

Linux provides access to the various IMU components through the IIO subsystem (via iio-tools and libiio).

Accelerometer (ST ISM330)

This platform features an ST accelerometer / gyroscope. The accelerometer has an acceleration range of ±2/±4/±8/±16 g.

Early units were built using the "ism330dlc", and newer units are built using the "ism330dhcx". These are functionally the same and provide the same channels and performance, but IIO requires you to specify the part number. Our example python/c code will show how to work with either.

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
# ISM330DLC
iio_attr -c ism330dlc_accel accel_x
iio_attr -c ism330dlc_accel accel_y
iio_attr -c ism330dlc_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') or ctx.find_device('ism330dlc_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 accelerometer / gyroscope. The gyroscope has a selectable angular range of ±125/±250/±500/±1000/±2000 dps

Early units were built using the "ism330dlc", and newer units are built using the "ism330dhcx". These are functionally the same and provide the same channels and performance, but IIO requires you to specify the part number. Our example python/c code will show how to work with either.

The gyroscope is accessed through IIO with channels:

  • anglvel_x
  • anglvel_y
  • anglvel_z
  • timestamp

For example:

# ISM330DHCX
iio_attr -c ism330dhcx_gyro anglvel_x
iio_attr -c ism330dhcx_gyro anglvel_y
iio_attr -c ism330dhcx_gyro anglvel_z
# ISM330DLC
iio_attr -c ism330dlc_gyro anglvel_x
iio_attr -c ism330dlc_gyro anglvel_y
iio_attr -c ism330dlc_gyro anglvel_z
root@tsimx6ul:~# iio_attr -c ism330dhcx_gyro anglvel_x
dev 'ism330dhcx_gyro', channel 'anglvel_x' (input), attr 'raw', value '2359'
dev 'ism330dhcx_gyro', channel 'anglvel_x' (input), attr 'scale', value '0.000153'
dev 'ism330dhcx_gyro', channel 'anglvel_x' (input), attr 'scale_available', value '0.000153 0.000305 0.000611 0.001222'
root@tsimx6ul:~# iio_attr -c ism330dhcx_gyro anglvel_y
dev 'ism330dhcx_gyro', channel 'anglvel_y' (input), attr 'raw', value '-1667'
dev 'ism330dhcx_gyro', channel 'anglvel_y' (input), attr 'scale', value '0.000153'
dev 'ism330dhcx_gyro', channel 'anglvel_y' (input), attr 'scale_available', value '0.000153 0.000305 0.000611 0.001222'
root@tsimx6ul:~# iio_attr -c ism330dhcx_gyro anglvel_z
dev 'ism330dhcx_gyro', channel 'anglvel_z' (input), attr 'raw', value '2761'
dev 'ism330dhcx_gyro', channel 'anglvel_z' (input), attr 'scale', value '0.000153'
dev 'ism330dhcx_gyro', channel 'anglvel_z' (input), attr 'scale_available', value '0.000153 0.000305 0.000611 0.001222'

This shows a snapshot of the x, y, z values. To get the real world value, multiply the scale * the raw value. In this case:

  • X: 0.360927 dps
  • Y: -0.255051 dps
  • Z: 0.422433 dps

The default scale is ±250, but ±250/±500/±1000/±2000 can be selected by setting the scale:

dev 'ism330dhcx_gyro', channel 'anglvel_z' (input), attr 'scale', value '0.000153'
dev 'ism330dhcx_gyro', channel 'anglvel_z' (input), attr 'scale_available', value '0.000153 0.000305 0.000611 0.001222'

To set ±1000, you would write the third available scale:

iio_attr -c ism330dhcx_gyro anglvel_z scale 0.000611

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_gyro -T 0 -s 128 > samples.bin

The format of this file is specified with iio_attr:

root@tsimx6ul:~# iio_attr -c ism330dhcx_gyro
dev 'ism330dlc_gyro', channel 'anglvel_x' (input, index: 0, format: le:S16/16>>0), found 3 channel-specific attributes
dev 'ism330dlc_gyro', channel 'anglvel_y' (input, index: 1, format: le:S16/16>>0), found 3 channel-specific attributes
dev 'ism330dlc_gyro', channel 'anglvel_z' (input, index: 2, format: le:S16/16>>0), found 3 channel-specific attributes
dev 'ism330dlc_gyro', 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 anglvel_x, little endian, signed
15:0 anglvel_y, little endian, signed
15:0 anglvel_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 into their raw values:

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 40
X:-58 Y:-199 Z:24 TS:419695978925948679
X:-67 Y:-196 Z:29 TS:419701023781322503
X:-64 Y:-197 Z:28 TS:419705968690298631
X:-58 Y:-203 Z:29 TS:419711008204553991

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_gyro') or ctx.find_device('ism330dlc_gyro')

with open(f'/sys/bus/iio/devices/{dev.id}/sampling_frequency', 'w') as f:
	f.write(f"833.000")

for chan_name in ["anglvel_x", "anglvel_y", "anglvel_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'  anglvel_x={x}, anglvel_y={y}, anglvel_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("anglvel_x=%d, anglvel_y=%d, anglvel_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] = { "anglvel_x", "anglvel_y", "anglvel_z" };

    // Create context and find device
    ctx = iio_create_local_context();
    if (!ctx || !(dev = iio_context_find_device(ctx, "ism330dhcx_gyro")) &&
        !(dev = iio_context_find_device(ctx, "ism330dlc_gyro"))) {
        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;
}

Interrupts

Note: This section is incomplete at this time.

LEDs

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

The following LEDs are available on this system:

  • red-led
  • green-led

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.

Magnetometer (ST IIS2MDCTR)

This platform 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 lis2mdl with channels:

  • magn_x
  • magn_y
  • magn_z
  • timestamp

For example:

root@tsimx6ul:~# iio_attr -c lis2mdl -c magn_x
dev 'lis2mdl', channel 'magn_x' (input), attr 'raw', value '630'
dev 'lis2mdl channel 'magn_x' (input), attr 'scale', value '0.001500'
root@tsimx6ul:~# iio_attr -c lis2mdl -c magn_y
dev 'lis2mdl channel 'magn_y' (input), attr 'raw', value '-165'
dev 'lis2mdl channel 'magn_y' (input), attr 'scale', value '0.001500'
root@tsimx6ul:~# iio_attr -c lis2mdl -c magn_z
dev 'lis2mdl channel 'magn_z' (input), attr 'raw', value '9'
dev 'lis2mdl channel 'magn_z' (input), attr 'scale', value '0.001500'

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

MicroSD Interface

The i.MX6ul SDHCI driver supports MicroSD (0-2GB), MicroSDHC (4-32GB), and MicroSDXC(64GB-2TB). The linux driver provides access to this socket at /dev/mmcblk1 as a standard Linux block device.

We have performed compatibility testing on the Sandisk MicroSD cards we provide. We do not suggest switching brands/models without your own qualification testing. While SD cards specifications are standardized, in practice cards behave very differently. We do not recommend ATP or Transcend MicroSD cards due to known compatibility issues.

MicroSD cards should not have power removed during a write or they will have disk corruption. Keep the filesystem mounted read only if this is a possibility. It is not always possible for fsck to recover from the types of failures that will be seen with SD power loss. Consider using the eMMC for storage instead which is far more resilient to power loss.

Supervisory Microcontroller

Note: The supervisory features are only supported in kernel 5.10 and later, and on PCB revision C (early 2023) and later.

The TS-7250-V3 includes a preprogrammed microcontroller intended to manage the SBC's power state, RTC, and a few additional ADCs to monitor system rails. This microcontroller also provides the USB console functionality. The way console is routed can be changed from /sys/:

root@tsimx6ul:~# cat /sys/bus/i2c/devices/0-0010/console_cfg 
[auto] always-usb
Setting Description
auto Console routes to DB9 by default, but if a USB cable is connected to the microcontroller it routes here instead.
always-usb Console does not route to db9, and is only available on USB.

This can be written to /sys/, and will persist between reboots:

echo "always-usb" > /sys/bus/i2c/devices/0-0010/console_cfg

Anytime console is routed to USB instead, ttyS8 is routed to the DB9 port instead for application use.

Supervisory Microcontroller Low Power Mode

To achieve the lowest power sleep state, the kernel is configured with the reset controller driver (ts_supervisor_rstc). This driver implements shutdown for the SBC to enter the low power state. For example, shutdown -h now will cause a proper shutdown to the board, and then remove power to the rest of the board. It will wait for one of 3 reasons to wake back up.

In this low power state the board draws approximately 25mW. When one of the wake up reasons occurs the SBC starts back up like a normal boot. The wakeup source can be detected from the reset reasons.

Supervisory Microcontroller Reset Reasons

The supervisory microcontroller can detect multiple reasons why the system rebooted or woke back up. These are provided by the sysfs device:

root@tsimx6ul:~# cat /sys/bus/platform/devices/tssupervisor-reset/reboot_reason
POR
reboot_reason text Description
POR Initial boot from power/console being powered.
Brownout 5V dropped below 4.7V, but microcontroller stayed power (from 5v/vin or USB).
CPU WDT CPU Watchdog initiated a reboot.
Software Reboot The linux kernel initiated a reboot.
RTC Alarm Reboot RTC Alarm initiated reboot.
Wake from PWR Cycle System initiated a shutdown with console connected, and power was cycled.
Wake from WAKE_EN System initiated a shutdown, powered back on when CN8 pin 9 was brought up to 3-30 V.
Wake from USB VBUS System initiated a shutdown, USB console plugged in.
Wake from RTC Alarm System initiated a shutdown, and the RTC alarm tripped.

Supervisory Microcontroller RTC

The supervisory microcontroller also provides a real time clock to backup system time when power is lost. This RTC features:

  • Unsigned 32-bit counter
  • Alarm to wake the board up from power off
  • Alarm can be configured as a slow watchdog
  • Battery detection
  • Offset support to tune rtc in ppb
  • Can retain time for ~8 years with the CR1632 battery

The typical RTC features are provided by the rtc-tssupervisor driver present in our kernels. This includes setting/getting the latest epoch time. On most distributions this requires no user interaction and systemd will sync the hardware clock when it syncs to a network time protocol (NTP) server.

To manually interact with the RTC the hwclock command is typically used:

# Set the RTC from the system time
hwclock --systohc

# Set the system time from the RTC
hwclock --hctosys

# Just print the RTC time, do not set either clock
hwclock --show

Our RTC also has several sysfs entries to support the nonstandard features:

### VBATT:
## Returns 0 or 1 to indicate if VBATT is > 1.8-2.0V
## Does not indicate a sufficient voltage to keep time, but can be used to detect
## no battery or a malfunctioning battery
## This is only checked on power on
cat /sys/class/rtc/rtc0/device/batt_present 

### RTC Alarm Wake up:
## The RTC can be used to wake the system after shutting down.
# The hwclock call can be skipped if system time is already set:
hwclock --systohc

# Takes the current time and adds 60 seconds
echo $(($(date +%s)+60)) > /sys/class/rtc/rtc0/device/alarm
echo 1 > /sys/class/rtc/rtc0/device/alarm_en
shutdown -h now
# The system will go into the low power state, and power back up when the alarm expires.

### RTC Alarm Watchdog:
## If its not being used for waking the system, the RTC alarm can be used to
## Reset the system if the time is not updated. This can be used as a very slow watchdog, setting
## a reset time of minutes/hours/days ahead instead of a typical 128 seconds a typical
## watchdog allows as the largest timer, but it is slower to feed so it should not be fed quickly.
## Keep in mind this reset is immediate and can corrupt any filesystems mounted read/write when the watchdog
## trips.
# The hwclock call can be skipped if system time is already set:
hwclock --systohc

# Takes the current time and adds 10 seconds
echo $(($(date +%s)+10)) > /sys/class/rtc/rtc0/device/alarm
echo 1 > /sys/class/rtc/rtc0/device/alarm_cause_reboot
echo 1 > /sys/class/rtc/rtc0/device/alarm_en

A typical RTC crystal is approximately ±20 ppm accurate which results in drift while the system is offline of approximately ±631 seconds per year. Our circuit, operating temperature, crystal age (~±3 ppm per year), all influence the accuracy of the crystal. An NTP client like chrony can be used to determine the PPM offset at a board at a given temperature, and the RTC can automatically apply this offset correction. This relies on having access to an accurate NTP server to calibrate the RTC.

Eg, on Debian:

apt-get update && apt-get install chrony -y

In case there was a previous offset applied which may affect the calibration:

echo 0 > /sys/class/rtc/rtc0/offset

Open /etc/chrony/chrony.conf, and add the line:

rtcfile /var/lib/chrony/rtcfile

Make sure there is no 'rtcsync' in the file since it cannot sync the hardware clock during this test, and that cannot be set with rtcfile.

Then restart chrony, and force a sync:

service chrony restart
chronyc makestep

At this step we must wait for the system clock to get in sync with the upstream NTP server. From our testing with a good network connection this can take 15-30 minutes until the RTC offset settles. From here we can query chrony for the RTC offset.

root@tsimx6:~# chronyc rtcdata
RTC ref time (UTC) : Fri Nov 18 13:38:36 2022
Number of samples  : 64
Number of runs     : 40
Sample span period : 254m
RTC is fast by     :    -0.198639 seconds
RTC gains time at  :   -21.674 ppm

In this example, the RTC is offset is -21.674 ppm. Linux expects this value to be in parts per billion, and it should be indicated what value will correct the crystal. Multiply the ppm by 1000 * -1 to get a value we can write to the offsets file. For example:

# Parse rtcdata and get the PPM value
# Eg, with the above example this is:
# PPM=-21.674
PPM=$(chronyc rtcdata | grep ppm | awk '{print $6}')
# Convert to parts per billion and invert the value to apply a correction.
# Eg, with the above example this is:
# PPB_CORRECTION=21674
PPB_CORRECTION=$(echo "scale=0; ${PPM}*-1000/1" | bc)

echo $PPB_CORRECTION > /sys/class/rtc/rtc0/offset

Once this is calibrated at a given temperature the ppm offset range can be calculated with:

0.035 * (CalibrationTemp - OperatingTemp)^2

For example, if the RTC is calibrated to the offset at 22C:

0.035*(22-85)^2=138.915

, or roughly ±139 ppm at 85 C.

To calculate the seconds drift per year with this PPM offset:

(60*60*24*365)*(138.915*10^-6)=4380.8

Supervisory Microcontroller ADC

The supervisory microcontroller provides ADC channels to monitor onboard rails such as VIN, 5 V, 3.3 V, and more. Channels that are less than 3.3 V are provided directly by the IIO device "tssupervisor_adc". The analog frontend driver is used to rescale some channels to the range supported by this ADC.

iio device iio channel Schematic name
an_3p3v voltage0 3.3V
tssupervisor-adc voltage1 VDD_ARM_CAP
tssupervisor-adc voltage2 VDD_SOC_CAP
an_5v voltage0 5V_A
an_8v_48v voltage0 8V_48V

These can be accessed from any language with iio bindings, from /sys/bus/iio/, or using iio_attr. For example, to reading the input voltage with iio_attr:

root@tsimx6ul:~# iio_attr -c an_8v_48v voltage0
dev 'an_8v_48v', channel 'voltage0' (input), attr 'raw', value '619'
dev 'an_8v_48v', channel 'voltage0' (input), attr 'scale', value '18.650634765'

The raw and scale values can be used to get the real value in millivolts:

619*18.650634765 = 11544.742919535

or 11.54 V.

Supervisory Microcontroller Temp Sensor

The supervisory microcontroller provides a temperature sensor to read its die temperature as millicelcius.

iio device iio channel
tssupervisor-temp temp

These can be accessed from any language with iio bindings, from /sys/bus/iio/, or using iio_attr. For example, to reading the temperature with iio_attr:

root@tsimx6ul:~# iio_attr -c tssupervisor-temp temp
dev 'tssupervisor-temp', channel 'temp' (input), attr 'input', value '32915'

This value would be 32.915C.

SPI

The TS-7250-V3 FPGA includes the 2 opencore SPI controllers. Under Linux these are spi4 and spi5.

Controller Chip select Device
spi4 0 /dev/spidev4.0 DIO Header SPI bus
1 #FRAM
2 FPGA Flash (/dev/mtdblock0) [1]
spi5 0 /dev/spidev5.0 #Mikrobus SPI
  1. Unless instructed by our support, it is not recommended to manipulate this flash. Erasing this data will require an RMA to recover.

The /dev/spidev* devices can be accessed from Linux. See the kernel spidev documentation for more information on interfacing with the SPI peripherals from C.

Other languages also have bindings to interface with spidev:

UARTs

The TS-7250-V3 includes UARTs on the CPU, as well as 16550A compatible registers on the FPGA interface.

UART Dev. Type TX / + Loc. RX / - Loc. 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.3V 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 [1] TTL DIO pin 5 DIO pin 7 N/A N/A N/A N/A DIO pin 13
ttyS15 [1] TTL DIO pin 9 DIO pin 11 N/A N/A N/A N/A DIO pin 15
  1. 1.0 1.1 Not enabled until setting #FPGA Syscon 0x08 bit 9 to 1

The DIO header uarts, ttyS14 and ttyS15 are not enabled until a mux register is set.

# Change DIO header to use UARTs instead of GPIO
tshwctl --address 0x08 --poke32 0x6000

RS-485

RS-485 is implemented via a UART interface inside of the FPGA. This device handles automatic TXEN assertion and de-assertion for half-duplex RS-485 communication without any required settings or API calls. See the UARTs section for the location of the RS-485 port.

RS-422

While both ttyS11 and ttyS12 support RS-485 half duplex these uarts can also be used as a single full duplex RS-422. Either of these UARTs are electrically compatible with RS-485/RS-422 and support TX or RX. To implement RS-422 in software open either UART and use it for transmit, and open the other UART and only use it for receive.

USB

The TS-7250-V3 offers two USB 2.0 host ports. Power to the host ports can be controlled with the LED subsystem under the LED device "/sys/class/leds/en-usb-5v/". By writing a value greater than 0 to the "brightness" file in that folder, it will enable USB power. While setting it to 0 will turn it off. See the DIO section of the manual for more information on this. The USB A host port stack can provide 1 A total power output shared between the two ports.

Watchdog

The TS-7250-V3 implements a WDT inside the CPU. Our stock distribution uses the 'watchdog' utility to check system health, set feed length, and perform feeds. Setting a timeout of 0 and issuing a feed will disable the WDT in hardware.

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. This is the default behavior of our stock kernel.

Additionally, if the kernel is compiled with CONFIG_WATCHDOG_NOWAYOUT then the WDT can never be stopped once it is started at boot. This is not enabled by default in our stock kernel

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

Specifications

IO specifications

IO Type Voltage max Absolute max Source Current Sink Current VIL VIH
CPU 3.3V 3.3V 3.6V 2.31 0.99
CPU 3.3V+QS3861 5V 7V
FPGA 3.3-V LVTTL 3.3V 3.6V 6mA 6mA
FPGA 3.3-V LVTTL+QS3861 5V 7V 6mA 6mA
PCA9555 5.3V 6V 25mA [1] 8-24mA 0.3V 2.31V
  1. "Each I/O must be externally limited to a maximum of 25 mA and each octal (IO0_0 to IO0_7 and IO1_0 to IO1_7) must be limited to a maximum current of 100 mA for a device total of 200 mA. The total current sourced by all I/Os must be limited to 160 mA

Power Consumption

The TS-7250-V3's i.MX6UL CPU is very flexible with power. It can change the running frequency as needed to consume less power or to allow for more processing power.

The Ethernet can be put into a lower power state by bringing them up, and back down on startup. This is not done by default, and helps power savings regardless of if Ethernet is connected.

ifconfig eth0 up
ifconfig eth1 up
ifconfig eth0 down
ifconfig eth1 down
ifconfig wlan0 up # only needed if WIFI is present

These tests were run with 5V input. Unless otherwise specified these tests are run with no external connections except power, booted over eMMC at an idle prompt. The above ifconfig commands are included in our tests.

TS-7250-V3
Test Avg. (W) Peak (W)
Idle 0.77 1.29
CPU fully loaded [1] 1.03 1.45
CPU idle, single Ethernet port up and active [2] 1.17 1.75
CPU fully loaded [1], single Ethernet port up and active [2] 1.40 2.03
Supervisory Microcontroller sleep mode (ARM CPU off) 0.055 0.063
  1. 1.0 1.1 This is accomplished by running stress-ng --matrix 0 -t 60m which generally consumes 100% CPU time
  2. 2.0 2.1 Using iperf to create bidirectional activity which adds minor CPU load

Power Input Specifications

The TS-7250-V3 supports 2 input ranges. The +5V input on CN2, and the 8-28V input on CN12. The 8-28V input can also come from the PC104 connector's +12V signal.

Input Min range Max Range
5 VDC 4.7 VDC [1] 5.2 VDC
8-28 VDC 8 VDC 28 VDC
  1. This requires requires at least 4.7V to start up, and the onboard supervisory microcontroller will trigger a brownout reset if this dips below 4V. While the TS-7250-V3 will continue to operate at this low of a voltage, any connected devices using the 5V rail directly such as USB may not function as intended this low.

External Interfaces

ADC Header

The ADC header supports 5 channels of 0-30VDC ADC. Of these 5, 3 channels support sampling 0-20mA current loops. These channels are sampled from:

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

See the ADC section for more details on sampling these pins.

Signals Pin Layout
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 #RTC uses removable lithium cr1632 batteries.

COM2 Header

The COM2 header is a 0.1" pitch 2x5 header supporting RS-485, RS-422 and RS-232.

Signals Pin Layout
Pin Signal
1 ttyS11 RS485+
2 ttyS9 RS-232 RXD
3 ttyS9 RS-232 TXD
4 ttyS12 RS485+
5 GND
6 ttyS11 RS485-
7 ttyS9 RS-232 RTS
8 ttyS9 RS-232 CTS
9 ttyS12 RS485-
10 NC

COM3 Header

The COM3 header is a 0.1" pitch 2x5 header supporting CAN and RS-232.

Signals Pin Layout
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 (DE-9) connector provides an RS-232 port with full handshakes.

Signals Pin Layout
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" pitch 2x8 header including SPI and GPIO. All pins on this header are 5V tolerant except SPI output pins. The SPI input pins are 5V tolerant and can be connected to a 5V SPI device. All of these DIO include pullups.

Signals Pin Layout
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[1] 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 spidev 4.0 Chip Select / 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 spidev 4.0 MISO / GPIO Chip 50004010.fpga_gpio IO 10 [2]
11 FPGA 3.3-V LVTTL+QS3861 GPIO Chip 50004010.fpga_gpio IO 7 / ttyS15 RX
12 FPGA 3.3-V LVTTL spidev 4.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 spidev 4.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.3V

  1. High drives ground, low is tristate.
  2. This pin is input only even when in the GPIO mode

To use the SPI pins on this header as GPIO instead, disable SPI by changing the FPGA Syscon 0x08 bit 10:

​tshwctl -a 0x8 --poke32 0x400

The DIO header is designed to provide compatibility with the KPAD accessory. This is a 4x4 numerical keypad. This is supported in userspace with the keypad.c source code, or the "keypad" utility which is included in the shiping image.

This debounces presses to 50ms, and does not repeat when numbers are held. This will output a string containing the key that is pressed. Eg:

root@tsimx6:~# keypad
1
UP
DOWN
2ND
ENTER

Ethernet connectors

The TS-7250-V3 supports two independent 10/100 Ethernet ports. See the Configuring the Network section of the manual for more information on configuration.

LCD Header

The LCD header is a 0.1" pitch 2x7 header including GPIO. This is designed around compatibility with the HD44780 LCD controller which includes our LCD-LED. The LCD Data pins (7-14) are 5V tolerant. These will output up to 3.3V, and the remaining control IO and PWM are 3.3V tolerant. The TS-7250-V3 Debian images include a command lcdmesg. This can be used to write to our LCD-LED display.

For example, this would write to the display:

lcdmesg "line 1" "line 2"
# Messages can also be piped to lcdmesg:
echo -e "line 1\nline 2\n" | lcdmesg

For example, running:

lcdmesg Technologic Systems

will display:

Pin 4, the LCD_BIAS pin, is used to set the contrast on the LCD.

tshwctl --address 0x1c --poke16 0x0 # Writes minimum
tshwctl --address 0x1c --poke16 0xf # Writes maximum
Signals Pin Layout
Pin IO Type Signal
1 5V
2 GND
3 CPU 3.3V LCD_RS GPIO Chip 20a4000.gpio IO 21
4 CPU 3.3V LCD_BIAS [1]
5 CPU 3.3V LCD_EN GPIO Chip 50004010.fpga_gpio IO 20
6 CPU 3.3V LCD_WR GPIO Chip 50004010.fpga_gpio IO 19
7 CPU 3.3V+QS3861 LCD D1 GPIO Chip 20a4000.gpio IO 9
8 CPU 3.3V+QS3861 LCD D0 GPIO Chip 50004010.fpga_gpio IO 10
9 CPU 3.3V+QS3861 LCD D3 GPIO Chip 50004010.fpga_gpio IO 11
10 CPU 3.3V+QS3861 LCD D2 GPIO Chip 50004010.fpga_gpio IO 12
11 CPU 3.3V+QS3861 LCD D5 GPIO Chip 50004010.fpga_gpio IO 15
12 CPU 3.3V+QS3861 LCD D4 GPIO Chip 50004010.fpga_gpio IO 16
13 CPU 3.3V+QS3861 LCD_D7 GPIO Chip 50004010.fpga_gpio IO 17
14 CPU 3.3V+QS3861 LCD_D6 GPIO Chip 50004010.fpga_gpio IO 18

  1. PWM duty cycle controlled by FPGA Syscon reg 0x1c. This may need to be tuned depending on the environment or altitude where the display is used.

mikroBUS Header

The Mikrobus header is a 0.1" pitch 2x8 header which supports the Mikroe Click board ecosystem. This header features 3.3 V, 5 V, SPI, GPIO, ADC, PWM, a UART, and PWM. All I/O on this header are FPGA 3.3-V LVTTL.

The Click boards™ standard (where Click boards™ are a modular prototyping add-on board) is openly documented, allowing for custom boards to be designed.

By default all of these headers default to their non-gpio functions. These can be changed in the FPGA syscon register 0x08. For example:

# Make all mikrobus header pins GPIO:
peekpoke 32 0x50004008 0xF0

# Set only SPI to GPIO:
peekpoke 32 0x50004008 0x10
Signals Pin Layout
Pin Name Description
1 AN #FPGA_ADC / GPIO Chip 50004054.fpga_gpio IO 1 [1]
2 RST GPIO Chip 50004054.fpga_gpio IO 0 [2]
3 CS spidev 5.0 CS# / GPIO Chip 50004054.fpga_gpio IO 5
4 SCK spidev 5.0 CLK / GPIO Chip 50004054.fpga_gpio IO 6
5 MISO spidev 5.0 MISO / GPIO Chip 50004054.fpga_gpio IO 7
6 MOSI spidev 5.0 MOSI / GPIO Chip 50004054.fpga_gpio IO 8
7 +3.3V 3.3V
8 GND GND
9 GND GND
10 +5V 5V
11 SDA /dev/i2c-4 DAT / GPIO Chip 50004054.fpga_gpio IO 11
12 SCL /dev/i2c-4 CLK / GPIO Chip 50004054.fpga_gpio IO 12
13 TX ttyS13 TXD / GPIO Chip 50004054.fpga_gpio IO 9
14 RX ttyS13 RXD / GPIO Chip 50004054.fpga_gpio IO 10
15 INT FPGA IRQ 18 / GPIO Chip 50004054.fpga_gpio IO 2
16 PWM MIKRO_PWM / GPIO Chip 50004054.fpga_gpio IO 4

  1. This signal does not require a mux to use as a GPIO or ADC. To use the ADC signal the GPIO should be an input which is the reset default.
  2. This signal is pulled high, but your specific click card may require a specific reset duration.

MicroSD Connector

The MicroSD socket is located near the DB9 on top of the board. See the #MicroSD Interface section for more details on the CPU controller.

MicroUSB Connector

The TS-7250-V3 features an onboard supervisory microcontroller that converts the onboard 3.3V TTL console UART (ttymxc0) into a CP2103 USB serial device.

PC104 Header

The PC/104 connector consists of four rows of pins labelled A-D. This header implements the #PC104 Bus, and optionally most pins can be GPIO.

Refer to the IO specifications for details on the IO voltages of these pins. Not all pins on the PC/104 bus are designed to be 5V tolerant, but will be in places where it is needed for compatibility with the bus.

Pins IO Specification
D3-D15 [1] PCA9555
A1 CPU 3.3V
A10, A11, A12-A31, B6, B8, B11-B20, B25-B28, B30, D1-D2 FPGA 3.3-V LVTTL
A2-A9, B4, B21-B23, C11-C18 FPGA 3.3-V LVTTL+QS3861
B2 Open drain with pull to 5V
  1. These are only present on the models with the optional I2C port expander

Pin Description Pin Description Pin Description Pin Description
B32 GND A32 GND
B31 GND A31 ISA_ADD_00/Chip 50004064.fpga_gpio IO 0
B30 ISA_14_3_MHZ [1] A30 ISA_ADD_01/Chip 50004064.fpga_gpio IO 1
B29 +5V [2] A29 ISA_ADD_02/Chip 50004064.fpga_gpio IO 2
B28 Chip 50004040.fpga_gpio IO 1/TS mode DAT15 A28 ISA_ADD_03/Chip 50004064.fpga_gpio IO 3 C19 GND D19 GND
B27 Chip 50004040.fpga_gpio IO 2/TS mode DAT14 A27 ISA_ADD_04/Chip 50004064.fpga_gpio IO 4 C18 ISA_DAT_15/Chip 5000405c.fpga_gpio IO 15 D18 GND
B26 Chip 50004040.fpga_gpio IO 10/TS mode DAT13 A26 ISA_ADD_05/Chip 50004064.fpga_gpio IO 5 C17 ISA_DAT_14/Chip 5000405c.fpga_gpio IO 14 D17 Unused
B25 FPGA IRQ 13/TS mode DAT11 A25 ISA_ADD_06/Chip 50004064.fpga_gpio IO 6 C16 ISA_DAT_13/Chip 5000405c.fpga_gpio IO 13 D16 +5V [2]
B24 GND A24 ISA_ADD_07/Chip 50004064.fpga_gpio IO 7 C15 ISA_DAT_12/Chip 5000405c.fpga_gpio IO 12 D15 Chip 50004054.fpga_gpio IO 12
B23 FPGA IRQ 14 A23 ISA_ADD_08/Chip 50004064.fpga_gpio IO 8 C14 ISA_DAT_11/Chip 5000405c.fpga_gpio IO 11 D14 Chip 50004054.fpga_gpio IO 11
B22 FPGA IRQ 15 A22 ISA_ADD_09/Chip 50004064.fpga_gpio IO 9 C13 ISA_DAT_10/Chip 5000405c.fpga_gpio IO 10 D13 Chip 50004054.fpga_gpio IO 10
B21 FPGA IRQ 16 A21 ISA_ADD_10/Chip 50004064.fpga_gpio IO 10 C12 ISA_DAT_09/Chip 5000405c.fpga_gpio IO 9 D12 Chip 50004054.fpga_gpio IO 9
B20 TS mode DAT12 A20 ISA_ADD_11/Chip 50004064.fpga_gpio IO 11 C11 ISA_DAT_08/Chip 5000405c.fpga_gpio IO 8 D11 Chip 50004054.fpga_gpio IO 8
B19 Chip 50004040.fpga_gpio IO 6 A19 ISA_ADD_12/Chip 50004064.fpga_gpio IO 12 C10 Unused D10 Chip 50004054.fpga_gpio IO 7
B18 Chip 50004040.fpga_gpio IO 7/TS mode DAT10 A18 ISA_ADD_13/Chip 50004064.fpga_gpio IO 13 C09 Unused D09 Chip 50004054.fpga_gpio IO 6
B17 Chip 50004040.fpga_gpio IO 8/TS mode DAT9 A17 ISA_ADD_14/Chip 50004064.fpga_gpio IO 14 C08 Unused D08 Chip 50004054.fpga_gpio IO 5
B16 Chip 50004040.fpga_gpio IO 12 A16 ISA_ADD_15/Chip 50004064.fpga_gpio IO 15 C07 Unused D07 Chip 50004054.fpga_gpio IO 4
B15 Chip 50004040.fpga_gpio IO 13 A15 ISA_ADD_16/Chip 5000406c.fpga_gpio IO 0 C06 Unused D06 Chip 50004054.fpga_gpio IO 3
B14 ISA_IOR/Chip 5000406c.fpga_gpio IO 4 A14 ISA_ADD_17/Chip 5000406c.fpga_gpio IO 1 C05 Unused D05 Chip 50004054.fpga_gpio IO 2
B13 ISA_IOW/Chip 5000406c.fpga_gpio IO 5 A13 ISA_ADD_18/Chip 5000406c.fpga_gpio IO 2 C04 Unused D04 Chip 50004054.fpga_gpio IO 1
B12 ISA_MEMR/Chip 5000406c.fpga_gpio IO 6 A12 ISA_ADD_19/Chip 5000406c.fpga_gpio IO 3 C03 Unused D03 Chip 50004054.fpga_gpio IO 0
B11 ISA_MEMW/Chip 5000406c.fpga_gpio IO 7 A11 ISA_AEN/Chip 50004040.fpga_gpio IO 0 C02 Unused D02 Chip 5000406c.fpga_gpio IO 9
B10 GND A10 Chip 50004040.fpga_gpio IO 5 C01 Unused D01 Chip 5000406c.fpga_gpio IO 8
B09 8V_48V [3] A09 ISA_DAT_00/Chip 5000405c.fpga_gpio IO 0 C00 GND D00 GND
B08 Chip 50004040.fpga_gpio IO 3 A08 ISA_DAT_01/Chip 5000405c.fpga_gpio IO 1
B07 Unused A07 ISA_DAT_03/Chip 5000405c.fpga_gpio IO 3
B06 Chip 50004040.fpga_gpio IO 9 A06 ISA_DAT_04/Chip 5000405c.fpga_gpio IO 4
B05 N/A A05 ISA_DAT_05/Chip 5000405c.fpga_gpio IO 5
B04 FPGA IRQ 17/TS mode DAT8 A04 ISA_DAT_02/Chip 5000405c.fpga_gpio IO 2
B03 +5V [2] A03 ISA_DAT_06/Chip 5000405c.fpga_gpio IO 6
B02 Chip 20a4000.gpio IO 7 [4] A02 ISA_DAT_07/Chip 5000405c.fpga_gpio IO 7
B01 GND A01 Chip 20a4000.gpio IO 8
  1. Outputs a continuous 14.318180 MHz clock
  2. 2.0 2.1 2.2 Powering the system from PC104 5V prevents the Board's low power sleep mode from functioning.
  3. This pin can be used to supply power to the board through the switching regulator.
  4. This is automatically pulsed on startup by the ts-pc104 driver as ISA_RESET

Power Connectors

Note: While the photos below, and shipped PCBs, may show a silkscreen of "8-28V" on the high-voltage input block, all PCB revisions from Rev. A forward support 8-48 VDC input as noted below.


The TS-7250-V3 provides two power inputs on 2 pin removable terminal blocks. One terminal block supports 5 VDC, and one supports 8-48 VDC. Only one power input may be connected at a time. A typical power supply for this platform should provide 10 W. Refer to the specifications section for more information on power requirements.

Under the removable terminal block the PCB is labelled with the power supply polarity.

USB Ports

The TS-7250-V3 has 2 USB type A host ports. The bottom USB host port can optionally be routed to the #XBEE Header for USB cell modems.

# Route USB to XBEE
gpioset 209c000.gpio 11=1

# Route USB to bottom of J2 (default)
gpioset 209c000.gpio 11=0

Power can also be controlled to save power or reboot peripherals in the field.

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 2mm pitch 2x10 header which supports XBEE form factor modules. These include Nimbelink and Digi cell modems, Zigbee, Digi mesh, and other third party radios.

For Cell radios that use USB this must be enabled. This turns off USB to the bottom port on the dual high type A connector. Only enable if this is compatible with your module:

# Turn on the USB
gpioset 209c000.gpio 11=1

This header can provide 3.3V or 4V as some cell radios require higher voltage. Only enable one power supply to match your radio:

## For 3.3V modules:
#gpioset 50004040.fpga_gpio 4=1

## For 4V modules:
#gpioset 50004040.fpga_gpio 11=1

# Reset to the modem is controlled with:
gpioset 

# If your modem supports USB, this must be enabled,
# disabling the lower external USB port and enabling
# the modem's.
gpioset 209c000.gpio 11=1

# Some modems require NIM_PWR_ON to be "pressed" before they
# turn on. WARNING: If the modem is already on, this same
# sequence may turn it off.
#gpioset 209c000.gpio 31=1
#sleep 1
#gpioset 209c000.gpio 31=0

For example, this initialization is known to work for these modules:

  • NL_SW_LTE_S7588-T-C
  • NL_SW_LTE_SVZM20-B
gpioset 209c000.gpio 11=1 # Route USB to nimbelink
gpioset 6 11=1 # Turn on 4V
gpioset 209c000.gpio 31=1 # assert NIM_PWR_ON
sleep 1
gpioset 209c000.gpio 31=0 # deassert NIM_PWR_ON

For serial modules refer to these related links:

This sample code can be used to verify connectivity to the serial based modules:

wget http://ftp.embeddedTS.com/ftp/ts-arm-sbc/ts-7840-linux/samples/xbeetest.c
gcc xbeetest.c -o xbeetest

gpioset 6 4=1 # Turn on only 3.3V

./xbeetest /dev/ttymxc3

This will print out the module information such as:

XBee3 Zigbee TH RELE: 100A
Build: Apr 16 2020 19:00:33
HV: 424E
Bootloader: 181 Compiler: 8030001
Stack: 6710
OK
Signals Pin Layout
Pin IO Type Signal
1 VCC (XBEE_3.3V or NIMBEL_4.7V)
2 CPU 3.3 ttymxc3 TXD
3 CPU 3.3 ttymxc3 RXD
4 GND
5 Open Drain [1] 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 ttymxc3 CTS#
13 Open drain [1] GPIO Chip 20a4000.gpio IO 2
14 3.3V VREF
15 GND
16 GND
17 NC
18 NC
19 NC
20 Open Drain [1] GPIO Chip 209c000.gpio IO 31 [2]

  1. 1.0 1.1 1.2 Driving high drives this pin to ground. Low tristates
  2. This pin is inverted, Setting to 1 drives pin 2 low.

Revisions and Changes

FPGA Changelog

See the current FPGA revision from the u-boot output:

Model: Technologic Systems i.MX6UL TS-7250-V3
Board: TS-7250-V3 REV A
FPGA:  Rev 24 (47555b21)
DRAM:  1 GiB

Or from Linux:

root@tsimx6:~# tshwctl -i
MODEL=7250
FPGA_REV=24
FPGA_HASH="47555b21"
OPTS=0x0
RAM_MB=1024
PCBREV=A
Revision Changes
24 Initial release
45
  • Updated System clock to expect 79.2MHz instead of 99MHz. This requires the latest u-boot for the clocks to match.
  • SPI Opencore updates
    • Fixed timing issues with SPI busses
      • Allows correct operation at top speed of 19.8MHz
    • Modified SPI core to add CPOL + CPHA
      • Latest kernel driver supports these changes needed to support all 4 SPI modes
  • ISA
    • Includes ISA timing changes. See #PC104_Bus for current timing description.
      • Previously write data was just set before one edge of an isa strobe (ior/iow/memw/memr). This change makes sure data is set for both edges. While not required by the ISA specification, this may improve compatibility with third party devices.
      • ISA pins can now be used as GPIO. No change in behavior by default, but see Syscon 0x08 bit 8 to enable gpio
  • Mikrobus implemented
  • GPIO
49
50
  • Added support for 7MHz on BCLK (ISA B20) See Syscon 0x08.
51
  • Added support for detecting REV C PCBS
52
  • Fixed 16550 control signal polarity. CTS/RTS/RI/DSR/DCD/DTR were previously inverted.
  • This is the last FPGA Revision with REV A support
53
  • Added support for REV C PCBs which bring console over the DB9 port
  • This FPGA and later revisions no longer support REV A PCBs. This should only be written to REV C and later.
56
  • Added support for using RTS instead of TXEN to control transmit enable on RS-485
  • Split the DIO UARTs enable to uart6 enable and uart 7 enable to control them independently
  • Added missing pullup to MIKRO_RESET#

See #Onboard_Firmware_Updates for more details on updating the system.

Note that since the release of REV C PCBs, REV A hardware should not use past REV 52. The update script shown above will update a REV C and later to the newest revision, and REV A to rev 52.

After updating the board must get a full power cycle to load the new bitstream. If the FPGA update fails then this must come back on an RMA to be recovered.

Microcontroller Changelog

On the engineering sampling units (REV A PCB) the microcontroller is a Silicon labs part, but this has been replaced with a Renesas RA4M2 on REV C and later. The REV C and later boards can be updated in the field:

Revision Description
23
  • Initial REV C release
35
  • Added RTC counter controller
  • Added ADC controller
  • Added Reset controller / low power mode
41
  • Support console on DB9, needs latest FPGA update
  • Implement low power mode fixes
  • Reset cause fixes
42
  • Support serial numbers (eth1 mac is now the usb serial number)
43
  • Fix CDC-ACM to fix support with some USB UART clients on Windows 10 after a Windows Update.
    • Putty, Realterm, and possibly others would refuse to open the port.
    • Teraterm was not affected
    • Windows 11 is not affected so far
    • Linux connectivity is not affected
44
  • Expanded 5V range for brownout detection allowing 5V to work in a wider range
47
  • Fixed ADC VREF. This was intended to generate a 2.5V VREF, but at some point regressed and was instead incorrectly generating a 0.8V on previous revisions.

See the tssupervisorupdate project for instructions on updating to the latest release.

PCB Revisions

Revision Description
A
  • Initial release
C
  • Changed Supervisory Microcontroller to a Renesas RA4M2 instead of Silicon Labs C8051F381.
  • Fixed ISA_RESET# polarity so it is deasserted on startup until power off
  • Added minor changes to support console on DB9

Software Images

Debian Changelog

Debian 12 Changelog

Image date Links Changes
20230809 headless minimal
  • Initial release of Debian 12
20230914 headless minimal
  • Fixed /boot/boot.scr for headless image
  • Bumped to kernel v5.10.170.6-ts
20240409 headless minimal
  • Bumped to kernel v5.10.214-ts
    • Includes fixes to GPIO bank numbering on TS-7250-V3 between models

Debian 11 Changelog

Image Changes
tsimx6ul-debian-bullseye-20211016.tar.bz2
  • Initial Bullseye release, includes previous Debian Buster fixes
tsimx6ul-debian-bullseye-20211222.tar.xz
  • Added latest kernel for 16550 fixes
tsimx6ul-debian-bullseye-20221024.tar.xz
  • Added latest kernel with PWM + ISA 16550 support
tsimx6ul-debian-bullseye-20230208.tar.xz
  • Updated kernel to support REV C, and separated /boot/imx6ul-ts7250v3-reva.dtb.
tsimx6ul-debian-bullseye-20230320.tar.xz
  • Updated to kernel 5.10
    • Supports new Supervisor driver for new RTC, Reset controller

Ubuntu Changelog

Ubuntu 23.04 Changelog

Image date Links Changes
20230922 headless minimal
  • Initial release of Ubuntu 23.04
20240409 headless minimal
  • Bumped to kernel v5.10.214-ts
    • Includes fixes to GPIO bank numbering on TS-7250-V3 between models

U-Boot Changelog

Unless you are experiencing issues it is not recommended to change u-boot. If the board is written with an invalid u-boot this will require an RMA to recover.

U-boot Links Changes
SPL-20210513 u-boot-dtb-20210513.img Initial release
SPL-20211015 u-boot-dtb-20211015.img
  • Updated to support 79.2MHz clock required by REV 45
  • Do not update if your FPGA REV is < 45
SPL-20230203 u-boot-dtb-20230203.img
  • Updated to support REV C boards.
  • Rev. A boards now load the device tree:
    • /boot/imx6ul-ts7250v3-reva.dtb
  • This will be used to support these engineering sampling boards going forward. REV C and later will use:
  • /boot/imx6ul-ts7250v3.dtb
SPL-20240820 u-boot-dtb-20240820.img
  • Detects the accelerometer+gyro present and updates the device tree before jumping to linux
    • Supports the existing ism330dlc, and the new ism330dhcx

Onboard Firmware Updates

The FPGA, supervisory microcontroller, and u-boot can all be updated in the field.

The supervisory microcontroller supports atomic updates so it is safe to update at any time, but the FPGA or u-boot must rewrite their running location. If an FPGA or u-boot update are interrupted at the wrong time this may require an RMA to recover.

Its recommended to run from the latest Debian headless image to run the updates, but the updates should run anywhere that has tssupervisorupdate. To run our update script:

wget https://files.embeddedts.com/ts-arm-sbc/ts-7250-v3-linux/update/update
chmod a+x ./update
./update
reboot

The updates will take effect on the next boot.

Product Notes

FCC Advisory

This equipment generates, uses, and can radiate radio frequency energy and if not installed and used properly (that is, in strict accordance with the manufacturer's instructions), may cause interference to radio and television reception. It has been type tested and found to comply with the limits for a Class A digital device in accordance with the specifications in Part 15 of FCC Rules, which are designed to provide reasonable protection against such interference when operated in a commercial environment. Operation of this equipment in a residential area is likely to cause interference, in which case the owner will be required to correct the interference at his own expense.

If this equipment does cause interference, which can be determined by turning the unit on and off, the user is encouraged to try the following measures to correct the interference:

Reorient the receiving antenna. Relocate the unit with respect to the receiver. Plug the unit into a different outlet so that the unit and receiver are on different branch circuits. Ensure that mounting screws and connector attachment screws are tightly secured. Ensure that good quality, shielded, and grounded cables are used for all data communications. If necessary, the user should consult the dealer or an experienced radio/television technician for additional suggestions. The following booklets prepared by the Federal Communications Commission (FCC) may also prove helpful:

How to Identify and Resolve Radio-TV Interference Problems (Stock No. 004-000-000345-4) Interface Handbook (Stock No. 004-000-004505-7) These booklets may be purchased from the Superintendent of Documents, U.S. Government Printing Office, Washington, DC 20402.

Limited Warranty

See our Terms and Conditions for more details.


WARNING: Setting any of the eMMC's write-once registers (e.g. enabling enhanced area and/or write reliability) will immediately void ALL of our return policies and replacement warranties. This includes but is not limited to: the 45-day full money back evaluation period; any returns outside of the 45-day evaluation period; warranty returns within the 1 year warranty period that would require SBC replacement. Our 1 year limited warranty still applies, however it is at our discretion to decide if the SBC can be repaired, no warranty replacements will be provided if the OTP registers have been written.

Trademarks

Arm and Cortex are registered trademarks of Arm Limited (or its subsidiaries) in the US and/or elsewhere.