4710 ADC

From Technologic Systems Manuals

The FPGA includes a core for communicating with the MCP3428 ADC controller we use on several of our baseboards. If you are using this on your own baseboard this core assumes the standard circuit which allows 2 differential channels and 4 single-ended channels. The single-ended channels are chosen using analog muxes controlled by the AN_SEL line. Since different baseboards use a different pin for AN_SEL, a register is also provided to select the correct lines. Channels 1 and 2 are differential channels with a range of -2.048V to +2.048V. Channels 3-6 are 0 to 10.24V.

Our default core shipped with the TS-4710 does not include support for ADC. Support can be included with this opencore bitstream. See the FPGA section for more information on loading this bitstream, or building a custom one.

This example prints out all 6 ADC readings in millivolts:

#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <assert.h>
#include <fcntl.h>

#define peek16(adr) PEEK16((unsigned long)&syscon[(adr)/2])
#define poke16(adr, val) POKE16((unsigned long)&syscon[(adr)/2],(val))

static volatile unsigned short *syscon;

static inline unsigned short PEEK16(unsigned long addr) {
        unsigned short ret;

        asm volatile (
                "ldrh %0, [ %1 ]\n"
                : "=r" (ret)
                : "r" (addr)
                : "memory"
        return ret;

static inline void POKE16(unsigned long addr, unsigned short dat) {
        asm volatile (
                "strh %1, [ %0 ]\n"
                : "r" (addr), "r" (dat)
                : "memory"

int main()
	int x, i, devmem;

	// Map the Syscon core
	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);

	//// Select AN_SEL line:
	//// If you have a TS-TPC-8390 baseboard:
	poke16(0x400, 0x28);
	//// TS-8160/TS-8100
	//poke16(0x400, 0x18);
	//// if unknown baseboard, uses no an_sel
        //// but assumes ADC is there
	//poke16(0x400, 0x08);

	// enable all 6 channels
	poke16(0x402, 0x3f);
	// allow time for conversions

	for (i = 1; i <= 6; i++) {
		x = (signed short)peek16(0x402 + 2*i);
                if (i > 2) x = (x * 1006)/200;
                x = (x * 2048)/0x8000;
                printf("adc%d=%d\n", i, x);

	return 0;

Running this code on a TS-TPC-8390 with pin 7 of the ADC header (channel 3) connected to 3.3V returns:

 root@ts4700:~# ./adctest 
ADC Core Register Map
Offset Bits Description
0x0 15:8 Core ID register (reads 0xad)
7:6 Reserved
Analog Select Pin
Value Description
0 Do not use an AN+SEL
1 use CN1 pin 77 for AN_SEL (TS-8100)
2 use CN1 pin 74 for AN_SEL (TS-8390)
3 Reserved
Value Description
0 240Hz, 12 bit resolution
1 60Hz, 14 bit resolution
2 15Hz, 16 bit resolution
3 Reserved
Programmable Gain Amplifier
Value Description
0 No gain
1 2x gain
2 4x gain
3 8x gain
0x2 15:0 Channel Mask
0x4 15:0 Channel 1 most recent conversion value
0x6 15:0 Channel 2 most recent conversion value
0x8 15:0 Channel 3 most recent conversion value
0xa 15:0 Channel 4 most recent conversion value
0xc 15:0 Channel 5 most recent conversion value
0xe 15:0 Channel 6 most recent conversion value

The channel mask register controls which channels are enabled. Bits 0-5 enable channels 1-6 respectively. If a given channel is not enabled, (enable bit == 0) it will not be sampled and its conversion value register will contain an obsolete and meaningless value. The more channels that are enabled, the lower the sampling speed on each channel.