Daqctl

From embeddedTS Manuals

Overview

daqctl is the userspace application to manage the data acquisition core in the FPGA. This facilitates PWM, counters, digital inputs/outputs, and ADC.

ADC

A single high speed 200ksps 12-bit SPI ADC is connected to a 16 channel analog mux. After the mux, there is an op-amp circuit with a FET connected under FPGA control that adds 2x gain stage before feeding to the SPI National Semiconductor ADC121S021 ADC chip.

Upon reaching the end of the 8Kbyte buffer, the address is wrapped back to zero and sampling continues. Software must be able to keep up and read samples before the address wraps around and old samples are overwritten. There will be an irq every 4kb written to the external memory. The IRQ is cleared and re-armed upon reading of the current state reg.

The ADC can sample based off of either the internally configurable sample rate, or by using one of the digital inputs as an external trigger.

PWM

Our PWM core will accept the PWMSPEC format as a simple way to control it. For example:

  • "0:33%" - Set duty to 33%, preserving frequency
  • "0:33%@100" - Set duty to 33%, frequency at 100Hz
  • "0:33%@1Khz" - Set duty to 33%, frequency at 1Khz
  • "0:0x1230@100" - 0x123 is the 12-bit duty pct (0x123/0x4096)
  • "0:2048" - Preserves the frequency if just setting the duty cycle
  • "0:50%@EXT0" - 50% duty, uses external trig #0 (ADC sample rate)
  • "0:33.333%@100" - should parse decimal also
  • "0:50%@100us" - and period specs rather than freq
  • "0-3:50%" - Can take multiple channels
  • "0,2,5-6:50%" - Can accept multiple channel ranges.

The core allows you to set 2 different frequencies. One is dedicated to the PWM timer which will support up to 131071hz. You can also use the ADC sample rate. This core also supports using external triggers from the digital inputs (1-4).

 Note: If you change any timer for one channel it will 
 change it for any channels currently using it as well.

Usage

Help

 Usage: daqctl [OPTION] ...
 Technologic Systems ADC-7558 core userspace driver utility.
 Example: daqctl --server --speed=13khz --chan=0-3,5,7 --gain2x=5
 
 -i, --irq=N            Use IRQ N as ADC IRQ (30)
 -s, --speed=FREQ       Use FREQ as default sample frequency (1000)
 -c, --chan=CHANS       Sample only channels CHANS (0-15)
 -x, --exttrig=PIN      Override --speed and use external trigger instead
 -g, --gain2x=CHANS     Use high-gain setting on CHANS
 -l, --cloop=CHANS      Enable 4-20 ma current loops on CHANS
 -d, --server           Daemonize and run as server
 -I, --bind=IPADDR      Bind server to IPADDR
 -p, --connect=HOST     Connect to remote daqctl service at HOST
 -P, --dumpcsv          Output acquired samples as CSV text
 -b, --dumpbin          Output acquired samples as raw binary
 -1, --single           Print one sample's values
 -D, --pwm=PWMSPEC      Sets PWM outputs according to PWMSPEC
 -h, --help             This help
 
 --irq-on-change=PIN    Flush when digital input PIN (0-7) changes
 --irq-on-glitch=PIN    Flush when digital input PIN (0-7) glitches
 --irq-on-high=PIN      Flush while digital input PIN (0-7) is high
 --irq-on-low=PIN       Flush while digital input PIN (0-7) is low
 --irq-on-quadrature    Flush when a quadrature counter changes value
 --irq-on-quad-dir      Flush when quadrature changes direction
 --irq-on-counter       Flush when a monitored edge counter changes
 --irq-on-any-change    Flush when any digital input changes
 --max-irq-rate=NSAM    IRQ at most every NSAM (1,4,16,64) samples
 --max-irq-rate-always  Always interrupt at max rate
 --min-irq-rate=HZ      Minimum IRQ rate HZ (1000,500,100,0)
 --min-irq-rate-always  Always interrupt at min rate

Examples

Even without writing code, you can do simple operations like sampling ADC or controlling the PWM channels.

For sampling ADC to a CSV file:

daqctl -c 1-4 -P

Turn all 7 PWM channels (0-6) off (0% duty) (OUT1-OUT4 external and pins 23, 21, and 25 on the JTAG header) and set default PWM

daqctl --pwm=0-6:0%@100hz

Programming with libdaqctl

We provide a library for the end user called libdaqctl that you can use to interact with the daqctl TCP server.

[libdaqctl.c] [libdaqctl.h]

This will allow simple control to receive callbacks, control the PWM, or take ADC samples.

2 PWM frequencies and ADC sampling

This example shows using the PWM frequency and the ADC sample frequency on different channels, as well as sampling ADC channels 1 and 2.

#include <stdio.h>
#include <strings.h>

#include "libdaqctl.h"

int main(int argc, char **argv)
{
        int sk;
        int i;
        struct daqctl dq;
        bzero(&dq, sizeof(dq));

        // You can pass any arguments as you could to 
        // the daqctl application here.  This example only samples 
        // ADC channels 4 and 5
        sk = daqctl_connect("127.0.0.1", "--chan=4-5");
        assert(sk != 0);

        // The daqctl structure needs the file descriptor for process calls
        dq.fd = sk;

        // Set channel 0 to 1khz with a duty cycle of 500khz
        daqctl_setoutput(sk, 0, PWM_FREQ(1000) | PWM_DUTY(50));

        // Set channel 1 to 2khz from ADC core
        daqctl_setoutput(sk, 1, PWM_DUTY_PCT(30) | PWM_FREQ_IS_SAMPLERATE);

        for(;;)
        {
                daqctl_process(&dq);

                for(i = 3; i < 5; i++)
                {
                        printf("chan%i=%i\n", i, dq->last[i]);
                }
        }

	close(sk);

        return 0;
}

Controlling a Servo

In this example I've wired 5v in to OUT1+ (from JTAG pin 26 on the TS-7558), as well as the servo's + wire. OUT- and servo- are connected, and the servo's GND is plugged into one of the ground connectors in P2. The desired behavior is that the servo will rotate back and forth between maximum and minimum and reverting to neutral when a ctrl+c / SIGINT is sent to the application.

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include "libdaqctl.h"

int sigint_rec = 0;

void sigint_cb(int sig);

int main(int argc, char **argv)
{
	char *host;
	if(argc < 2)
		host = "127.0.0.1";
	else
		host = argv[1];

	int daqfd = daqctl_connect(
		host,
		"--pwm=0:100%@20ms");
	assert(daqfd != 0);

	signal(SIGINT, sigint_cb);

	for(;;)
	{
		printf("Maximum\n");
		daqctl_setoutput(daqfd, 0, PWM_DUTY(0x199)); // 2ms
		sleep(1);

		if(sigint_rec)
			break;

		printf("Minimum\n");
		daqctl_setoutput(daqfd, 0, PWM_DUTY(0x51)); // 1ms
		sleep(1);

		if(sigint_rec)
			break;
	}

	printf("Neutral\n");
	daqctl_setoutput(daqfd, 0, PWM_DUTY(0xF5)); // 1.5ms
	sleep(1);

	close(daqfd);

	return 0;
}

void sigint_cb(int sig)
{
	sigint_rec = 1;
}

Controlling an LED

This example toggles the brightness of an LED by toggling power fed from an external source.

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include <limits.h>
#include "libdaqctl.h"
 
int sigint_rec = 0;

void sigint_cb(int sig);
 
int main(int argc, char **argv)
{
        unsigned int i = 0;
        char *host;
        int dir = 0;

        if(argc < 2)
                host = "127.0.0.1";
        else
                host = argv[1];
 
        int daqfd = daqctl_connect(
                host,
                "--pwm=0:100%@120hz");
        assert(daqfd != 0);

        signal(SIGINT, sigint_cb);
 
        daqctl_setoutput(daqfd, 0, PWM_DUTY_PCT(0)); // Off
 
        for(;;)
        {
                const int step = 20;

                // This will use the default PWM frequency
                // set in the connect call
                daqctl_setoutput(daqfd, 0, PWM_DUTY(i));

                printf("%i@120hz\n", i);

                if(i == 0 || i + step >= 4095)
                        dir = !dir;

                if(dir)
                        i += step;
                else
                        i -= step;

                if(sigint_rec)
                        break;

                usleep(10);
        }
 
        daqctl_setoutput(daqfd, 0, PWM_DUTY(0)); // Off
        close(daqfd);
 
        return 0;
}
 
void sigint_cb(int sig)
{
        sigint_rec = 1;
}

FPGA Core

Register Map

Address Description Access Notes
Base + 0 Channel enable #15 (MSB) through #0 (LSB) Read/Write 1 to enable
Base + 2 Current state Read/Write
Bit Access Description
15 Read/Write Sampling Enable
14 Read Only Overflow (clears after read)
13 Read/Write Interrupt mood (1 is interested)
12 N/A Reserved
11-0 Read Only Next buffer num to fill (0-4095 for 8kbyte total buffer)
Base + 4 Sample Period Read/Write
Bit Access Description
15 Read/Write Timescale (1 is ms, 0 is µs)
14-0 Read/Write value (4096 is 4097µs period)
Base + 6 Misc Read/Write
Bit Access Description
15 Read/Write External sample trigger enable
14-12 Read/Write External trigger input number (0-7) (triggers on posedge)
11-8 Read/Write Current loop enable on chans #3 (MSB) - #0 (LSB)
7-0 Read/Write Gain 2x enable on chans #7 (MSB) to #0 (LSB)
Base + 8 Interest criteria Read/Write
Bit Description
15 When any digital input changes
14 When any enabled counters change
13 When a quadrature counter changes direction
12 When a quadrature counter changes value
11 When one particular digital input
10-9 0 - Changes State

1 - Glitches (a change >= 25ns but <= sample period)
2 - Is high
3 - Is low

8-5 If bit 14 is enabled, which digital input? (0-7)
4-3 0 - 1 sample

1 - 4 samples
2 - 16 samples
3 - 64 samples

2 Interrupt rate duration

0 - Until interest criteria is no longer met
1 - Forever (requires reset via write to bit 13, reg 0x2)

1-0 Interrupt when not interested

0 - When buffer is half full
1 - Every 1ms
2 - Every 2ms
3 - Every 10ms

Base + 10 Sample Period Read/Write Return core type (0x7558 or 0x1978)
Base + 12 PWM output controller Read/Write
Base + 14 Current state of all inputs Read Only