comedilib/demo/ao_waveform.c
Ian Abbott 80d809282c demo: update demo programs to set read or write subdevice
Attempt to make the streaming command demos work if the specified
subdevice is not the default 'read' or 'write' subdevice (depending on
the direction of the command).

This uses the new comedi_set_read_subdevice() and
comedi_set_write_subdevice() functions.

Changing the read or write subdevice is currently supported only by the
Linux "in-tree" version of Comedi since kernel version 3.19.
2016-05-13 17:19:10 +01:00

374 lines
8.2 KiB
C

/*
* Asynchronous Analog Output Example
* Part of Comedilib
*
* Copyright (c) 1999,2000 David A. Schleef <ds@schleef.org>
*
* This file may be freely modified, distributed, and combined with
* other software, as long as proper attribution is given in the
* source code.
*/
/*
* Requirements: Analog output device capable of
* asynchronous commands.
*
* This demo uses an analog output subdevice with an
* asynchronous command to generate a waveform. The
* demo hijacks for -n option to select a waveform from
* a predefined list. The default waveform is a sine
* wave (surprise!). Other waveforms include sawtooth,
* square, triangle and cycloid.
*
* The function generation algorithm is the same as
* what is typically used in digital function generators.
* A 32-bit accumulator is incremented by a phase factor,
* which is the amount (in radians) that the generator
* advances each time step. The accumulator is then
* shifted right by 20 bits, to get a 12 bit offset into
* a lookup table. The value in the lookup table at
* that offset is then put into a buffer for output to
* the DAC.
*
* [ Actually, the accumulator is only 26 bits, for some
* reason. I'll fix this sometime. ]
*
*/
#include <stdio.h>
#include <comedilib.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <getopt.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include "examples.h"
/* frequency of the sine wave to output */
double waveform_frequency = 10.0;
/* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
double amplitude = 4000;
/* offset, in DAC units */
double offset = 2048;
/* This is the size of chunks we deal with when creating and
outputting data. This *could* be 1, but that would be
inefficient */
#define BUF_LEN 0x8000
int external_trigger_number = 0;
sampl_t data[BUF_LEN];
void dds_output(sampl_t *buf,int n);
void dds_init(double waveform_frequency, double update_frequency, int fn);
void dds_init_sine(void);
void dds_init_pseudocycloid(void);
void dds_init_cycloid(void);
void dds_init_ramp_up(void);
void dds_init_ramp_down(void);
void dds_init_triangle(void);
void dds_init_square(void);
void dds_init_blancmange(void);
static void (* const dds_init_function[])(void) = {
dds_init_sine,
dds_init_ramp_up,
dds_init_ramp_down,
dds_init_triangle,
dds_init_square,
dds_init_cycloid,
dds_init_blancmange,
};
#define NUMFUNCS (sizeof(dds_init_function)/sizeof(dds_init_function[0]))
int main(int argc, char *argv[])
{
comedi_cmd cmd;
int err;
int n,m;
int total=0;
comedi_t *dev;
unsigned int chanlist[16];
unsigned int maxdata;
comedi_range *rng;
int ret;
struct parsed_options options;
int fn;
init_parsed_options(&options);
options.subdevice = -1;
options.n_chan = -1; /* waveform */
parse_options(&options, argc, argv);
/* Use n_chan to select waveform (cheat!) */
fn = options.n_chan;
if(fn < 0 || fn >= NUMFUNCS){
fprintf(stderr,"Use the option '-n' to select another waveform.\n");
fn = 0;
}
/* Force n_chan to be 1 */
options.n_chan = 1;
if(options.value){
waveform_frequency = options.value;
}
dev = comedi_open(options.filename);
if(dev == NULL){
fprintf(stderr, "error opening %s\n", options.filename);
return -1;
}
if(options.subdevice < 0)
options.subdevice = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_AO, 0);
maxdata = comedi_get_maxdata(dev, options.subdevice, options.channel);
rng = comedi_get_range(dev, options.subdevice, options.channel, options.range);
offset = (double)comedi_from_phys(0.0, rng, maxdata);
amplitude = (double)comedi_from_phys(1.0, rng, maxdata) - offset;
memset(&cmd,0,sizeof(cmd));
cmd.subdev = options.subdevice;
cmd.flags = CMDF_WRITE;
cmd.start_src = TRIG_INT;
cmd.start_arg = 0;
cmd.scan_begin_src = TRIG_TIMER;
cmd.scan_begin_arg = 1e9 / options.freq;
cmd.convert_src = TRIG_NOW;
cmd.convert_arg = 0;
cmd.scan_end_src = TRIG_COUNT;
cmd.scan_end_arg = options.n_chan;
cmd.stop_src = TRIG_NONE;
cmd.stop_arg = 0;
cmd.chanlist = chanlist;
cmd.chanlist_len = options.n_chan;
chanlist[0] = CR_PACK(options.channel, options.range, options.aref);
//chanlist[1] = CR_PACK(options.channel + 1, options.range, options.aref);
dds_init(waveform_frequency, options.freq, fn);
if (options.verbose)
dump_cmd(stdout,&cmd);
err = comedi_command_test(dev, &cmd);
if (err < 0) {
comedi_perror("comedi_command_test");
exit(1);
}
err = comedi_command_test(dev, &cmd);
if (err < 0) {
comedi_perror("comedi_command_test");
exit(1);
}
comedi_set_write_subdevice(dev, cmd.subdev);
ret = comedi_get_write_subdevice(dev);
if (ret < 0 || ret != cmd.subdev) {
fprintf(stderr,
"failed to change 'write' subdevice from %d to %d\n",
ret, cmd.subdev);
exit(1);
}
if ((err = comedi_command(dev, &cmd)) < 0) {
comedi_perror("comedi_command");
exit(1);
}
dds_output(data,BUF_LEN);
n = BUF_LEN * sizeof(sampl_t);
m = write(comedi_fileno(dev), (void *)data, n);
if(m < 0){
perror("write");
exit(1);
}else if(m < n)
{
fprintf(stderr, "failed to preload output buffer with %i bytes, is it too small?\n"
"See the --write-buffer option of comedi_config\n", n);
exit(1);
}
if (options.verbose)
printf("m=%d\n",m);
ret = comedi_internal_trigger(dev, options.subdevice, 0);
if(ret < 0){
perror("comedi_internal_trigger\n");
exit(1);
}
while(1){
dds_output(data,BUF_LEN);
n=BUF_LEN*sizeof(sampl_t);
while(n>0){
m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
if(m<0){
perror("write");
exit(0);
}
if (options.verbose)
printf("m=%d\n",m);
n-=m;
}
total+=BUF_LEN;
//printf("%d\n",total);
}
return 0;
}
#define WAVEFORM_SHIFT 16
#define WAVEFORM_LEN (1<<WAVEFORM_SHIFT)
#define WAVEFORM_MASK (WAVEFORM_LEN-1)
sampl_t waveform[WAVEFORM_LEN];
unsigned int acc;
unsigned int adder;
void dds_init(double waveform_frequency, double update_frequency, int fn)
{
adder = waveform_frequency / update_frequency * (1 << 16) * (1 << WAVEFORM_SHIFT);
(*dds_init_function[fn])();
}
void dds_output(sampl_t *buf,int n)
{
int i;
sampl_t *p=buf;
for(i=0;i<n;i++){
*p=waveform[(acc>>16)&WAVEFORM_MASK];
p++;
acc+=adder;
}
}
/* Defined for x in [0,1] */
static inline double triangle(double x)
{
return (x > 0.5) ? 1.0 - x : x;
}
void dds_init_sine(void)
{
int i;
double ofs = offset;
double amp = 0.5 * amplitude;
if(ofs < amp){
/* Probably a unipolar range. Bump up the offset. */
ofs = amp;
}
for(i=0;i<WAVEFORM_LEN;i++){
waveform[i]=rint(ofs+amp*cos(i*2*M_PI/WAVEFORM_LEN));
}
}
/* Yes, I know this is not the proper equation for a
cycloid. Fix it. */
void dds_init_pseudocycloid(void)
{
int i;
double t;
for(i=0;i<WAVEFORM_LEN/2;i++){
t=2*((double)i)/WAVEFORM_LEN;
waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t));
}
for(i=WAVEFORM_LEN/2;i<WAVEFORM_LEN;i++){
t=2*(1-((double)i)/WAVEFORM_LEN);
waveform[i]=rint(offset+amplitude*sqrt(1-t*t));
}
}
void dds_init_cycloid(void)
{
enum { SUBSCALE = 2 }; /* Needs to be >= 2. */
int h, i, ni;
double t, x, y;
i = -1;
for (h = 0; h < WAVEFORM_LEN * SUBSCALE; h++){
t = (h * (2 * M_PI)) / (WAVEFORM_LEN * SUBSCALE);
x = t - sin(t);
ni = (int)floor((x * WAVEFORM_LEN) / (2 * M_PI));
if (ni > i) {
i = ni;
y = 1 - cos(t);
waveform[i] = rint(offset + (amplitude * y / 2));
}
}
}
void dds_init_ramp_up(void)
{
int i;
for(i=0;i<WAVEFORM_LEN;i++){
waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);
}
}
void dds_init_ramp_down(void)
{
int i;
for(i=0;i<WAVEFORM_LEN;i++){
waveform[i]=rint(offset+amplitude*((double)(WAVEFORM_LEN-1-i))/WAVEFORM_LEN);
}
}
void dds_init_triangle(void)
{
int i;
for (i = 0; i < WAVEFORM_LEN; i++) {
waveform[i] = rint(offset + amplitude * 2 * triangle((double)i / WAVEFORM_LEN));
}
}
void dds_init_square(void)
{
int i;
for (i = 0; i < WAVEFORM_LEN / 2; i++) {
waveform[i] = rint(offset);
}
for ( ; i < WAVEFORM_LEN; i++) {
waveform[i] = rint(offset + amplitude);
}
}
void dds_init_blancmange(void)
{
int i, n;
double b, x;
for (i = 0; i < WAVEFORM_LEN; i++) {
b = 0;
for (n = 0; n < 16; n++) {
x = (double)i / WAVEFORM_LEN;
x *= (1 << n);
x -= floor(x);
b += triangle(x) / (1 << n);
}
waveform[i] = rint(offset + amplitude * 1.5 * b);
}
}