2000-02-02 05:14:23 +00:00
|
|
|
/*
|
2000-10-19 06:28:27 +00:00
|
|
|
* 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
|
2007-11-30 16:02:30 +00:00
|
|
|
* 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.
|
2000-10-19 06:28:27 +00:00
|
|
|
*
|
|
|
|
* 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. ]
|
|
|
|
*
|
2000-02-02 05:14:23 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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>
|
2003-01-01 23:07:02 +00:00
|
|
|
#include <string.h>
|
2000-10-19 06:28:27 +00:00
|
|
|
#include "examples.h"
|
2000-02-02 05:14:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* frequency of the sine wave to output */
|
2002-06-07 00:16:42 +00:00
|
|
|
double waveform_frequency = 10.0;
|
2000-02-02 05:14:23 +00:00
|
|
|
|
|
|
|
/* 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 */
|
2004-01-02 00:06:33 +00:00
|
|
|
#define BUF_LEN 0x8000
|
2000-02-02 05:14:23 +00:00
|
|
|
|
|
|
|
int external_trigger_number = 0;
|
|
|
|
|
|
|
|
sampl_t data[BUF_LEN];
|
|
|
|
|
|
|
|
void dds_output(sampl_t *buf,int n);
|
2007-11-30 16:02:30 +00:00
|
|
|
void dds_init(double waveform_frequency, double update_frequency, int fn);
|
2000-10-19 06:28:27 +00:00
|
|
|
|
2000-02-02 05:14:23 +00:00
|
|
|
void dds_init_sine(void);
|
|
|
|
void dds_init_pseudocycloid(void);
|
2007-11-30 16:02:30 +00:00
|
|
|
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);
|
2007-11-30 18:20:15 +00:00
|
|
|
void dds_init_blancmange(void);
|
2007-11-30 16:02:30 +00:00
|
|
|
|
|
|
|
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,
|
2007-11-30 18:20:15 +00:00
|
|
|
dds_init_blancmange,
|
2007-11-30 16:02:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define NUMFUNCS (sizeof(dds_init_function)/sizeof(dds_init_function[0]))
|
2000-02-02 05:14:23 +00:00
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2000-10-19 06:28:27 +00:00
|
|
|
comedi_cmd cmd;
|
2000-02-02 05:14:23 +00:00
|
|
|
int err;
|
2000-10-19 06:28:27 +00:00
|
|
|
int n,m;
|
2000-02-02 05:14:23 +00:00
|
|
|
int total=0;
|
|
|
|
comedi_t *dev;
|
2002-03-18 22:11:32 +00:00
|
|
|
unsigned int chanlist[16];
|
2002-06-07 00:16:42 +00:00
|
|
|
unsigned int maxdata;
|
|
|
|
comedi_range *rng;
|
|
|
|
int ret;
|
2007-01-03 21:14:53 +00:00
|
|
|
struct parsed_options options;
|
2007-11-30 16:02:30 +00:00
|
|
|
int fn;
|
2000-02-02 05:14:23 +00:00
|
|
|
|
2007-01-03 21:14:53 +00:00
|
|
|
init_parsed_options(&options);
|
|
|
|
options.subdevice = -1;
|
2012-05-02 18:40:07 +01:00
|
|
|
options.n_chan = -1; /* waveform */
|
2007-01-03 21:14:53 +00:00
|
|
|
parse_options(&options, argc, argv);
|
2000-02-02 05:14:23 +00:00
|
|
|
|
2007-11-30 16:02:30 +00:00
|
|
|
/* Use n_chan to select waveform (cheat!) */
|
|
|
|
fn = options.n_chan;
|
|
|
|
if(fn < 0 || fn >= NUMFUNCS){
|
2012-05-02 18:40:07 +01:00
|
|
|
fprintf(stderr,"Use the option '-n' to select another waveform.\n");
|
2007-11-30 16:02:30 +00:00
|
|
|
fn = 0;
|
|
|
|
}
|
|
|
|
|
2002-03-18 22:11:32 +00:00
|
|
|
/* Force n_chan to be 1 */
|
2007-01-03 21:14:53 +00:00
|
|
|
options.n_chan = 1;
|
2002-03-18 22:11:32 +00:00
|
|
|
|
2007-01-03 21:14:53 +00:00
|
|
|
if(options.value){
|
|
|
|
waveform_frequency = options.value;
|
2001-06-27 19:51:11 +00:00
|
|
|
}
|
2000-02-02 05:14:23 +00:00
|
|
|
|
2007-01-03 21:14:53 +00:00
|
|
|
dev = comedi_open(options.filename);
|
2001-08-24 19:08:55 +00:00
|
|
|
if(dev == NULL){
|
2007-01-03 21:14:53 +00:00
|
|
|
fprintf(stderr, "error opening %s\n", options.filename);
|
2001-08-24 19:08:55 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2007-01-03 21:14:53 +00:00
|
|
|
if(options.subdevice < 0)
|
|
|
|
options.subdevice = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_AO, 0);
|
2000-02-02 05:14:23 +00:00
|
|
|
|
2007-11-22 18:11:22 +00:00
|
|
|
maxdata = comedi_get_maxdata(dev, options.subdevice, options.channel);
|
|
|
|
rng = comedi_get_range(dev, options.subdevice, options.channel, options.range);
|
2002-06-07 00:16:42 +00:00
|
|
|
|
2007-01-03 21:14:53 +00:00
|
|
|
offset = (double)comedi_from_phys(0.0, rng, maxdata);
|
|
|
|
amplitude = (double)comedi_from_phys(1.0, rng, maxdata) - offset;
|
2002-06-07 00:16:42 +00:00
|
|
|
|
2000-10-19 06:28:27 +00:00
|
|
|
memset(&cmd,0,sizeof(cmd));
|
2007-01-03 21:14:53 +00:00
|
|
|
cmd.subdev = options.subdevice;
|
2007-08-13 21:20:35 +00:00
|
|
|
cmd.flags = CMDF_WRITE;
|
2001-08-24 19:08:55 +00:00
|
|
|
cmd.start_src = TRIG_INT;
|
2000-10-19 06:28:27 +00:00
|
|
|
cmd.start_arg = 0;
|
|
|
|
cmd.scan_begin_src = TRIG_TIMER;
|
2007-01-03 21:14:53 +00:00
|
|
|
cmd.scan_begin_arg = 1e9 / options.freq;
|
2001-01-23 18:22:24 +00:00
|
|
|
cmd.convert_src = TRIG_NOW;
|
2000-10-19 06:28:27 +00:00
|
|
|
cmd.convert_arg = 0;
|
|
|
|
cmd.scan_end_src = TRIG_COUNT;
|
2007-01-03 21:14:53 +00:00
|
|
|
cmd.scan_end_arg = options.n_chan;
|
2000-10-19 06:28:27 +00:00
|
|
|
cmd.stop_src = TRIG_NONE;
|
|
|
|
cmd.stop_arg = 0;
|
|
|
|
|
|
|
|
cmd.chanlist = chanlist;
|
2007-01-03 21:14:53 +00:00
|
|
|
cmd.chanlist_len = options.n_chan;
|
2000-10-19 06:28:27 +00:00
|
|
|
|
2007-01-03 21:14:53 +00:00
|
|
|
chanlist[0] = CR_PACK(options.channel, options.range, options.aref);
|
2007-11-22 18:11:22 +00:00
|
|
|
//chanlist[1] = CR_PACK(options.channel + 1, options.range, options.aref);
|
2000-02-02 05:14:23 +00:00
|
|
|
|
2007-11-30 16:02:30 +00:00
|
|
|
dds_init(waveform_frequency, options.freq, fn);
|
2000-02-02 05:14:23 +00:00
|
|
|
|
2012-05-02 18:40:07 +01:00
|
|
|
if (options.verbose)
|
|
|
|
dump_cmd(stdout,&cmd);
|
2002-06-07 00:16:42 +00:00
|
|
|
|
2002-09-25 01:11:47 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-05-13 17:19:10 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2000-10-19 06:28:27 +00:00
|
|
|
if ((err = comedi_command(dev, &cmd)) < 0) {
|
2001-03-01 21:57:00 +00:00
|
|
|
comedi_perror("comedi_command");
|
2000-02-02 05:14:23 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
2001-08-24 19:08:55 +00:00
|
|
|
|
2004-01-02 00:06:33 +00:00
|
|
|
dds_output(data,BUF_LEN);
|
2004-01-02 01:16:00 +00:00
|
|
|
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);
|
2002-06-07 00:16:42 +00:00
|
|
|
}
|
2012-05-02 18:40:07 +01:00
|
|
|
if (options.verbose)
|
|
|
|
printf("m=%d\n",m);
|
2004-01-02 00:06:33 +00:00
|
|
|
|
2007-01-03 21:14:53 +00:00
|
|
|
ret = comedi_internal_trigger(dev, options.subdevice, 0);
|
|
|
|
if(ret < 0){
|
2002-06-07 00:16:42 +00:00
|
|
|
perror("comedi_internal_trigger\n");
|
|
|
|
exit(1);
|
2002-05-22 16:44:42 +00:00
|
|
|
}
|
2001-08-24 19:08:55 +00:00
|
|
|
|
2000-02-02 05:14:23 +00:00
|
|
|
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);
|
|
|
|
}
|
2012-05-02 18:40:07 +01:00
|
|
|
if (options.verbose)
|
|
|
|
printf("m=%d\n",m);
|
2000-02-02 05:14:23 +00:00
|
|
|
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;
|
|
|
|
|
2007-11-30 16:02:30 +00:00
|
|
|
void dds_init(double waveform_frequency, double update_frequency, int fn)
|
2000-02-02 05:14:23 +00:00
|
|
|
{
|
2007-01-03 21:14:53 +00:00
|
|
|
adder = waveform_frequency / update_frequency * (1 << 16) * (1 << WAVEFORM_SHIFT);
|
2000-02-02 05:14:23 +00:00
|
|
|
|
2007-11-30 16:02:30 +00:00
|
|
|
(*dds_init_function[fn])();
|
2000-02-02 05:14:23 +00:00
|
|
|
}
|
|
|
|
|
2000-10-19 06:28:27 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-30 18:20:15 +00:00
|
|
|
/* Defined for x in [0,1] */
|
|
|
|
static inline double triangle(double x)
|
|
|
|
{
|
|
|
|
return (x > 0.5) ? 1.0 - x : x;
|
|
|
|
}
|
2000-10-19 06:28:27 +00:00
|
|
|
|
2000-02-02 05:14:23 +00:00
|
|
|
void dds_init_sine(void)
|
|
|
|
{
|
|
|
|
int i;
|
2007-11-30 16:02:30 +00:00
|
|
|
double ofs = offset;
|
|
|
|
double amp = 0.5 * amplitude;
|
2000-02-02 05:14:23 +00:00
|
|
|
|
2007-11-30 16:02:30 +00:00
|
|
|
if(ofs < amp){
|
|
|
|
/* Probably a unipolar range. Bump up the offset. */
|
|
|
|
ofs = amp;
|
|
|
|
}
|
2000-02-02 05:14:23 +00:00
|
|
|
for(i=0;i<WAVEFORM_LEN;i++){
|
2007-11-30 16:02:30 +00:00
|
|
|
waveform[i]=rint(ofs+amp*cos(i*2*M_PI/WAVEFORM_LEN));
|
2000-02-02 05:14:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Yes, I know this is not the proper equation for a
|
|
|
|
cycloid. Fix it. */
|
2000-10-19 06:28:27 +00:00
|
|
|
void dds_init_pseudocycloid(void)
|
2000-02-02 05:14:23 +00:00
|
|
|
{
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-30 16:02:30 +00:00
|
|
|
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)
|
2000-02-02 05:14:23 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for(i=0;i<WAVEFORM_LEN;i++){
|
|
|
|
waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-30 16:02:30 +00:00
|
|
|
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;
|
|
|
|
|
2007-11-30 18:20:15 +00:00
|
|
|
for (i = 0; i < WAVEFORM_LEN; i++) {
|
|
|
|
waveform[i] = rint(offset + amplitude * 2 * triangle((double)i / WAVEFORM_LEN));
|
2007-11-30 16:02:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-30 18:20:15 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|