TS-7250-V3 TS-ADC24

From embeddedTS Manuals
TS-ADC24
Product Page
alt 16-bit IO

The TS-ADC24 provides 24 channels of 12-bit analog to digital conversion at 4 channels of 12-bit digital to analog conversion. On the TS-7250-V3 this will support a max sample rate of 8khz with the minimum 4 channels enabled.

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

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

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

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

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

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

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

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

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

#include "pc104.h"

#define TSADC24_BASE 0x100
#define SAMPLE_SIZE 1024

/* 
 * 32MHz delays
 * 1/32000000=31.35ns increments
 * 4000 * 31.35ns = 125.7us.  1/0.0000627 = 7955hz sample rate
 */
#define ADCDLY 4000

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

uint16_t twos_complement_to_unsigned(uint16_t val)
{
	if(val & (1 << 11))
		return val -= 2048;
	return val += 2048;
}

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

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

	pc104_init();
	if((pc104_io_16_alt_read(TSADC24_BASE) & 0xff) != 0x3F) {
		fprintf(stderr, "TS-ADC24 not detected");
		return 1;
	}

	assert(MAX_CHAN_PAIR < 8);

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

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

		for (i = 0; i < fifocnt; i++) {
			int16_t buffer = pc104_io_16_alt_read(TSADC24_BASE + 0xa);
			buffer &= 0xfff;

			/* Vref*2 uses 2s complement for 0-5V */
			buffer = twos_complement_to_unsigned(buffer);

			ret = write(1, &buffer, sizeof(int16_t));
			assert(ret == sizeof(int16_t));
		}
	} while(1);

	return 0;
}

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