From embeddedTS Manuals
WARNING: This article is a work in progress. If you have questions about using the TS-8820-4720, please call our support team, or email support@embeddedTS.com. Thanks!
TS-8820 Product Page
TS-4720 Product Page
Software Support
TS-8820 source directory
Dimensions (in enclosure)
6.35 inches (tall) by 7.714 inches (wide including DB9) by 1.14 inches (deep)


The TS-8820-BOX is a rugged, feature-rich TS-SOCKET based baseboard and System-on-Module (SoM) combination for industrial applications. The TS-8820-BOX is powered by the TS-4100, TS-4700, TS-4710, TS-4720 or TS-4800 System-on-Module devices. The TS-8820-BOX enclosure exposes all of the available I/O on rugged screw terminals while protecting the SoM and other sensitive electronics. The TS-8820-BOX a tough, durable, flexible, powerful, and affordable industrial process control platform.

Getting Started

A Linux PC is recommended for development, and will be assumed for this documentation. For users in Windows or OSX we recommend virtualizing a Linux PC. Most of our platforms run Debian and if there is no personal distribution preference this is what we recommend for ease of use.


Suggested Linux Distributions

It may be possible to develop using a Windows or OSX system, but this is not supported. Development will include accessing drives formatted for Linux and often Linux based tools.

Of particular importance, you will need these things:

  • A desktop computer with an unrestricted internet connection.
  • A wired Ethernet connection to the same local area network as the above desktop computer.
  • A TS-8820-4720.
  • A reliable 12-24 VDC power source.
  • An RS232 serial crossover cable connected to the above Desktop computer.
  • Download the ts8820ctl source code from the TS-8820 ftp directory.

Connecting Power

The TS-8820-4720 takes 12 to 24 VDC input on header P6. Connect ground to P6 pin 6 and + VDC to pin P6 pin 7. Note for applications with particularly strong current draw, P6 pins 8 and 9 can also be used for + VDC, and P6 pin 5 is also a safe GND connection.


The TS-4720 eMMC contains a bootable Linux image by default. The system will start up automatically when connected to power.

Once you have applied power to the baseboard, look for console output. The next section of the manual provides information on getting the console connected. The first output is from the bootrom:

>> TS-BOOTROM - built Jan 21 2013 16:17:55
>> Copyright (c) 2012, Technologic Systems
Uncompressing Linux... done, booting the kernel.
Booted in 0.66 s
Type 'tshelp' for help

The "Booting From" message will indicate your boot media. The 2 dots after indicate steps of the booting procedure. The first dot means the MBR was copied into memory and executed. The next dot indicates that the MBR executed and the kernel/initramfs were copied into memory and executed.

Getting a Console

Option 1: Telnet

If your system is configured with zeroconf support (Avahi, Bonjour, etc) you can simply connect to the TS-4720 with:

telnet ts4720-<last 6 characters of the MAC address>.local
# You will need to use your TS-4720 MAC address, but 
# for example if you mac is 00:d0:69:01:02:03
telnet ts4720-010203.local

When the board first powers up it has two network interfaces. The first interface eth0 is configured to use IPv4LL, and eth0:0 is configured to use DHCP. The board broadcasts using multicast DNS advertising the _telnet._tcp service. You can use this to query all of the available TS-4720s on the network.

From Linux you can use the avahi commands to query for all telnet devices with:

avahi-browse _telnet._tcp

Which would return:

+   eth0 IPv4 TS-4720 console [4f47a5]                      Telnet Remote Terminal local
+   eth0 IPv4 TS-4720 console [4f471a]                      Telnet Remote Terminal local

This will show you the mac address you can use to resolve the board. In this case you can connect to either ts4720-4f47a5 or ts4720-4f47a5.

From Windows you can use Bonjour Print Services to get the dns-sd command. OSX also comes preinstalled with the same command. Once this is installed you can run:

dns-sd -B _telnet._tcp

Which will return:

Browsing for _telnet._tcp
Timestamp     A/R Flags if Domain                    Service Type              Instance Name
10:27:57.078  Add     3  2 local.                    _telnet._tcp.             TS-4720 console [4f47a5]
10:27:57.423  Add     3  2 local.                    _telnet._tcp.             TS-4720 console [4f47a5]

This will show you the mac address you can use to resolve the board. In this case you can connect to either ts4720-4f47a5.local or ts4720-4f47a5.local.

Option 2: Serial Console

The console UART (ttyS0) is an RS232 UART at 115200 baud, 8n1 (8 data bits 1 stop bit), and no flow control. This appears at Pin 2 (TX) and Pin 3 (RX).

Console from Linux

There are many serial terminal applications for Linux, three common used applications are picocom, screen, and minicom. These examples demonstrate all three applications and assume that the serial device is "/dev/ttyUSB0" which is common for USB adapters. Be sure to replace the serial device string with that of the device on your workstation.

picocom is a very small and simple client.

sudo picocom -b 115200 /dev/ttyUSB0

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

sudo screen /dev/ttyUSB0 115200

Or a very commonly used client is minicom which is quite powerful but requires some setup:

sudo minicom -s
  • Navigate to 'serial port setup'
  • Type "a" and change location of serial device to "/dev/ttyUSB0" then hit "enter"
  • If needed, modify the settings to match this and hit "esc" when done:
     E - Bps/Par/Bits          : 115200 8N1
     F - Hardware Flow Control : No
     G - Software Flow Control : No
  • Navigate to 'Save setup as dfl', hit "enter", and then "esc"

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 Putty Configuration


When the board first boots up you should have a console such as:

>> TS-BOOTROM - built Mar 14 2013 15:01:50
>> Copyright (c) 2012, Technologic Systems
Uncompressing Linux... done, booting the kernel.
Booted in 0.90s
Initramfs Web Interface: http://ts47XX-112233.local

This is a minimalistic initial ram filesystem that includes our specific utilities for the board, and is then used to bootstrap the Linux root. The initramfs is built into the kernel image so it cannot be modified without rebuilding the kernel, but it does read several bits from nonvolatile memory for common configuration options we call soft jumpers. Note: Soft jumper settings are not stored on the SD media, so re-flashing your SD card will not reset the soft jumpers. This action can only be taken from within the OS.

WARNING: Setting soft jumper 1 will boot the system straight to Debian, leaving the serial port as the only default access method. Ensure that alternate access methods (telnet, SSH, etc.) are set up and working in Debian if the serial port is not a viable access method before this jumper is set. If a lockout situation does occur, please contact us at support@embeddedTS.com
Soft Jumpers
Jumper Function
1 Boot automatically to Debian [1]
2 Reserved
3 Reserved
4 Reserved
5 Reserved
6 Reserved
7 Skip most of the init. [2]
8 Skip full DRAM test on startup [3]
  1. Initramfs boot is default. Be sure to configure Debian before setting this jumper if serial port access is not possible, see "Warning" above.
  2. This option skips a significant amount of setup and will boot to a single SD card as fast as possible with no initialization. This mode will still execute /mnt/root/ts/init if it exists, or boot to Debian if jp1 is set. Note that this will not initialize any networking in the initramfs, leaving the serial port as the only access method. If booting to Debian, see "Warning" above.
  3. The DRAM test can be used to verify the RAM, but adds approximately 20 seconds to the boot time. This should normally only be enabled when diagnosing problems.

There are 2 ways to manipulate soft jumpers on the board. The web interface at

"http://ts<model>-<last 6 chars of the MAC>.local" 

has a list of checkboxes that will immediately change the values. You can also use tshwctl:

# Boot automatically to Debian:
tshwctl --setjp=1

# Or revert to the initramfs:
tshwctl --removejp=1

The Debian boot can also be inhibited by creating a file in /ts/fastboot in the Debian root. While this file exists the board will stop booting at the initramfs. If you do not have a serial console, make sure you first configure Debian's network settings first before booting directly to Debian. Once JP1 is enabled, the initramfs does not run ifplugd/udhcpc to configure the network.

Most development should be done in Debian, however many applications are capable of running from the initramfs. Utilities from Debian can be accessed under /mnt/root as read only, but for Debian services, or using apt-get a full boot into Debian should be performed. The initramfs itself cannot be easily modified, and it is not recommended to do so. The initramfs however has several hooks for applications to manipulate it's behavior.


For headless applications you can create a bash script with any initialization you require in /ts/init. This does not use the same $PATH as Debian, so you should enter the full path to any applications you intend to run from this environment. The init file does not exist by default and must be created.:


/path/to/your/application &

Remember to set it executable!
chmod a+x /ts/init


Graphical applications run in the initramfs should use /ts/initramfs-xinit. Users booting to Debian should use /usr/bin/default-x-session. The xinit file is used to start up a window manager and any applications. The default initramfs-xinit starts a webbrowser viewing localhost:

# Causes .Xauthority and other temp files to be written to /root/ rather than default /
export HOME=/root/
# Disables icewm toolbars
export ICEWM_PRIVCFG=/mnt/root/root/.icewm/

# minimalistic window manager
icewm-lite &

# this loop verifies the window manager has successfully started
while ! xprop -root | grep -q _NET_SUPPORTING_WM_CHECK
    sleep 0.1

# This launches the fullscreen browser.    If the xinit script ever closes, x11 will close.  This is why the last
# command is the target application which is started with "exec" so it will replace the xinit process id.
exec /usr/bin/fullscreen-webkit http://localhost


This config file can be used to alter many details of the initramfs boot procedure.

## This file is included by the early init system to set various bootup settings.
## if $jp7 is enabled none of these settings will be used.

## Used to control whether the FPGA is reloaded through software.
## 1 to enable reloading (default)
## 0 to disable reloading

## By default dns-sd is started which advertises the ts<model>-<last 6 of mac> 
## telnet and http services using zeroconf.
## 1 to enable dns-sd (default)
## 0 to disable dns-sd

## This is used to discover hosts and advertise this host over multicast DNS.
## 1 to enable mdns (default)
## 0 to disable mdns

## ifplugd is started in the initramfs to start udhcpc, and receive an ipv4ll
## address.
## 1 to enable ifplugd (default)
## 0 to disable ifplugd

## By default telnet is started on port 2323.
## 1 to enable telnet (default)
## 0 to disable telnet

## The busybox webserver is used to display a diagnostic web interface that can
## be used for development tasks such as rewriting the SD or uploading new
## software
## 1 to enable (default)
## 0 to disable

## This eanbles a reset switch on DIO 29 (TS-7700), or DIO 9 on all of the 
## boards (except TS-7250-V2).  Pull low to reset the board immediately.
## 1 to enable the reset sw (default)
## 0 to disable

## The console is forwarded through xuartctl which makes the cpu console available
## over telnet or serial console.
## 1 to enable network console (default)
## 0 to disable network console

## By default Alsa will put the SGTL5000 chip into standby after 5 seconds of 
## inactivity.  This is desirable in that it results in lower power consumption,
## but it can result in an audible popping noise.  This setting prevents 
## standby so the pop is never heard.  
## 1 to disable standby
## 0 to enable standby (default)

## xuartctl is used to access the FPGA uarts.  By default it is configured to
## be IRQ driven which is optimized for best latency, but at the cost of 
## additional CPU time.  You can reduce this by specifying a polling rate.
## The xuartctl process also binds to all network interfaces which can provide a 
## simple network API to access serial ports remotely.  You can restrict this to
## the local network with the bind option.
## Configure XUART polling 100hz
## Default is IRQ driven
## Configure xuartctl to bind on localhost
## Default binds on all interfaces
#CFG_XUARGS="--bind --irq=100hz"
## For a full list of arguments, see the xuartctl documentation here:
## http://docs.embeddedts.com/wiki/Xuartctl#Usage

## By default the system will probe for up to 10s on USB for a mass storage device
## and mount the first partition.  If there is an executable /tsinit script in the
## root this will be executed.  This is intended for production or updates.
## 2 to enable USB init always (adds 10s or $CFG_USBTIME to startup)
## 1 to enable USB init when jp1=0 (default)
## 0 to disable USB init always

## The USB init script by default blocks for 10s to detect a thumb drive that 
## contains the tsinit script.  Most flash media based drives can be detected 
## in 3s or less.  Some spinning media drives can take 10s, or potentially longer.
## This options is the number of seconds to wait before giving up on the 
## mass storage device.

### TS-8700
## Using the TS-8700 baseboard the board will by default initialze all of the 
## ethernet ports as individual vlan ports, eg eth0.1, eth0.2, eth0,3, and eth0.4
## The alterantive option sets Port A to eth0.1, and Ports B-D to eth0.2, or
## you can configure all ethernet ports as a single eth0 port.
## See http://docs.embeddedts.com/wiki/TS-8700 for more information
## 2 disables any vlan and passes through all interfaces to eth0
## 1 enables "WLAN" mode setting "A" as eth0.1, and all others as eth0.2
## 0 enables "VLAN" mode for 4 individual ports (default)

### TS-4712 / TS-4720
## These boards include an onboard switch with 2 external ports.  By default
## the switch will detect if it is on a known baseboard that supports the second
## ethernet switch port, and set up VLAN rules to define eth0.1 and eth0.2.  The
## other option is to configure the switch to pass through the packets to eth0
## regarless of port.
## 2 Disable VLAN and pass through to eth0
## 1 Enable VLAN on all baseboards
## 0 Enable VLAN on supported baseboards (Default)

Debian Configuration

For development, it is recommended to work directly in Debian on the SD card. Debian provides many more packages and a much more familiar environment for users already versed in Debian. Through Debian it is possible to configure the network, use the 'apt-get' suite to manage packages, and perform other configuration tasks. Out of the box the Debian distribution does not have any default username/password set. The account "root" is set up with no password configured. It is possible to log in via the serial console without a password but many services such as ssh will require a password set or will not allow root login at all. It is advised to set a root password and create a user account when the unit is first booted.

Note: Setting up a password for root is only feasible on the uSD image.

It is also possible to cross compile applications. Using a Debian host system will allow for installing a cross compiler to build applications. The advantage of using a Debian host system comes from compiling against libraries. Debian cross platform support allows one to install the necessary development libraries on the host, building the application on the host, and simply installing the runtime libraries on the target device. The library versions will be the same and completely compatible with each other. See the respective Debian cross compiling section for more information.

Configuring the Network

From almost any Linux system you can use "ip" or the ifconfig/route commands to initially set up the network. To configure the network interface manually you can use the same set of commands in the initramfs or Debian.

# Bring up the CPU network interface
ifconfig eth0 up

# Or if you're on a baseboard with a second ethernet port, you can use that as:
ifconfig eth1 up

# Set an ip address (assumes subnet mask)
ifconfig eth0

# Set a specific subnet
ifconfig eth0 netmask

# Configure your route.  This is the server that provides your internet connection.
route add default gw

# Edit /etc/resolv.conf for your DNS server
echo "nameserver" > /etc/resolv.conf

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

Configure DHCP in Debian:

# To setup the default CPU ethernet port
dhclient eth0
# Or if you're on a baseboard with a second ethernet port, you can use that as:
dhclient eth1
# You can configure all ethernet ports for a dhcp response with

Configure DHCP in the initrd:

udhcpc -i eth0
# Or if you're on a baseboard with a second ethernet port, you can use that as:
udhcpc -i eth1

To make your network settings take effect on startup in Debian, edit /etc/network/interfaces:

 # Used by ifup(8) and ifdown(8). See the interfaces(5) manpage or 
 # /usr/share/doc/ifupdown/examples for more information.          
 # We always want the loopback interface.                          
 auto lo                                                           
 iface lo inet loopback                                            
 auto eth0                                                         
 iface eth0 inet static                                            
 auto eth1                                                         
 iface eth1 inet dhcp
Note: During Debian's startup it will assign the interfaces eth0 and eth1 to the detected mac addresses in /etc/udev/rules.d/70-persistent-net.rules. If the system is imaged while this file exists it will assign the new interfaces as eth1 and eth2. This file is generated automatically on startup, and should be removed before your first software image is created. The initrd network configuration does not use this file.

In this example eth0 is a static configuration and eth1 receives its configuration from the DHCP server. For more information on network configuration in Debian see their documentation here.

WIFI Client

This board optionally supports 802.11 through the WIFI-N-USB-2 module using the ath9k_htc driver.

Scan for a network

ifconfig wlan0 up

# Scan for available networks
iwlist wlan0 scan

In this case I'm connecting to "default" which is an open network:

          Cell 03 - Address: c0:ff:ee:c0:ff:ee
                    Encryption key:off
                    Bit Rates:9 Mb/s

To connect to this open network:

iwconfig wlan0 essid "default"

You can use the iwconfig command to determine if you have authenticated to an access point. Before connecting it will show something similar to this:

# iwconfig wlan0
wlan0     IEEE 802.11bgn  ESSID:"default"  
          Mode:Managed  Frequency:2.417 GHz  Access Point: c0:ff:ee:c0:ff:ee   
          Bit Rate=1 Mb/s   Tx-Power=20 dBm   
          Retry  long limit:7   RTS thr:off   Fragment thr:off
          Encryption key:off
          Power Management:off
          Link Quality=70/70  Signal level=-34 dBm  
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:0  Invalid misc:0   Missed beacon:0

If you are connecting using WEP, you will need to define a network key:

iwconfig wlan0 essid "default" key "yourpassword"

If you are connecting to WPA you will need to use wpa_passphrase and wpa_supplicant:

wpa_passphrase the_essid the_password > /etc/wpa_supplicant.conf

Now that you have the configuration file, you will need to start the wpa_supplicant daemon:

wpa_supplicant -Dwext -iwlan0 -c/etc/wpa_supplicant.conf -B

Now you are connected to the network, but this would be close to the equivalent of connecting a network cable. To connect to the internet or talk to your internal network you will need to configure the interface. See the #Configuring the Network for more information, but commonly you can just run:

dhclient wlan0
Note: Some older images did not include the "crda" and "iw" packages required to make a wireless connection. If you cannot get an ip address you may want to connect over ethernet and install these packages with "apt-get install crda iw -y".

Host a WIFI Access Point

The software image includes a build of compat-drivers from 3.8 so a large amount of wireless devices are supported. Some devices support AP/Master mode which can be used to host an access point. The WIFI-N-USB-2 module we provide also supports this mode.

First install hostapd to manage the access point:

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

Edit /etc/hostapd/hostapd.conf to include:

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 create a valid wireless access point, however many devices will not be able to connect without either a static connection, or a DHCP server. Refer to Debian's documentation for more details on DHCP configuration.

Installing New Software

Debian provides the apt-get system which manages pre-built applications. Before packages can be installed, the list of package versions and locations needs to be updated. This assumes the device has a valid network connection to the internet.

Debian Wheezy has been moved to archive status, this requires an update of /etc/apt/sources.list to contain only the following lines:

 deb http://archive.debian.org/debian wheezy main non-free
 deb-src http://archive.debian.org/debian wheezy main non-free
apt-get update
apt-get install --allow-unauthenticated debian-archive-keyring
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.

 <user>@<hostname>:~# apt-cache search openjdk                                                                                  
 icedtea-6-jre-cacao - Alternative JVM for OpenJDK, using Cacao                                                           
 icedtea6-plugin - web browser plugin based on OpenJDK and IcedTea to execute Java applets                                 
 openjdk-6-dbg - Java runtime based on OpenJDK (debugging symbols)                                                        
 openjdk-6-demo - Java runtime based on OpenJDK (demos and examples)                                                      
 openjdk-6-doc - OpenJDK Development Kit (JDK) documentation                                                              
 openjdk-6-jdk - OpenJDK Development Kit (JDK)                                                                            
 openjdk-6-jre-headless - OpenJDK Java runtime, using Hotspot Zero (headless)                                             
 openjdk-6-jre-lib - OpenJDK Java runtime (architecture independent libraries)                                            
 openjdk-6-jre-zero - Alternative JVM for OpenJDK, using Zero/Shark                                                       
 openjdk-6-jre - OpenJDK Java runtime, using Hotspot Zero                                                                 
 openjdk-6-source - OpenJDK Development Kit (JDK) source files                                                            
 openoffice.org - office productivity suite                                                                               
 freemind - Java Program for creating and viewing Mindmaps                                                                
 default-jdk-doc - Standard Java or Java compatible Development Kit (documentation)                                       
 default-jdk - Standard Java or Java compatible Development Kit                                                           
 default-jre-headless - Standard Java or Java compatible Runtime (headless)                                               
 default-jre - Standard Java or Java compatible Runtime                                                                   

In this case you will likely want openjdk-6-jre to provide a runtime environment, and possibly openjdk-6-jdk to provide a development environment. You can often find the names of packages from Debian's wiki or from just searching on google as well.

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-6-jre
# You can also chain packages to be installed
apt-get install openjdk-6-jre nano vim mplayer

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

Setting up SSH

On our boards we include the Debian package for openssh-server, but we remove the automatically generated keys for security reasons. To regenerate these keys:

dpkg-reconfigure openssh-server

Make sure your board is configured properly on the network, and set a password for your remote user. SSH will not allow remote connections without a password or a shared key.

Note: Setting up a password for root is only feasible on the uSD image.
passwd root

You should now be able to connect from a remote Linux or OSX system using "ssh" or from Windows using a client such as putty.

Note: If your intended application does not have a DNS source on the target network, it can save login time to add "UseDNS no" in /etc/ssh/sshd_config.

Starting Automatically

From Debian the most straightforward way to add your application to startup is to create a startup script. This is an example simple startup script that will toggle the red led on during startup, and off during shutdown. In this case I'll name the file customstartup, but you can replace this with your application name as well.

Edit the file /etc/init.d/customstartup to contain this:

 #! /bin/sh
 # /etc/init.d/customstartup
 case "$1" in
     ## If you are launching a daemon or other long running processes
     ## this should be started with
     # nohup /usr/local/bin/yourdaemon &
     # if you have anything that needs to run on shutdown
     echo "Usage: customstartup start|stop" >&2
     exit 3
 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.

To make this run during startup and shutdown:

update-rc.d customstartup defaults

To manually start and stop the script:

/etc/init.d/customstartup start
/etc/init.d/customstartup stop

While this is useful for headless applications, if you are using X11 you should modify "/usr/bin/default-x-session":


export HOME=/root/
export ICEWM_PRIVCFG=/mnt/root/root/.icewm/

icewm-lite &

while ! xprop -root | grep -q _NET_SUPPORTING_WM_CHECK
    sleep 0.1

exec /usr/bin/fullscreen-webkit

Replace fullscreen-webkit with your own graphical application.

Backup / Restore

If you are using a Windows workstation there is no support for writing directly to block devices. However, as long as one of your booting methods still can boot a kernel and the initrd you can rewrite everything by using a usb drive. This is also a good way to blast many stock boards when moving your product into production. You can find more information about this method with an example script here.

Note: Note that the MBR installed by default on this board contains a 446 byte bootloader program that loads the initial power-on kernel and initrd from the first and second partitions. Replacing it with an MBR found on a PC would not work as a PC MBR contains an x86 code bootup program.

MicroSD Card

WARNING: While tools exist for writing image from Windows or other operating systems, we do not support their use. If they are not careful to make sure the OS has not mounted the FS, or existing drivers have ceased any access to the card, they may end up with corruption that is not immediately apparent upon using the card. This may present as sublte corruption, or a card that does not boot at all. We do not encourage use of any other process other than what is described in this section.
Click to download the latest 4GB SD card image.

Using onboard web interface

The initramfs contains a #Web interface that can be used to backup/restore the software image. From the main page, you can download a complete backup containing the MBR, Kernel, initramfs, and Debian filesystem by clicking "backup.dd". You can click "Choose File" and browse to a previous backup.dd, or the link above to rewrite the SD card.

Using another Linux workstation

If you do not have an SD card that can boot to the initramfs, you can download the sd card image and rewrite this from a Linux workstation. A USB MicroSD adapter can be used to access the card. First, you must find out which /dev/ device corresponds with your USB reader/writer.

Step 1 Option 1 (lsblk)

Newer distributions include a utility called "lsblk" which allows simple identification of the intended card:

 sda      8:0    0   400G  0 disk 
 ├─sda1   8:1    0   398G  0 part /
 ├─sda2   8:2    0     1K  0 part 
 └─sda5   8:5    0     2G  0 part [SWAP]
 sr0     11:0    1  1024M  0 rom  
 sdc      8:32   1   3.9G  0 disk 
 ├─sdc1   8:33   1   7.9M  0 part 
 ├─sdc2   8:34   1     2M  0 part 
 ├─sdc3   8:35   1     2M  0 part 
 └─sdc4   8:36   1   2.8G  0 part  

In this case my SD card is 4GB, so sdc is the target device.

Step 1 Option 2 (dmesg)

After plugging in the device, you can use dmesg to list

 scsi 9:0:0:0: Direct-Access     Generic  Storage Device   0.00 PQ: 0 ANSI: 2
 sd 9:0:0:0: Attached scsi generic sg2 type 0
 sd 9:0:0:0: [sdb] 7744512 512-byte logical blocks: (3.96 GB/3.69 GiB)

In this case, sdc is shown as a 3.96GB card.

Step 2

Once you have the target /dev/ device you can use "dd" to backup/restore the card. To restore the board to stock, or rewrite to the latest SD image:

wget https://files.embeddedTS.com/ts-socket-macrocontrollers/ts-4710-linux/binaries/ts-images/4gbsd-471x-latest.dd.bz2
bzip2 -d 4gbsd-471x-latest.dd.bz2

# Specify your block device instead of /dev/sdc
# Note that this does not include a partition, so use /dev/sdc instead of
# using /dev/sdc1
dd if=4gbsd-471x-latest.dd conv=fsync bs=4M of=/dev/sdc

To take a backup of your entire SD card, you can switch the input file and the output file:

dd if=/dev/sdc conv=fsync bs=4M of=backup.dd


Click to download the latest 2GB eMMC image.
WARNING: Make sure the SD and eMMC filesystems are mounted read only while writing any images. If dd is used to read or write a disk while a filesystem on it is mounted read/write this can result in a corrupt image.

First boot to the initramfs. These steps should not be run from the full Debian environment, so to get back to the initramfs, first run:

tshwctl --removejp 1 && reboot

Once you are in the initramfs, mount a network share or usb drive containing the emmc image.

Write an Image to eMMC

# Plug in a thumbdrive formatted with ext2/3/4 or fat32/ntfs
mkdir /mnt/usbdev
mount -o ro /dev/sda1 /mnt/usbdev
dd if=/mnt/usbdev/emmc-image.dd bs=4M of=/dev/nbd1

Save an image from eMMC

# Plug in a thumbdrive formatted with ext2/3/4 or fat32/ntfs
mkdir /mnt/usbdev
mount -o ro /dev/sda1 /mnt/usbdev
# This can take about 15 minutes
dd if=/dev/nbd1 bs=4M of=/mnt/usbdev/emmc-image.dd
umount /mnt/usbdev
Click to download the latest 2GB eMMC image.
WARNING: Make sure the SD and eMMC filesystems are mounted read only while writing any images. If dd is used to read or write a disk while a filesystem on it is mounted read/write this can result in a corrupt image.

First boot to the initramfs. These steps should not be run from the full Debian environment, so to get back to the initramfs, first run:

tshwctl --removejp 1 && reboot

Once you are in the initramfs, mount a network share or usb drive containing the emmc image.

Write an Image to eMMC

# Plug in a thumbdrive formatted with ext2/3/4 or fat32/ntfs
mkdir /mnt/usbdev
mount -o ro /dev/sda1 /mnt/usbdev
dd if=/mnt/usbdev/emmc-image.dd bs=4M of=/dev/nbd1

Save an image from eMMC

# Plug in a thumbdrive formatted with ext2/3/4 or fat32/ntfs
mkdir /mnt/usbdev
mount -o ro /dev/sda1 /mnt/usbdev
# This can take about 15 minutes
dd if=/dev/nbd1 bs=4M of=/mnt/usbdev/emmc-image.dd
umount /mnt/usbdev

Software Development

Most of our examples are going to be in C, but Debian will include support for many more programming languages. Including (but not limited to) C++, PERL, PHP, SH, Java, BASIC, TCL, and Python. Most of the functionality from our software examples can be done from using system calls to run our userspace utilities. For higher performance, you will need to either use C/C++ or find functionally equivalent ways to perform the same actions as our examples. Our userspace applications are all designed to go through a TCP interface. By looking at the source for these applications, you can learn our protocol for communicating with the hardware interfaces in any language.

The most common method of development is directly on the SBC. Since debian has space available on the SD card, we include the build-essentials package which comes with everything you need to do C/C++ development on the board.


Vim is a very common editor to use in Linux. While it isn't the most intuitive at a first glance, you can run 'vimtutor' to get a ~30 minute instruction on how to use this editor. Once you get past the initial learning curve it can make you very productive. You can find the vim documentation here.

Emacs is another very common editor. Similar to vim, it is difficult to learn but rewarding in productivity. You can find documentation on emacs here.

Nano while not as commonly used for development is the easiest. It doesn't have as many features to assist in code development, but is much simpler to begin using right away. If you've used 'edit' on Windows/DOS, this will be very familiar. You can find nano documentation here.


We only recommend the gnu compiler collection. There are many other commercial compilers which can also be used, but will not be supported by us. You can install gcc on most boards in Debian by simply running 'apt-get update && apt-get install build-essential'. This will include everything needed for standard development in c/c++.

You can find the gcc documentation here. You can find a simple hello world tutorial for c++ with gcc here.

Build tools

When developing your application typing out the compiler commands with all of your arguments would take forever. The most common way to handle these build systems is using a make file. This lets you define your project sources, libraries, linking, and desired targets. You can read more about makefiles here.

If you are building an application intended to be more portable than on this one system, you can also look into the automake tools which are intended to help make that easier. You can find an introduction to the autotools here.

Cmake is another alternative which generates a makefile. This is generally simpler than using automake, but is not as mature as the automake tools. You can find a tutorial here.


Linux has a few tools which are very helpful for debugging code. The first of which is gdb (part of the gnu compiler collection). This lets you run your code with breakpoints, get backgraces, step forward or backward, and pick apart memory while your application executes. You can find documentation on gdb here.

Strace will allow you to watch how your application interacts with the running kernel which can be useful for diagnostics. You can find the manual page here.

Ltrace will do the same thing with any generic library. You can find the manual page here.

Accessing Hardware Registers

The standard assumption in Linux is that kernel drivers are required in order to control hardware. However, it is also possible to talk to hardware devices from user space. In doing so, one does not have to be aware of the Linux kernel development process. This is the recommended way of accessing hardware on a TS-SOCKET system. The special /dev/mem device implements a way to access the physical memory from the protected user space, allowing reading and writing to any specific memory register. Applications may be allowed temporary access through memory space windows granted by the mmap() system call applied to the /dev/mem device node.

The following C code is provided as an example of how to set up user space access to the SYSCON registers at base address 0x80004000:

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
static volatile unsigned short *syscon;
static unsigned short peek16(unsigned int adr) {
	return syscon[adr / 2];
static void poke16(unsigned int adr, unsigned short val) {
	syscon[adr / 2] = val;

int main(void) {
        int devmem = open("/dev/mem", O_RDWR|O_SYNC);

	assert(devmem != -1);
	syscon = (unsigned short *) mmap(0, 4096,
	  PROT_READ | PROT_WRITE, MAP_SHARED, devmem, 0x80004000);

        poke16(0x6, 0x3); // disable watchdog
        poke16(0x12, peek16(0x12) | 0x1800); // turn on both LEDs

        return 0;

Important Notes about the preceding example:

  • The peek16 and poke16 wrapper functions make the code more readable due to how pointer arithmetic/array indexing works in C, since the same offsets from the register map appear in the code.
  • Make sure to open using O_SYNC, otherwise you may get a cachable MMU mapping which, unless you know what you're doing, probably is not what you want when dealing with hardware registers.
  • mmap() must be called only on pagesize (4096 byte) boundaries and size must at least have pagesize granularity.
  • Only the root user can open '/dev/mem'. For testing, this just means the tester needs to be root, which is normal in embedded Linux. For deployment in the field under Debian, this can be an issue because the init process does not have root privileges. To get around this, make sure the binary is owned by root and has the setuid bit set. The command 'chmod +s mydriver' will set the setuid flag.
  • The pointers into memory space should have the same bit width as the registers they are accessing. In the example above, the TS-4710 FPGA registers are 16 bits wide, so an unsigned short pointer is used. With very few exceptions, FPGA registers on TS-SOCKET macrocontrollers will be 16 bits wide and CPU registers will be 32 bits wide. Unsigned int, unsigned short, and unsigned char pointers should be used for 32, 16, and 8 bit registers, respectively.
  • When compiling ARM code that emits 16 bit or 8 bit hardware register accesses, it is important to add the compiler switch -mcpu=arm9. Otherwise the wrong opcodes may be emitted by the compiler and unexpected behavior will occur.
  • Pointers into memory space must be declared as volatile.

Cross Compiling

While you can develop entirely on the board itself, if you prefer to develop from another x86 compatible Linux system we have a cross compiler available. For this board you will want to use this toolchain. To compile your application, you only need to use the version of GCC in the cross toolchain instead of the version supplied with your distribution. The resulting binary will be for ARM.

[user@localhost]$ /opt/arm-2008q3/bin/arm-none-linux-gnueabi-gcc hello.c -o hello
[user@localhost]$ file hello
hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.14, not stripped

This is one of the simplest examples. If you want to work with a project, you will typically create a makefile. You can read more about makefiles here. Another common requirement is linking to third party libraries provided by Debian on the board. There is no exact set of steps you can take for every project, but the process will be very much the same. Find the headers, and the libraries. Sometimes you have to also copy over their binaries. In this example, I will link to sqlite from Debian (which will also work in the Ubuntu image).

Install the sqlite library and header on the board:

apt-get update && apt-get install -y libsqlite3-0 libsqlite-dev

This will fetch the binaries from the internet and install them. You can list the installed files with dpkg:

dpkg -L libsqlite3-0 libsqlite3-dev

The interesting files from this output will be the .so files, and the .h files. In this case you will need to copy these files to your project directory.

I have a sample example with libsqlite3 below. This is not intended to provide any functionality, but just call functions provided by sqlite.

#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int main(int argc, char **argv)
	sqlite3 *db;
	char *zErrMsg = 0;
	int rc;
	printf("opening test.db\n");
	rc = sqlite3_open("test.db", &db);
		fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
		fprintf(stderr, "SQL error: %s\n", zErrMsg);
	printf("closing test.db\n");
	return 0;

To build this with the external libraries I have the makefile below. This will have to be adjusted for your toolchain path. In this example I placed the headers in external/include and the library in external/lib.

CFLAGS=-c -Wall

all: sqlitetest

sqlitetest: sqlitetest.o
        $(CC) sqlitetest.o external/lib/libsqlite3.so.0 -o sqlitetest
sqlitetest.o: sqlitetest.c
        $(CC) $(CFLAGS) sqlitetest.c -Iexternal/include/

        rm -rf *o sqlitetest.o sqlitetest

You can then copy this directly to the board and execute it. There are many ways to transfer the compiled binaries to the board. Using a network filesystem such as sshfs or NFS will be the simplest to use if you are frequently updating data, but will require more setup. See your linux distribution's manual for more details. The simplest network method is using ssh/sftp. You can use winscp if from windows, or scp from linux. Make sure you set a password from debian for root or set up a shared key. Otherwise the ssh server will deny connections. From winscp, enter the ip address of the SBC, the root username, and the password you have set or the use of a shared key. This will provide you with an explorer window you can drag files into.

Note: Setting up a password for root is only feasible on the uSD image.

For scp in linux, run:

#replace with your app name and your SBC IP address
scp sqlitetest root@

After transferring the file to the board, execute it:

ts:~# ./sqlitetest 
opening test.db
closing test.db


This piece of software is essential both as an early prototyping tool and a usage example for nearly all functionality provided by the TS-8820-4720. The source code is located on the FTP path here. It is possible to build this tool either on the TS-8820-4720 directly, or to use the methods described above to cross compile the software.

To compile the ts8820ctl application on the TS-8820-4720:

gcc ts8820ctl.c ts8820.c -o ts8820ctl -mcpu=arm9

ts8820ctl usage:

Usage: ./ts8820ctl [OPTION] ...
Technologic Systems TS-8820 FPGA manipulation

General options:
  -s, --sample=N          Display N samples per ADC channel in mV
  -a, --acquire=N         Send N raw samples per ADC channel to stdout
  -r, --rate=SPEED        Sample at SPEED Hz (default 10000)
  -m, --mask=MASK         Sample only channels actived in 16 bit MASK
  -d, --setdac=N          Set DAC #N (1-4)
  -v, --mvolts=VALUE      DAC/PWM voltage in mV (0 to 10000, default 0)
  -p, --pwm=N             Put PWM on digital out #N (1-6)
  -P, --prescalar=VALUE   PWM freq will be (12207/(2^VALUE)) Hz (0-7)
  -1, --hbridge1=ARG      Control H-bridge #1 (0/1/2, default 1)
  -2, --hbridge2=ARG      Control H-bridge #2 (0/1/2, default 1)
  -c, --counter=N         Read pulse counter for digital input #N (1-14)
  -D, --setdio=LVAL       Set DIO output to LVAL
  -G, --getdio            Get DIO input
  -R  --read=BYTES        Read BYTES bytes from TS-8820 SRAM to stdout
  -W  --write=BYTES       Write BYTES bytes from stdin to TS-8820 SRAM
  -h, --help              This help

PWMs 1-6 feed digital outputs; PWMs 7 and 8 feed H-bridges.
The --pwm option overrides the DIO setting and makes the pin a PWM.
The --pwm=N --mvolts=10000 gives a 100% duty cycle.
To revert back to simple DIO, use --mvolts=-1.
H-bridge arguments: 1=run forward; 2=run backward; 0=disable.
To "free-wheel" an H-bridge, set its PWM to 0% and leave it enabled.
A disabled H-bridge will have high-impedance on both sides.

Compile the Kernel

For adding new support to the kernel, or recompiling with more specific options you will need to have an X86 compatible linux host available that can handle the cross compiling. Compiling the kernel on the board is not supported or recommended. Before building the kernel you will need to install a few support libraries on your workstation:



yum install ncurses-devel ncurses
yum groupinstall "Development Tools" "Development Libraries"


sudo apt-get install build-essential libncurses5-dev libncursesw5-dev git
## If you are on a 64-bit system then 32-bit libraries will be required for the toolchain
# sudo apt-get install ia32-libs
# On newer distributions with Multiarch support:
#sudo dpkg --add-architecture i386
#sudo apt-get update
#sudo apt-get install libc6-dev:i386 zlib1g-dev:i386

For other distributions, please refer to their documentation to find equivalent tools.

Set up the Sources and Toolchain

# Download the cross compile toolchain (EABI)from Technologic Systems:
wget ftp://ftp.embeddedTS.com/ts-socket-macrocontrollers/ts-4700-linux/cross-toolchains/arm-2008q3.tar.gz

# Extract the toolchain
tar xvf arm-2008q3.tar.gz

# Move arm-2008q3 to a permanent location, eg /opt/toolchains/
mkdir /opt/toolchains/
mv arm-2008q3 /opt/toolchains/

# Download the Kernel sources
git clone https://github.com/embeddedTS/linux-2.6.34-ts471x.git

cd linux-2.6.34-ts471x

# Set the CROSS_COMPILE variable to the absolute path to the toolchain.
export CROSS_COMPILE=/opt/toolchains/arm-2008q3/bin/arm-none-linux-gnueabi-
export ARCH=arm

# This sets up the default configuration that we ship with for the TS-471x
make ts471x_defconfig

Once you have the configuration ready you can make your changes to the kernel. Commonly a reason for recompiling is to add support that was not built into the standard image's kernel. You can get a menu to browse available options by running:

make menuconfig

You can use the "/" key to search for specific terms through the kernel.

Build the kernel

Once you have it configured you can begin building the kernel. This usually takes about 5-10 minutes.


The new kernel will be at "arch/arm/boot/Image".

Install the Kernel and Modules

Install the target SD card in your workstation, and mount the Debian partition. For example, if your workstation's SD card is /dev/sdb:

# Update this to point to your SD card block device
export DEV=/dev/sdb
sudo mkdir /mnt/sd/
sudo dd if=arch/arm/boot/zImage of="$DEV"1 conv=fsync
sudo mount "$DEV"2 /mnt/sd/
INSTALL_MOD_PATH=/mnt/sd/ sudo -E make modules_install
INSTALL_HDR_PATH=/mnt/sd/ sudo -E make headers_install
sudo umount /mnt/sd/

Build compat-drivers (optional)

Optionally if you use the WIFI-N-USB2 module or another recent USB wireless device you can build "compat-drivers" which provides more recent compatibility on this kernel.

# Assuming you are still in the 2.6.34 kernel directory
cd ../
export ARCH=arm
export CROSS_COMPILE=/opt/toolchains/arm-2008q3/bin/arm-none-linux-gnueabi-

# Update this to point to your SD card block device
export DEV=/dev/sdb
export KLIB=/mnt/sd
# Update these paths to point to the linux tree
export KLIB_BUILD=../linux-2.6.34-ts471x/

wget http://www.kernel.org/pub/linux/kernel/projects/backports/stable/v3.8.3/compat-drivers-3.8.3-2-snpu.tar.bz2 && \
tar xf compat-drivers-3.8.3-2-snpu.tar.bz2 && \
cd compat-drivers-3.8.3-2-snpu/ && \
make && \
sudo mount "$DEV"2 /mnt/sd/ && \
INSTALL_MOD_PATH=/mnt/sd/ sudo -E make install-modules && \
sudo umount /mnt/sd/ && \



The TS-4720 is a Computer On Module design in the TS-SOCKET family. The TS-4720 forms the computational heart of the TS-8820-4720 version of the TS-8820-BOX product. This manual should serve as the complete operational and technical resource for the TS-8820-4720 product combination, but the TS-4720 manual may prove useful under certain unanticipated circumstances.

TS-4720 Syscon

The TS-4720's FPGA register map, commonly referred to as the Syscon, contains the System Configuration registers relevant to the TS-4720 and its communication with any attached devices. In this case, the TS-8820 baseboard and its peripherals. Note the signal names in this register map table refer exclusively to the signals as they appear at the connector to the TS-4720. For more specific signal naming and definitions, see the applicable section(s) of this manual.

The registers listed below are all 16 bit registers and must be accessed with 16 bit reads and writes. This register block appears at base address 0x80004000. For example, to identify the model:

devmem 0x80004000 16

This will return 0x4710, 0x4712, 0x4720, or 0x4740 depending on the model.

Many of the syscon options can be manipulated using tshwctl.

 Usage: tshwctl [OPTION] ...
 Technologic Systems TS-471x / TS-77XX FPGA manipulation.
 General options:
   -g, --getmac            Display ethernet MAC address
   -s, --setmac=MAC        Set ethernet MAC address
   -R, --reboot            Reboot the board
   -t, --getrtc            Get system time from RTC time/date
   -S, --setrtc            Set RTC time/date from system time
   -F, --rtcinfo           Print RTC temperature, poweron/off time, etc
   -v, --nvram             Get/Set RTC NVRAM
   -i, --info              Display board FPGA info
   -e, --greenledon        Turn green LED on
   -b, --greenledoff       Turn green LED off
   -c, --redledon          Turn red LED on
   -d, --redledoff         Turn red LED off
   -D, --setdio=<pin>      Sets DDR and asserts a specified pin
   -O, --clrdio=<pin>      Sets DDR and deasserts a specified pin
   -G, --getdio=<pin>      Sets DDR and gets DIO pin input value
   -x, --random            Get 16-bit hardware random number
   -W, --watchdog          Daemonize and set up /dev/watchdog
   -n, --setrng            Seed the kernel random number generator
   -X, --resetswitchon     Enable reset switch
   -Y, --resetswitchoff    Disable reset switch
   -l, --loadfpga=FILE     Load FPGA bitstream from FILE
   -q, --cputemp           Display the CPU die temperature
   -U, --removejp=JP       Remove soft jumper numbered JP (1-8)
   -J, --setjp=JP          Set soft jumper numbered JP (1-8)
   -k, --txenon=XUART(s)   Enables the TX Enable for an XUART
   -K, --txenoff=XUART(s)  Disables a specified TX Enable
   -N, --canon=PORT(s)     Enables a CAN port
   -f, --canoff=PORT(s)    Disables a CAN port
   -h, --help              This help
   -j, --bbclkon           Enables a 12.5MHz clock on DIO 3
   -H, --bbclkoff          Disables the 12.5MHz clock
   -E, --bbclk2on          Enables a 25MHz clock on DIO 34
   -I, --bbclk2off         Disables the 25MHz clock
   -r, --touchon           Turns the touchscreen controller on
   -T, --touchoff          Turns the touchscreen controller off
   -B, --baseboard         Display baseboard ID
   -a, --adc               Display MCP3428 ADC readings in millivolts
   -P, --ethvlan           Configures a network switch to split each port individually in a vlan
   -y, --ethswitch         Configures a network switch to switch all of the outside ports to one interface
   -e, --ethwlan           Configures the first network port (A) to its own VLAN, and all other ports to a shared switch
   -C, --ethinfo           Retrieves info on the onboard switch
Offset Bits Usage
0x00 15:0 Returns board model, eg 0x4710 = TS-4710
0x02 15 Reset switch enable (Use DIO 9 input)
14 Enable touchscreen (override DIO 30-35)
13 Enable UART4 TXEN (override DIO 14)
12 Enable UART0 TXEN (override DIO 12)
11 Enable 12.5MHz base board clock (override DIO 3)
10 Enable SPI (override DIO 17-20)
9 Enable 2nd CAN (override DIO 10,11)
8 Enable CAN (override DIO 15,16)
7:6 Scratch Register
5 Mode2
4 Mode1
3:0 FPGA revision
0x04 15:0 Muxbus configuration register
0x06 15:0 Watchdog feed register
0x08 15:0 Free running 1MHz counter LSB
0x0a 15:0 Free running 1MHz counter MSB
0x0c 15:0 Hardware RNG LSB
0x0e 15:0 Hardware RNG MSB
0x10 15 Baseboard 25MHz Clock (override DIO 34)
14:0 DIO 14:0 output data
0x12 15:14 Reserved
13 Enable alternate touch controller pins
12 Red LED (1 = on)
11 Green LED (1 = on)
10:6 DIO 26:22 output data
5:0 DIO 20:15 output data
0x14 15:0 DIO 42:27 output data
0x16 15 Enable UART2 TXEN (override DIO 10)
14 Enable UART1 TXEN (override DIO 8)
13 Enable UART5 TXEN (override DIO 7)
12 Enable UART3 TXEN (override DIO 13)
11:0 DIO 59:48 output data
0x18 15 Reserved
14:0 DIO 14:0 data direction
0x1a 15:11 Reserved
10:6 DIO 26:22 data direction
5:0 DIO 20:15 data direction
0x1c 15:0 DIO 42:27 data direction
0x1e 15:12 Reserved
11:0 DIO 59:48 data direction
0x20 15 Reserved
14:0 DIO 14:0 input data
0x22 15:11 Reserved
10:6 DIO 26:22 input data
5:0 DIO 20:15 input data
0x24 15:0 DIO 42:27 input data
0x26 15:12 Reserved
11:0 DIO 59:48 input data
0x28 15:4 Reserved
3:0 FPGA TAG memory access [1]
0x2a 15:0 Custom load ID register [2]
0x2c 15:6 Reserved
5 Offboard IRQ 7
4 Offboard IRQ 6
3 Offboard IRQ 5
0x2e 15:6 Reserved
5 Offboard IRQ 7 mask (1 disabled, 0 on) [3]
4 Offboard IRQ 6 mask (1 disabled, 0 on) [3]
3 Offboard IRQ 5 mask (1 disabled, 0 on)[3]
2 CAN2 IRQ mask (1 disabled, 0 on)[3]
1 CAN IRQ mask (1 disabled, 0 on)[3]
0 XUART IRQ mask (1 disabled, 0 on)[3]
0x34 0 Enable 14.3MHz baseboard clock on DIO 3
1 USB 5V disable [4]
2 LCD 3.3V disable [5]
  1. TAG memory stores persistent data on the FPGA such a the MAC address, CPU settings, and the born on date. Software using this data should instead use tshwctl rather than accessing this register manually.
  2. Reads back 0 on default load. Used to identify customized bitstreams
  3. 3.0 3.1 3.2 3.3 3.4 3.5 The IRQ masks are handled automatically by the kernel after an IRQ is requested. Under most circumstances these registers should not be manipulated.
  4. This toggles a DIO on CN1_04 and requires offboard circuitry on the baseboard to toggle USB power.
  5. This toggles a DIO on CN1_48 and requires offboard circuitry on the baseboard to toggle LCD power.


The TS-8820 is powered by a Lattice XP2 FPGA with 5000 LUTs. Many but not all of the features listed in chapter 5 are driven by FPGA logic. The hardware functionality described in this document is programmed in the FPGA at the factory by default. The TS-SOCKET System-on-Module (SoM) also has an FPGA, but when an FPGA is mentioned in this document it should be assumed that the TS-8820 FPGA is being discussed.

The SoM communicates with the TS-8820 FPGA using the MUXBUS, a simple address/data bus defined by embeddedTS and implemented in the SoM FPGA. TS-8820 application developers do not need to understand the full hardware stack that enables TS-8820 registers to be accessed in memory space. It is necessary to program the MUXBUS registers with values that work for the TS-8820. See ts8820ctl source code for an example.

For applications that require custom logic or interfaces, contact embeddedTS regarding custom FPGA customization. Sources for the The TS-8820 FPGA are also available via special arrangement with embeddedTS, contact sales@embeddedTS.com for more information.

TS-8820 Register Map

This register map assumes a base address offset. The offset when using the TS-4720 is 0x80008000. This address must first be activated using the MUXBUS enable and timing registers on the CPU module. On the TS-4720, write 0xF3FF to the address 0x80004004. Sample code for accessing the functions described in this table are largely encompassed by the ts8820ctl software available on the TS FTP Site.

Offset Bits Description
0x0 15:0 Model ID: Reads 0x8820
0x2 15:11 Reserved
10 Pull-up 5-8 enable
9 Pull-up 3-4 enable
8 Pull-up 1-2 enable
7 H-bridge 2 enable (contacts go high-Z otherwise)
6 H-bridge 1 enable (contacts go high-Z otherwise)
5 H-bridge 2 direction
4 H-bridge 1 direction
3:0 FPGA Revision
0x4 15:14 Reserved
13:0 Digital inputs 14:1
0x6 15:10 Reserved
9:0 SRAM Page register
0x8 15:12 Reserved
11:6 Override Digital Outputs 6:1 with PWM
5:0 Digital Output Values 6:1
0xa 7:0 HD1 input values
15:8 SPI core override for HD1. 1 = SPI mode
0xc 7:0 HD1 output values (see HD1).
15:8 HD1 direction 1=drive output value 0=float pin
0xe 15:0 Reserved
0x10 15:13 PWM #1 Prescaler
12:0 PWM #1 Duty Cycle
0x12 15:13 PWM #2 Prescalar
12:0 PWM #2 Duty Cycle
0x14 15:13 PWM #3 Prescaler
12:0 PWM #3 Duty Cycle
0x16 15:13 PWM #4 Prescaler
12:0 PWM #4 Duty Cycle
0x18 15:13 PWM #5 Prescaler
12:0 PWM #5 Duty Cycle
0x1a 15:13 PWM #6 Prescaler
12:0 PWM #6 Duty Cycle
0x1c 15:13 PWM #7 Prescaler
12:0 PWM #7 Duty Cycle
0x1e 15:13 PWM #8 Prescaler
12:0 PWM #8 Duty Cycle
0x20 15:0 Pulse Counter #1 (RO)
0x22 15:0 Pulse Counter #2 (RO)
0x24 15:0 Pulse Counter #3 (RO)
0x26 15:0 Pulse Counter #4 (RO)
0x28 15:0 Pulse Counter #5 (RO)
0x2a 15:0 Pulse Counter #6 (RO)
0x2c 15:0 Pulse Counter #7 (RO)
0x2e 15:0 Pulse Counter #8 (RO)
0x30 15:0 Pulse Counter #9 (RO)
0x32 15:0 Pulse Counter #10 (RO)
0x34 15:0 Pulse Counter #11 (RO)
0x36 15:0 Pulse Counter #12 (RO)
0x38 15:0 Pulse Counter #13 (RO)
0x3a 15:0 Pulse Counter #14 (RO)
0x3c 15:0 Reserved
0x3e 15:0 Reserved
0x80 15:0 ADC Core ID (reads 0xadc1)
0x82 15:8 ADC Channel Mask (0 = do not save channel data)
7:6 Highest number chip to use (0-3, if 01 then sample chip 0 and chip 1)
5 1 = Force standby
4 1 = Use standby between samples to save power
3 1 = Smart DMA IRQ mode
2 1 = Enable IRQ
1 1 = Collect samples, 0 = stop
0 1 = Reset ADC chips and all FIFOs
0x84 15 1 = There has been a FIFO overflow since last reset
14:0 Number of samples available to be read
0x86 15:0 Sample Data (RO)
0x88 15:0 Sampling period LSB (RW)
0x8a 15:0 Sampling period MSB (RW)
0x8c 15:0 IRQ Threshold (RW)
0x8e 15:0 Reserved
0x90 15:0 Reserved
0x92 15:0 Reserved
0x94 15:0 Reserved
0x96 15:0 Reserved
0x98 15:0 Reserved
0x9a 15:0 Reserved
0x9c 15:0 Reserved
0x9e 15:0 Reserved
0xa0 15:0 DAC 1 Control Register
0xa2 15:0 DAC 2 Control Register
0xa4 15:0 DAC 3 Control Register
0xa6 15:0 DAC 4 Control Register

Temperature Sensor

This System-on-Module includes temperature sensors located on the CPU and RTC. Both of these can be read using tshwctl:

tshwctl --rtctemp
tshwctl --cputemp

Both of these will return the temperature in millicelsius.

Non-Volatile SRAM

The TS-8820 provides 2MB of battery backed static RAM. The RAM is accessed through a 4KB memory window. After programming the SRAM page register, 16 bit reads or writes can be performed to any part of the page.

This can be accessed through ts8820ctl:

echo "test" | ts8820ctl -W 5
ts8820ctl -R 5
Note: The SRAM is not populated by default. See U16 to verify the presence on your board. If you require battery backed SRAM please contact us.


The RTC is accessed using tshwctl. This is automatically retrieved on startup, but must be set manually.

# Save the running system clock to the RTC
tshwctl --setrtc

# Set the system clock from the RTC
tshwctl --getrtc


The RTC has an included 128-byte battery-backed NVRAM which can be accessed using tshwctl. Its contents will remain with the main power off, so long as the RTC battery is installed and withing a valid voltage range.

tshwctl --nvram

This will return a format such as:


This breaks up the NVRAM into 32 32-bit registers which can be accessed in bash. As this uses the name=value output, "eval" can be used for simple parsing:

eval `tshwctl --nvram`
echo $nvram2

From the above value, this would return 0x48ca4278. To set values, the respective environment variable name can be set:

nvram0=0x42 tshwctl --nvram

Note that the command 'tshwctl --nvram' will output the current contents of NVRAM before setting any new values. At this point, running 'tshwctl --nvram' once more will print the updated contents for verification. This can be used for reading a 32-bit quantity and updating it with a single command.

Data Storage

The TS-4720 brings two primary data storage interfaces to the TS-8820-BOX product. Micro SD provides the downstream developer with rapid access to a virtually "unbrickable" media that is easy to replace in case of wear-out during heavy development. The eMMC media provides an exceptionally robust bootable storage device for permanent installations and applications where the robustness of a soldered-down media solution is essential.

MicroSD Interface

This System-on-Module (SoM) uses our SD controller implementation which supports microSD, microSDHC, and microSDXC cards. This controller has been tested with Sandisk Extreme SD cards which allow read speeds up to 20.5MB/s, and write speeds up to 21.5MB/s.

The support for the SD controller is provided by sdctl which serves up a /dev/nbd0 for the entire block device. The kernel also includes a module that will break this up into partitions. Our default software image contains 2 partitions:

Device Contents
/dev/nbd0 SD Card block device
/dev/nbd0p1 Kernel and initramfs
/dev/nbd0p2 Full Linux Root


This board includes an onboard 4GB eMMC disk. This disk is accessed very similar to an SD card and is also supported with the sdctl driver. This eMMC supports read and write speeds at approximately 10MB/s. The kernel provides access to the flash at /dev/nbd1 for the entire block device. The kernel also includes a module that will break this up into partitions. Our default software image contains 2 partitions:

Device Contents
/dev/nbd1 eMMC block device
/dev/nbd1p1 Kernel and initramfs
/dev/nbd1p2 Full Linux Root

Pre 11/2/2014 release TS-4720 eMMC by default uses Single card doublestore. To access the Linux partition of the SD card while booted to eMMC, you can use the mount command as expected:

mkdir /mnt/sd
mount /dev/nbd1p2 /mnt/sd

Post 11/2/2014 release TS-4720 eMMC uses Enhanced SLC mode eMMC, allowing for even greater reliability and longer lifespan. To access the Linux partition of the SD Card while using this eMMC, read the device partition table, and then mount as normal:

busybox blockdev --rereadpt /dev/nbd1
mkdir /mnt/sd
mount /dev/nbd1p2 /mnt/sd

A useful script for this device read activity has been included in the ts.subr definitions file for use within custom startup scripts:

# excpecting "source ts.subr" is already done
scan_lun <lun number>
mkdir /dev/sd
mount /dev/nbd1p2 /mnt/sd

Digital Outputs

The TS-8820 offers 6 digital outputs. OUT1 through OUT4 are isolated and act as a solid state relay capable of switching up to 40 VDC at 1 A continuous draw. OUT5 and OUT6 are non-isolated, able to sink up to 1 A continuous current, and are 40 VDC tolerant.

The outputs can be controlled directly through the 'ts8820ctl' application. See "ts8820ctl.c" and "ts8820.c" for examples of how this writes to the TS-8820 registers.

With the 'ts8820ctl' application, all 6 outputs are manipulated in a single command. That is, a 6-bit value is passed as an argument, and that value is directly set to the outputs. For example, to set OUT5:

ts8820ctl --setdio=0x10

When an output is activated its associated LED indicator is enabled to provide visual feedback.

Isolated Outputs

Digital outputs OUT1 through OUT4 are isolated, each having their own positive and negative terminal. Each isolated output acts as a solid state relay allowing current to flow through the contacts only when it is activated. The isolated outputs are able to switch up to 40 VDC at 1 A continuous current draw. Isolated output terminals are made available on the P7 terminal block.

Non-Isolated Outputs

Digital outputs OUT5 and OUT6 are non-isolated, they rely on the main TS-8820 ground and only have a single contact. Each non-isolated output is a low side switch capable of sinking current when activated. The non-isolated outputs are capable of sinking 1 A continuous current with 40 V input voltage. Non-isolated input terminals appear on the P2 terminal block.


The TS-8820-4720 has four relays available at terminal block P8. All relays follow the same pattern Normally Open (NO), Common (COM), Normally Closed (NC). Relay 1 is pins 1-3, 2 is 4-6, 3 is 7-9, and 4 is 10-12. These relays are controlled via FPGA DIO on the TS-4720 computer module. For operational details please see the source code below.

// ts8820relays.c
// c. Technologic Systems, Inc.  2017
// Written by Michael D Peters.
// Cycles each relay on the TS-8820-47xx once per quarter second in an infinite loop.
// Assumes ts-8820-47xx.  Toggles relays.

#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdint.h>

int main(int argc, char* argv[])

  int i, fb, mem = 0;
  uint16_t *syscon = 0;
  static int rotation[4] = {0x100, 0x80, 0x40, 0x10};

  mem=open("/dev/mem", O_RDWR|O_SYNC);
  syscon = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, mem, 0x80004000);
  // Setup muxbus and turn on baseboard clock.  No harm if it's already done, just a wasted cpu cycle.
  syscon[0x4/2] = 0xF3FF; // TS-8820 needs as much delay as possible in muxbus configuration.
  i = syscon[0x34/2];
  syscon[0x34/2] = i | 0x1; // Bit 1 of 0x34 turns on baseboard clock.  We don't want to disturb the other bits in that register.
  syscon[0x18/2] = 0x1d0;

    for(i = 0; i < 4; i++){
      syscon[0x10/2] = rotation[i];
      usleep(250000); // quarter second.
  return 0;

Digital Inputs

The TS-8820 offers 14 digital input pins. There are 8 isolated inputs that are 30 V tolerant. The 6 non-isolated inputs are 40 V tolerant.

The inputs can be polled directly through the ts8820ctl application. See ts8820ctl.c and ts8820.c for examples of how this reads from the TS-8820 registers.

With the ts8820ctl application, all 14 inputs can be read with a single command. A 14-bit value is returned in hex in a format that can be parsed easily by scripting languages. For example, the following output indicates that IN1 and IN3 are active:

ts8820ctl --getdio

When an input is activated its associated LED indicator is enabled to provide visual feedback.

All inputs (isolated and non-isolated) are also accessible by reading the combined input register at 0x80008004, and are represented in a bitwise manner from bit 0 (input 1) through bit 13 (input 14).

Buffered Inputs

Digital inputs IN9 through IN14 are non-isolated, buffered, active low inputs. Each pin has a nominal threshold of 2.5 VDC, a 3.24 Kohm pull-up to 5 VDC, and are 40 VDC tolerant. Non-isolated inputs are located on the P2 terminal block.

Isolated Inputs

Digital inputs IN1 though IN8 are isolated, each having their own positive and negative terminal. In order to activate an input, a potential of at least 1.4 VDC and not more than 30 VDC must be generated across these terminals. Isolated input terminals are located on P1 and P7 terminal blocks.


We include a userspace IRQ patch in our kernels. This allows you to receive interrupts from your applications where you would normally have to write a kernel driver. This works by creating a file for each interrupt in '/proc/irq/<irqnum>/irq'. The new irq file allows you to block on a read on the file until an interrupt fires.

The original patch is documented here.

The Linux kernel supports up to 16 IRQs from the FPGA. When the CPU receives an IRQ from the FPGA, it uses the IRQ register in the #Syscon to find out which IRQ on the MUX is triggering. Currently only three IRQs are used. Off-board IRQs 5, 6, and 7 correspond to FPGA IRQs 0, 1, and 2, respectively. FPGA IRQs 3 to 15 are reserved for future uses. If the DIO pins are not being used as IRQs, they can be masked out by writing 0 to the corresponding bit in the IRQ mask register.

IRQ # Name Socket Location
49 Combined GPIO Interrupt Any MFP pin
66 CAN 2 IRQ N/A
67 IRQ5/DIO_00[1] CN1-93
68 IRQ6/DIO_01[1] CN1-91
69 IRQ7/DIO_02[1] CN1-89
  1. 1.0 1.1 1.2 The PC/104 IRQs need to have the MUXBUS bus enabled in order to function as IRQs

This example below will work with any of our products that support userspace IRQs. It opens the IRQ number specified in the first argument, and prints when it detects an IRQ.

#include <stdio.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
	char proc_irq[32];
	int ret, irqfd = 0;
	int buf; // Holds irq junk data
	fd_set fds;

	if(argc < 2) {
		printf("Usage: %s <irq number>\n", argv[0]);
		return 1;

	snprintf(proc_irq, sizeof(proc_irq), "/proc/irq/%d/irq", atoi(argv[1]));
	irqfd = open(proc_irq, O_RDONLY| O_NONBLOCK, S_IREAD);

	if(irqfd == -1) {
		printf("Could not open IRQ %s\n", argv[1]);
		return 1;
	while(1) {
		FD_SET(irqfd, &fds); //add the fd to the set
		// See if the IRQ has any data available to read
		ret = select(irqfd + 1, &fds, NULL, NULL, NULL);
		if(FD_ISSET(irqfd, &fds))
			FD_CLR(irqfd, &fds);  //Remove the filedes from set
			printf("IRQ detected\n");
			// Clear the junk data in the IRQ file
			read(irqfd, &buf, sizeof(buf));
		//Sleep, or do any other processing here
	return 0;

Any of the MFP pins can be repurposed to trigger IRQ 49. For example, to make MFP_46 (CN2_72) trigger on a rising edge:

# Enable rising edge detection on MFP_46
peekpoke 32 0xD4019034 0x4000

# Unmask MFP_46
peekpoke 32 0xD40190A0 0x4000

# to clear the interrupt after it has been triggered
peekpoke 32 0xD401904c 0x4000

See page 169 of the CPU manual for more information on the interrupt controller.

External Reset

The external reset pin (DIO 9) will reset the CPU by default when it is low. You can disable this functionality to use this as a DIO by running:

tshwctl --resetswitchoff

This can be disabled with the CFG_RESETSW_EN=0 option in the #Initramfs.

Pulse Counters

Every input has its own wrap-around pulse count register. Wrap-around means that there is no "zero" function. The counter will count up to 65535 pulses and wrap to zero. For usage details please see the sample code below:

// ts8820 pulse counter demo
// c. Technologic Systems, Inc.  2017
// Written by Michael D Peters.
// Required materials:
// TS-8820
// TS-47xx
// 2 jumper wires about 10 inches long.
// 1 100k ohm leaded resistor
// Setup:
// Connect Relay 1's COM pin (P8 pin 2) to IN_14.
// Connect NC (P8 pin 3) to VIN using the 100K ohm resistor.
// Connect NO (P8 pin 1) to GND (any, P4 pin 12 works).
// This program toggles relay 1 and watches the pulse counter
// register for input 14, displaying rising edges coming from
// relay 1.
// Basically, the program provides a cycle count on the relay
//  toggle.  Expect "bounces" during the relay throw as
//  the input pin will float to any noise during the relay's
//  falling transition period.

#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdint.h>

#define RELAY_REGISTER 0x10
#define PULSE_COUNTER_14 0x3a

enum TS8820_RELAYS {
  RELAYS_OFF = 0x0000,
  RELAY4 = 0X10,
  RELAY3 = 0X40,
  RELAY2 = 0X80,
  RELAY1 = 0x100

int main(int argc, char* argv[])
  int i, mem;
  uint16_t *syscon;
  uint16_t *baseboard;

  // memory setup
  mem=open("/dev/mem", O_RDWR|O_SYNC);
  syscon = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, mem, 0x80004000);
  baseboard = mmap(0, getpagesize(), PROT_WRITE|PROT_READ, MAP_SHARED, mem, 0x80008000);
  // set muxbus speed to really slow (high delay).
  syscon[0x4/2] = 0xf3ff;

  // set dio direction to output for the relays.
  syscon[0x18/2] = 0x1d0;

  for(;;){ // forever loop starts here.  
    syscon[RELAY_REGISTER/2] = (uint16_t)RELAY1;
    printf("RELAY1 is ON\n");
    i = baseboard[PULSE_COUNTER_14/2];
    printf("Pulse counter 14 at %d\n", i);
    usleep(125000); // 1/8 second.
    syscon[RELAY_REGISTER/2] = (uint16_t)RELAYS_OFF;
    printf("Relays are OFF\n");
    i = baseboard[PULSE_COUNTER_14/2];
    printf("Pulse counter 14 at %d\n", i);
    usleep(125000); // 1/8 second.

  return 0;

ADC Channels

The TS-8820 offers 16 channels of single ended bi-polar ADC inputs. These ADC inputs are provided by two separate 8 channel ADC devices. Each ADC controller supports a selectable voltage (via GPIO from the SoM, see below) ranges of -5 V to +5 V as well as -10 V to +10 V. This means that each set of 8 channels can be set to different ranges. Each set of 8 channels are sampled simultaneously inside the ADC device.

All 16 ADC inputs are located on the P3, P4, and P5 terminal blocks. While each ADC has a pair of inputs, they are single ended ADC channels; all negative input terminals connect to the TS-8820 common ground.

Additionally, the ADC devices support a number of oversampling options, also controlled via GPIO from the SoM. Enabling oversampling has the effect of adding a digital filter function after the ADC. Increasing the oversampling ratio will decrease the effective sampling rate of the ADC but will increase the signal to noise ratio of each channel. The oversampling rate is shared between both ADC devices, that is, the rate can not be independently set per-device.

Setting the ADC voltage range:

ADC chan. 1 - 8
DIO Val Range
CN2_56 1 -10 V to +10 V
CN2_56 0 -5 V to +5 V
ADC chan. 9 - 16
DIO Val Range
CN2_58 1 -10 V to +10 V
CN2_58 0 -5 V to +5 V

Setting the oversampling rate:

CN2_64 CN2_62 CN2_60 OS Rate
0 0 0 N/A
0 0 1 2
0 1 0 4
0 1 1 8
1 0 0 16
1 0 1 32
1 1 0 64
1 1 1 Invalid
Note: SoM GPIO pins will usually start as inputs with pull up resistors. Therefore the default range will likely be -10 V to +10 V with an invalid oversampling rate. It is advised to set up these pins before acquiring ADC samples.

Current Loops (4-20 mA measurement)

All 16 ADC channels independently support 4-20 mA current loop measurements. This is achieved by setting a pin jumper for the respective channel on the current loop enable pin header. Setting a jumper will electrically enable a 220 Ω 0.5% resistor from the ADC channel to ground allowing for a constant current measurement.


The TS-8820 supports up to 8 thermistors on channels 1 though 8. Support for a thermistor is enabled via software, the ADC pullup bit of TS-8820 FPGA register 0x2. Setting bit 8 enables a pull up on ADC channels 1 and 2, setting bit 9 enables a pull up on ADC channels 3 and 4, and setting bit 10 enables a pull up on channels 5 through 8. When enabled, each channel will get a separate 6.04 kΩ resistor to +12.5 V allowing the use of a thermistor probe.

ADC Usage

The 'ts8820ctl' application can be used to quickly sample the ADCs. This will send simultaneous sampling commands to each of the two ADC devices which will then sample all 16 channels in total the amount of times specified. See the "ts8820ctl.c" and "ts8820.c" files for examples on how this operation takes place.

For example, to sample all of the channels 5 times, the following command would be used:

ts8820ctl --sample=5

Collected 80 samples total.
Ch 1 Ch 2 Ch 3 Ch 4 Ch 5 Ch 6 Ch 7 Ch 8 Ch 9 Ch10 Ch11 Ch12 Ch13 Ch14 Ch15 Ch16 
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 
-7626 2195 2193 2199 2192 2195 2195 2192 2189 2189 2193 2190 2189 2192 2191 2191 
-7626 2194 2192 2198 2191 2194 2194 2191 2188 2189 2192 2189 2188 2191 2189 2190 
-7626 2193 2191 2197 2190 2193 2193 2190 2187 2188 2191 2188 2186 2190 2189 2189 
-7626 2189 2188 2193 2186 2189 2190 2186 2183 2184 2187 2184 2183 2186 2185 2185 
-7626 2189 2187 2192 2186 2188 2189 2186 2183 2183 2187 2184 2182 2185 2184 2185

Above, channel 1 has a -7.62 V voltage source attached to it, all other channels are left unconnected. The range is set at -10 V to +10 V, and the oversampling rate is set to 64.

DAC Channels

The TS-8820 has 4 channels of 0 to +10 V DAC outputs. Each DAC channel has a positive and negative terminal connection. The negative side of the terminals are connected to the common ground of the TS-8820 and are non-isolated. The DAC terminals are on the P3 and P9 terminal blocks.

The DAC channels each have 12 bits of resolution that correspond to 0 to +10 V output. The upper bit (bit 15) of each register is a control bit that is used to synchronize the output of all 4 DAC channels. This means that all four registers can be updated without modifying the actual DAC output. Setting the control bit of any DAC channel register will case an update of the DAC output of all four channels. If the DAC register has not been modified, the update will still be sent but the actual output will remain the same. Reading this control bit will indicate if the synchronization is complete. Writing the DAC registers while the control bit is still asserted, indicating busy, will result in undefined behavior. The update process from setting the control bit to its completion takes approximately 3 microseconds.

When 0 is written to the control bit, the DAC values are updated internally in the FPGA but not transferred to the DAC. Thus any set of channels can be updated simultaneously by only writing a one on the final write.

The DAC channels can be controlled through 'ts8820ctl'. Note that the "--setdac" option to 'ts8820ctl' will always set the control bit of the selected register. See "ts8820ctl.c" and "ts8820.c" for an example of how this process works.

The following command would set DAC output 1 to 0.5 V:

ts8820ctl --setdac=1 --mvolts=500


The TS-8820 has 6 PWM outputs. PWM channels 1 to 6 feed digital outputs 1 to 6 respectively when the PWM override bit is set for a given output.

For all 8 PWM channels, the PWM frequency is approximately (12207/(2^prescaler)) Hz, where the prescaler value is 3 bits wide. That is, a prescaler value of 0 through 7. The PWM duty cycle has 12 bits of resolution. If bit 12 of a PWM register is set, then the PWM output for that channel will be 100% high. Otherwise, the duty cycle setting is divided by 4096 to give the effective duty cycle.

To give OUT5 a ~3 kHZ 50% duty cycle PWM output using ts8820ctl, the following arguments would be used:

# -P sets the PWM frequency via a prescaler value to (12207/(2^VALUE))Hz.
# --mvolts= sets the PWM duty cycle percentage from a decimal scale of 0-10000
# --pwm= PWM channel to enable and modify
ts8820ctl --pwm=5 --mvolts=5000 -P 2


The TS-8820 supports 2 H-bridges on terminal block P6. One bridge drives terminals 1 and 2, and the second drives terminals 3 and 4. Each pair of terminals can be connected directly to a DC motor. Each H-bridge can supply up to 2.8A of current. See the PWM section for more information on how the H-bridges are driven.

Isolated CAN Port

The TS-8820 provides an isolated CAN port on the P10 terminal block. The CAN interface itself is from the SoM with the TS-8820 facilitating the isolated physical interface.

Optionally, a 124 ohm termination resistor can be electrically added by setting the Term. CAN jumper. The SocketCAN device is started by loading the driver thus:

 modprobe sja1000_isa mem=0x81004c00 irq=65 clk=24000000

The CAN controller contained in the FPGA is compatible with the register interface for the SJA1000. This is implemented using SocketCAN.

Before proceeding with the examples, see the Kernel's CAN documentation here.

This board comes preinstalled with can-utils which can be used to communicate over a CAN network without writing any code. The candump utility can be used to dump all data on the network

## First, set the baud rate and bring up the device:
ip link set can0 type can bitrate 250000
ip link set can0 up

## Dump data & errors:
candump -cae can0,0:0,#FFFFFFFF &

## Send the packet with:
#can_id = 0x7df
#data 0 = 0x3
#data 1 = 0x1
#data 2 = 0x0c
cansend can0 7Df#03010c

This example packet is designed to work with the Ozen Elektronik myOByDic 1610 ECU simulator to read the RPM speed. This device will return data from candump with:

 can0  7DF  [3] 03 01 0C                  '...'
 can0  7E8  [8] 04 41 0C 2F C0 00 00 00   '.A./....'
 can0  7E9  [8] 04 41 0C 2F 80 00 00 00   '.A./....'

In this case, 0x2f is the current RPM value. This shows a simple way you can prove out the communication before moving to another language, but this next 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) {
		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) {
		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) {
			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;

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


The XUARTs are ttl serial ports implemented in the FPGA. These communicate with the userspace driver xuartctl. Each XUART core in the FPGA can handle up to 8 XUARTs, though the default TS-4710 FPGA contains 6. The XUART serial ports have a single shared 4kByte receive FIFO which makes real time interrupt latency response less of a concern and in actual implementation, the serial ports are simply polled at 100Hz and don't even use an IRQ. Even with all 8 ports running at 230400 baud, it is not possible to overflow the receive FIFO in 1/100th of a second. The "xuartctl --server" daemon is started by default in the init scripts which sets up listening TCP/IP ports for all XUART channels on ports 7350-7357. An application may simply connect to these ports via localhost (or via the network) and use the serial ports as if they were network services.

The typical method for accessing xuarts is using the pts layer. For example:

eval $(xuartctl --server --port 3 --mode=8n1 --speed 9600 2>&1); ln -s $ttyname /dev/ttyxuart3

This will set up XUART port 3 to 9600 baud, 8n1, and symlink it to /dev/ttyxuart3. In your application you can open the /dev/ttyxuart3 and for most part you can access this just like any other uart. When using the PTS layer, there are several operations that are not supported. The mode and baud rate must be set up with xuartctl, and cannot be programatically changed with the standard ioctl.

The XUARTs can be managed with xuartctl. See the xuartctl page for more details on programming with XUARTs. See either of these links for more information on using serial ports in Linux:

Isolated RS-232

An isolated RS-232 port is on P10, with TX on terminal 7 and RX on terminal 8. The RS-232 port is driven by XUART1.

Isolated RS-485

An isolated RS-485 port is on P10. Terminals 5 and 6 are the + and - lines, respectively. The RS-485 port is driven by XUART0.


The GPIO on HD1 can optionally be used instead for SPI functionality through the SPI core on the TS-8820's FPGA. The TS-8820 uses the same FPGA-based SPI implementation as many other TS-SOCKET and SBCs. The simplest way to turn on the SPI controller is to set the HD1 override register to 0xFF00 thus:

devmem 0x8000800a 16 0xff00

The SPI controller exists at address offset 0xC0.

The table below is the register map for the SPI in the FPGA:

Offset Access Bit(s) Description
0xC0 Read Only 15 SPI MISO state
Read/Write 14 SPI CLK state
Read/Write 13:10 Speed - 0 (highest), 1 (1/2 speed), 2 (1/4 speed)...
Read/Write 9:8 LUN (0-3 representing the 4 chip selects)
Read/Write 7 CS (1 - CS# is asserted)
N/A 6:1 Reserved
Read/Write 0 Speed
0xC2 Read Only 15:0 Previous SPI read data from last write
0xC4 N/A 15:0 Reserved
0xC6 N/A 15:0 Reserved
0xC8 Read/Write 15:0 SPI read/write with CS# to stay asserted
0xCa Read Only 15:0 SPI pipelined read with CS# to stay asserted
0xCc Read/Write 15:0 SPI Read/Write with CS# to deassert post-op
0xCe N/A 15:0 Reserved

The SPI clk state register should be set when CS# is deasserted. Value 0 makes SPI rising edge (CPOL=0), 1 is falling edge (CPOL=1). This only applies to speed >= 1. For speed == 0, SPI clock polarity/skew must be set from the PLL phase adjust registers in the syscon block.

Where the base clock is 75Mhz (extended temp alters this to 50Mhz), speed settings break down as follows:

 0 - 75Mhz (/1)
 1 - 37.5Mhz (/2)
 2 - 18.75Mhz (/4)
 3 - 12.5Mhz (/6)
 4 - 9.375Mhz (/8)
 5 - 7.5Mhz (/10)
 6 - 6.25Mhz (/12)
 7 - 5.36Mhz (/14)
 8 - 4.68Mhz (/16)
 9 - 4.17Mhz (/18)
 15 - 2.5Mhz (/30)
 19 - 1.97MHz (/38)
 31 - 1.21MHz (/62)

The pipelined read register is for read bursts and will automatically start a subsequent SPI read upon completion of the requested SPI read. Reading from this register infers that another read will shortly follow and allows this SPI controller "a head start" on the next read for optimum read performance. This register should be accessed as long as there will be at least one more SPI read with CS# asserted to take place.

Power Supply

The TS-8820 can be powered via PoE or direct DC voltage input to the terminal block connectors. If PoE is not used, power must be supplied on terminal block P6.

DC via Terminal Blocks

Direct power input can be applied to the P6 terminal block. There are three terminals for power and three for a ground connection. Each of the three sets of terminals are electrically connected together internally in the TS-8820. Operational range of the TS-8820 is +10 VDC to +30 VDC on these inputs. Supply an external ground on terminal 10, 11, and/or 12. Supply +10 V to +30 V on terminal 7, 8, and/or 9.

802.3af PoE

The TS-8820 is IEEE 802.3af PoE compliant. This allows the whole unit to be powered directly from a PoE sourcing device.

When powered via PoE, +24 VDC is made available on the P6 terminal block power terminals. Ensure that the total power draw of the system does not exceed the limits defined by IEEE 802.3af and that power is not also supplied to the TS-8820 on these pins!


The TS-8820 has 27 LEDs used to indicate the electrical status of the inputs and outputs on the device. LEDs are labelled as "LED#" on the PCB silkscreen.

The majority of the LEDs are not able to be directly controlled, they turn on or off as a reaction to the status of the I/O they are connected to. For example, when DIG_OUT1 is set, LED17 will turn on. The "System" LEDs are the exception to this and are controlled via the SoM LED interface.

1 System RED_LED 2 Doesn't exist.
3 System Power 4 System GREEN_LED
9 DIG_IN1 10 DIG_IN2
11 DIG_IN3 12 DIG_IN4
13 DIG_IN5 14 DIG_IN6
15 DIG_IN7 16 DIG_IN8
21 DIG_IN9 22 DIG_IN10
23 DIG_IN11 24 DIG_IN12
25 DIG_IN13 26 DIG_IN14

Battery Socket

The coin cell battery is not required for normal TS-8820 operation. The battery provides backup power for the battery backed SRAM and for the real time clock located on the SoM. Without a battery, however, a loss of power will result in a loss of RTC time in the SoM and SRAM data on the TS-8820.

External Interfaces

Terminal Blocks

Note: Rev A TS-8820 PCBs have incorrect P1-P10 silkscreens. Use the diagram below.
Pin Description
1 IN1+
2 IN1-
3 IN2+
4 IN2-
5 IN3+
6 IN3-
7 IN4+
8 IN4-
9 IN5+
10 IN5-
11 IN6+
12 IN6-
Pin Description
1 IN 9
2 IN 10
3 Ground
4 IN 11
5 IN 12
6 Ground
7 IN 13
8 IN 14
9 Ground
10 OUT 5
11 OUT 6
12 Ground
Pin Description
1 ADC Channel 13
2 Ground
3 ADC Channel 14
4 Ground
5 ADC Channel 15
6 Ground
7 ADC Channel 16
8 Ground
9 DAC 1
10 Ground
11 DAC 2
12 Ground
Pin Description
1 ADC Channel 7
2 Ground
3 ADC Channel 8
4 Ground
5 ADC Channel 9
6 Ground
7 ADC Channel 10
8 Ground
9 ADC Channel 11
10 Ground
11 ADC Channel 12
12 Ground
Pin Description
1 ADC Channel 1
2 Ground
3 ADC Channel 2
4 Ground
5 ADC Channel 3
6 Ground
7 ADC Channel 4
8 Ground
9 ADC Channel 5
10 Ground
11 ADC Channel 6
12 Ground
Pin Description
5 Ground
6 Ground
7 EXT_12V_24V
8 EXT_12V_24V
9 EXT_12V_24V
Pin Description
1 IN7+
2 IN7-
3 IN8+
4 IN8-
5 OUT1+
6 OUT1-
7 OUT2+
8 OUT2-
9 OUT3+
10 OUT3-
11 OUT4+
12 OUT4-
Pin Description
1 Relay 1 NO
2 Relay 1 COM
3 Relay 1 NC
4 Relay 2 NO
5 Relay 2 COM
6 Relay 2 NC
7 Relay 3 NO
8 Relay 3 COM
9 Relay 3 NC
10 Relay 4 NO
11 Relay 4 COM
12 Relay 4 NC
Pin Description
1 DAC 3
2 Ground
3 DAC 4
4 Ground
5 Spare 1
6 Spare 2
7 Spare 3
8 Spare 4
9 Spare 5
10 Spare 6
11 Spare 7
12 Spare 8
Pin Description
1 Not Connected
2 Not Connected
3 ISO Common
4 ISO Common
9 Not Connected
10 CAN Common
11 CAN_H
12 CAN_L

Ethernet Connector

The TS-8820 has a single 10/100 Ethernet port that can connect to any Ethernet LAN. The Ethernet connector includes LEDs indicating link and activity. The link LED should be on whenever the TS-8820 is powered and connected to a LAN. This connector allows the TS-8820 to be powered by PoE.

USB Host

The USB is available on two ports as a USB 2.0 host.

USB Host
USB Host
Header PIN Name
1 USB_5V


There is a pushbutton between the two USB ports connected to the TS-4720 DIO_9 signal (read via TS-4720#syscon). This button is a simple GPIO button with no default behavior. One suggested usage is to use the button in the downstream application to signal a software induced reset or reboot.

DB9 Connector

DB9 Connector
Pin Description
1 XUART4 RS485+
2 Debug Console RS232 RXD
3 Debug Console RS232 TXD
4 Not Connected
5 Ground
6 XUART4 RS485-
9 Not Connected


HD1 is a 26 pin header located just inside the enclosure of the TS-8820's main body next to the relays. This header offers some extra functionality both for possible internal custom expansion boards and external functionality.

The first 8 pins on the header are designated SPARE_1 through SPARE_8. These pins are connected directly to pins 5 through 12 on header P1. This allows the internal header's signals starting at pin 13 to be connected to sensors or used as GPIO either internally mounted inside the TS-8820's enclosure or externally by wiring the signals from the signal section of the header to the SPARE section of the header. Note the SPARE_x signals (pins 1-8) are non-isolated.

Pin 1 of this header is on the outside edge at the end nearest the green terminal blocks.

PIN Signal Name Function ----- PIN Signal Name Function
1 SPARE_8 P9.5 2 SPARE_7 P9.6
3 SPARE_6 P9.7 4 SPARE_5 P9.8
5 SPARE_4 P9.9 6 SPARE_3 P9.10
7 SPARE_2 P9.11 8 SPARE_1 P9.12
9 10
11 12
13 FPGA_SPARE1 gpio 3.3v (bit 0) 14 GND
15 TXCL_DATA4 gpio/CS#3 (bit 2) 16 FPGA_SPARE2 gpio 3.3v (bit 1)
17 TXCL_DATA3 gpio/CS#2 (bit 3) 18 V_PLUS VIN
19 TXCL_DATA2 gpio/CS#0 (bit 4) 20
21 TXCL_DATA1 gpio/MOSI (bit 5) 22 3.3V
23 TXCL_SYNC# gpio/MISO (bit 6) 24 5V
25 TXCL_CLK gpio/sck (bit 7) 26 GND

Footnote 1: All bitwise signals have a nominal pull-up to logic high when in GPIO mode.

Footnote 2: (bit x) refers to the bit numbering used in the TS-8820 register map when referencing these signals.


Power Consumption

All tests are performed with the PXA166 (797MHz) at 12V, with Ethernet, USB, disconnected and all IO in default states unless otherwise specified.

Test Average Max
Idle 276mA/3.31W 285mA/3.42W
Busy CPU 314mA/3.76W 319mA/3.83W
Idle with Ethernet connected 290mA/3.48W 312mA/3.74W
Idle with all 4 relays enabled 463mA/5.56W 510mA/6.13W

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.