siggen.c
1 /******************************************************************** 2 * Description: siggen.c 3 * This file, 'siggen.c', is a HAL component that 4 * generates square, triangle, sine, cosine, and 5 * sawtooth waves plus a clock signal. 6 * 7 * Author: John Kasunich 8 * License: GPL Version 2 9 * 10 * Copyright (c) 2003 All rights reserved. 11 * 12 * Last change: 17Nov2010 - Matt Shaver added the "clock" output pin. 13 ********************************************************************/ 14 /** This file, 'siggen.c', is a HAL component that generates square, 15 triangle, sine, cosine, and sawtooth waves. I expect that it 16 will mostly be used for testing. It is a realtime component. 17 18 It supports any number of signal generators, as set by the 19 insmod parameter 'num_chan'. Alternatively,use the names= specifier 20 and a list of unique names separated by commas. The names= and 21 num_chan= specifiers are mututally exclusive. 22 23 Each generator has a number of pins and parameters, whose 24 names begin with 'siggen.x.', where 'x' is the generator number. 25 Generator numbers start at zero. 26 27 Each generator is controlled by three pins. 'frequency' sets 28 the frequency in Hertz. 'amplitude' sets the peak amplitude, 29 and 'offset' sets the DC offset. For example, if 'amplitude' 30 is 1.0 and 'offset' is 0.0, the outputs will swing from -1.0 31 to +1.0. If 'amplitude' is 2.5 and 'offset' is 10.0, then 32 the outputs will swing from 7.5 to 12.5. 33 34 There are six output pins: 'square', 'triangle', 'sine', 'cosine', 35 'clock', and 'sawtooth'. All six run at the same frequency, 36 amplitude, and offset. 37 38 This component exports one function per signal generator, 39 called 'siggen.x.update'. It is a floating point function. 40 41 */ 42 43 /** Copyright (C) 2003 John Kasunich 44 <jmkasunich AT users DOT sourceforge DOT net> 45 */ 46 47 /** This program is free software; you can redistribute it and/or 48 modify it under the terms of version 2 of the GNU General 49 Public License as published by the Free Software Foundation. 50 This library is distributed in the hope that it will be useful, 51 but WITHOUT ANY WARRANTY; without even the implied warranty of 52 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 53 GNU General Public License for more details. 54 55 You should have received a copy of the GNU General Public 56 License along with this library; if not, write to the Free Software 57 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 58 59 THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR 60 ANY HARM OR LOSS RESULTING FROM ITS USE. IT IS _EXTREMELY_ UNWISE 61 TO RELY ON SOFTWARE ALONE FOR SAFETY. Any machinery capable of 62 harming persons must have provisions for completely removing power 63 from all motors, etc, before persons enter any danger area. All 64 machinery must be designed to comply with local and national safety 65 codes, and the authors of this software can not, and do not, take 66 any responsibility for such compliance. 67 68 This code was written as part of the EMC HAL project. For more 69 information, go to www.linuxcnc.org. 70 */ 71 72 #include "rtapi.h" /* RTAPI realtime OS API */ 73 #include "rtapi_app.h" /* RTAPI realtime module decls */ 74 #include "hal.h" /* HAL public API decls */ 75 #include <float.h> 76 #include <rtapi_math.h> 77 #include <rtapi_string.h> 78 79 /* module information */ 80 MODULE_AUTHOR("John Kasunich"); 81 MODULE_DESCRIPTION("Signal Generator Component for EMC HAL"); 82 MODULE_LICENSE("GPL"); 83 static int num_chan; /* number of channels */ 84 static int default_num_chan = 1; 85 static int howmany; 86 RTAPI_MP_INT(num_chan, "number of channels"); 87 88 #define MAX_CHAN 16 89 static char *names[MAX_CHAN] = {0,}; 90 RTAPI_MP_ARRAY_STRING(names, MAX_CHAN, "names of siggen"); 91 92 /*********************************************************************** 93 * STRUCTURES AND GLOBAL VARIABLES * 94 ************************************************************************/ 95 96 /** This structure contains the runtime data for a single siggen. 97 */ 98 99 typedef struct { 100 hal_float_t *square; /* pin: output */ 101 hal_float_t *sawtooth; /* pin: output */ 102 hal_float_t *triangle; /* pin: output */ 103 hal_float_t *sine; /* pin: output */ 104 hal_float_t *cosine; /* pin: output */ 105 hal_bit_t *clock; /* pin: output */ 106 hal_float_t *frequency; /* pin: frequency */ 107 hal_float_t *amplitude; /* pin: amplitude */ 108 hal_float_t *offset; /* pin: offset */ 109 hal_bit_t *reset; /* pin: reset */ 110 double index; /* position within output cycle */ 111 } hal_siggen_t; 112 113 /* pointer to array of siggen_t structs in shared memory, 1 per gen */ 114 static hal_siggen_t *siggen_array; 115 116 /* other globals */ 117 static int comp_id; /* component ID */ 118 119 /*********************************************************************** 120 * LOCAL FUNCTION DECLARATIONS * 121 ************************************************************************/ 122 123 static int export_siggen(int num, hal_siggen_t * addr,char* prefix); 124 static void calc_siggen(void *arg, long period); 125 126 /*********************************************************************** 127 * INIT AND EXIT CODE * 128 ************************************************************************/ 129 130 131 int rtapi_app_main(void) 132 { 133 int n, retval, i; 134 135 if(num_chan && names[0]) { 136 rtapi_print_msg(RTAPI_MSG_ERR,"num_chan= and names= are mutually exclusive\n"); 137 return -EINVAL; 138 } 139 if(!num_chan && !names[0]) num_chan = default_num_chan; 140 141 if(num_chan) { 142 howmany = num_chan; 143 } else { 144 howmany = 0; 145 for (i = 0; i < MAX_CHAN; i++) { 146 if ( (names[i] == NULL) || (*names[i] == 0) ){ 147 break; 148 } 149 howmany = i + 1; 150 } 151 } 152 153 /* test for number of channels */ 154 if ((howmany <= 0) || (howmany > MAX_CHAN)) { 155 rtapi_print_msg(RTAPI_MSG_ERR, 156 "SIGGEN: ERROR: invalid number of channels: %d\n", howmany); 157 return -1; 158 } 159 /* have good config info, connect to the HAL */ 160 comp_id = hal_init("siggen"); 161 if (comp_id < 0) { 162 rtapi_print_msg(RTAPI_MSG_ERR, "SIGGEN: ERROR: hal_init() failed\n"); 163 return -1; 164 } 165 /* allocate shared memory for siggen data */ 166 siggen_array = hal_malloc(howmany * sizeof(hal_siggen_t)); 167 if (siggen_array == 0) { 168 rtapi_print_msg(RTAPI_MSG_ERR, 169 "SIGGEN: ERROR: hal_malloc() failed\n"); 170 hal_exit(comp_id); 171 return -1; 172 } 173 /* export variables and functions for each siggen */ 174 i = 0; // for names= items 175 for (n = 0; n < howmany; n++) { 176 /* export everything for this loop */ 177 if(num_chan) { 178 char buf[HAL_NAME_LEN + 1]; 179 rtapi_snprintf(buf, sizeof(buf), "siggen.%d", n); 180 retval = export_siggen(n, &(siggen_array[n]),buf); 181 } else { 182 retval = export_siggen(n, &(siggen_array[n]),names[i++]); 183 } 184 185 if (retval != 0) { 186 rtapi_print_msg(RTAPI_MSG_ERR, 187 "SIGGEN: ERROR: siggen %d var export failed\n", n); 188 hal_exit(comp_id); 189 return -1; 190 } 191 } 192 rtapi_print_msg(RTAPI_MSG_INFO, 193 "SIGGEN: installed %d signal generators\n", howmany); 194 hal_ready(comp_id); 195 return 0; 196 } 197 198 void rtapi_app_exit(void) 199 { 200 hal_exit(comp_id); 201 } 202 203 /*********************************************************************** 204 * REALTIME LOOP CALCULATIONS * 205 ************************************************************************/ 206 207 static void calc_siggen(void *arg, long period) 208 { 209 hal_siggen_t *siggen; 210 double tmp1, tmp2; 211 212 /* point to the data for this signal generator */ 213 siggen = arg; 214 /* calculate the time since last execution */ 215 tmp1 = period * 0.000000001; 216 217 /* calculate how much of an output cycle that has passed */ 218 tmp2 = *(siggen->frequency) * tmp1; 219 /* limit frequency to comply with Nyquist limit */ 220 if ( tmp2 > 0.5 ) { 221 *(siggen->frequency) = 0.5 / tmp1; 222 tmp2 = 0.5; 223 } 224 /* index ramps from 0.0 to 0.99999 for each output cycle */ 225 if ( *(siggen->reset) ) { 226 siggen->index = 0.5; 227 } else { 228 siggen->index += tmp2; 229 } 230 /* wrap index if it is >= 1.0 */ 231 if ( siggen->index >= 1.0 ) { 232 siggen->index -= 1.0; 233 } 234 235 /* generate the square wave and clock output */ 236 /* tmp1 steps from -1.0 to +1.0 when index passes 0.5 */ 237 if ( siggen->index > 0.5 ) { 238 tmp1 = 1.0; 239 *(siggen->clock) = 1; 240 } else { 241 tmp1 = -1.0; 242 *(siggen->clock) = 0; 243 } 244 /* apply scaling and offset, and write to output */ 245 *(siggen->square) = (tmp1 * *(siggen->amplitude)) + *(siggen->offset); 246 247 /* generate the sawtooth wave output */ 248 /* tmp2 ramps from -1.0 to +1.0 as index goes from 0 to 1 */ 249 tmp2 = (siggen->index * 2.0) - 1.0; 250 /* apply scaling and offset, and write to output */ 251 *(siggen->sawtooth) = (tmp2 * *(siggen->amplitude)) + *(siggen->offset); 252 253 /* generate the triangle wave output */ 254 /* tmp2 ramps from -2.0 to +2.0 as index goes from 0 to 1 */ 255 tmp2 *= 2.0; 256 /* flip first half of ramp, now goes from +1 to -1 to +1 */ 257 tmp2 = (tmp2 * tmp1) - 1.0; 258 /* apply scaling and offset, and write to output */ 259 *(siggen->triangle) = (tmp2 * *(siggen->amplitude)) + *(siggen->offset); 260 261 /* generate the sine wave output */ 262 /* tmp1 is angle in radians */ 263 tmp1 = siggen->index * (2.0 * 3.1415927); 264 /* get sine, apply scaling and offset, and write to output */ 265 *(siggen->sine) = (sin(tmp1) * *(siggen->amplitude)) + *(siggen->offset); 266 267 /* generate the cosine wave output */ 268 /* get cosine, apply scaling and offset, and write to output */ 269 *(siggen->cosine) = (cos(tmp1) * *(siggen->amplitude)) + *(siggen->offset); 270 /* done */ 271 } 272 273 /*********************************************************************** 274 * LOCAL FUNCTION DEFINITIONS * 275 ************************************************************************/ 276 277 static int export_siggen(int num, hal_siggen_t * addr,char* prefix) 278 { 279 int retval; 280 char buf[HAL_NAME_LEN + 1]; 281 282 /* export pins */ 283 retval = hal_pin_float_newf(HAL_OUT, &(addr->square), comp_id, 284 "%s.square", prefix); 285 if (retval != 0) { 286 return retval; 287 } 288 retval = hal_pin_float_newf(HAL_OUT, &(addr->sawtooth), comp_id, 289 "%s.sawtooth", prefix); 290 if (retval != 0) { 291 return retval; 292 } 293 retval = hal_pin_float_newf(HAL_OUT, &(addr->triangle), comp_id, 294 "%s.triangle", prefix); 295 if (retval != 0) { 296 return retval; 297 } 298 retval = hal_pin_float_newf(HAL_OUT, &(addr->sine), comp_id, 299 "%s.sine", prefix); 300 if (retval != 0) { 301 return retval; 302 } 303 retval = hal_pin_float_newf(HAL_OUT, &(addr->cosine), comp_id, 304 "%s.cosine", prefix); 305 if (retval != 0) { 306 return retval; 307 } 308 retval = hal_pin_bit_newf(HAL_OUT, &(addr->clock), comp_id, 309 "%s.clock", prefix); 310 if (retval != 0) { 311 return retval; 312 } 313 retval = hal_pin_float_newf(HAL_IN, &(addr->frequency), comp_id, 314 "%s.frequency", prefix); 315 if (retval != 0) { 316 return retval; 317 } 318 retval = hal_pin_float_newf(HAL_IN, &(addr->amplitude), comp_id, 319 "%s.amplitude", prefix); 320 if (retval != 0) { 321 return retval; 322 } 323 retval = hal_pin_float_newf(HAL_IN, &(addr->offset), comp_id, 324 "%s.offset", prefix); 325 if (retval != 0) { 326 return retval; 327 } 328 retval = hal_pin_bit_newf(HAL_IN, &(addr->reset), comp_id, 329 "%s.reset", prefix); 330 if (retval != 0) { 331 return retval; 332 } 333 /* init all structure members */ 334 *(addr->square) = 0.0; 335 *(addr->sawtooth) = 0.0; 336 *(addr->triangle) = 0.0; 337 *(addr->sine) = 0.0; 338 *(addr->cosine) = 0.0; 339 *(addr->clock) = 0; 340 *(addr->frequency) = 1.0; 341 *(addr->amplitude) = 1.0; 342 *(addr->offset) = 0.0; 343 addr->index = 0.0; 344 /* export function for this loop */ 345 rtapi_snprintf(buf, sizeof(buf), "%s.update", prefix); 346 retval = 347 hal_export_funct(buf, calc_siggen, &(siggen_array[num]), 1, 0, 348 comp_id); 349 if (retval != 0) { 350 rtapi_print_msg(RTAPI_MSG_ERR, 351 "SIGGEN: ERROR: update funct export failed\n"); 352 hal_exit(comp_id); 353 return -1; 354 } 355 return 0; 356 }