scope_rt.c
1 /** This file, 'halscope_rt.c', is a HAL component that together with 2 'halscope.c' provides an oscilloscope to view HAL pins, signals, 3 and parameters 4 */ 5 6 /** Copyright (C) 2003 John Kasunich 7 <jmkasunich AT users DOT sourceforge DOT net> 8 */ 9 10 /** This program is free software; you can redistribute it and/or 11 modify it under the terms of version 2 of the GNU General 12 Public License as published by the Free Software Foundation. 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 22 THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR 23 ANY HARM OR LOSS RESULTING FROM ITS USE. IT IS _EXTREMELY_ UNWISE 24 TO RELY ON SOFTWARE ALONE FOR SAFETY. Any machinery capable of 25 harming persons must have provisions for completely removing power 26 from all motors, etc, before persons enter any danger area. All 27 machinery must be designed to comply with local and national safety 28 codes, and the authors of this software can not, and do not, take 29 any responsibility for such compliance. 30 31 This code was written as part of the EMC HAL project. For more 32 information, go to www.linuxcnc.org. 33 */ 34 35 #include <rtapi.h> /* RTAPI realtime OS API */ 36 #include <rtapi_app.h> /* RTAPI realtime module decls */ 37 #include <hal.h> /* HAL public API decls */ 38 #include "../hal_priv.h" /* HAL private API decls */ 39 #include "scope_rt.h" /* scope related declarations */ 40 #include "rtapi_string.h" 41 42 /* module information */ 43 MODULE_AUTHOR("John Kasunich"); 44 MODULE_DESCRIPTION("Oscilloscope for EMC HAL"); 45 MODULE_LICENSE("GPL"); 46 47 long num_samples = 16000; 48 long shm_size; 49 RTAPI_MP_LONG(num_samples, "Number of samples in the shared memory block") 50 51 /*********************************************************************** 52 * GLOBAL VARIABLES * 53 ************************************************************************/ 54 55 scope_rt_control_t *ctrl_rt; /* ptr to main RT control structure */ 56 scope_shm_control_t *ctrl_shm; /* ptr to shared mem control struct */ 57 58 /*********************************************************************** 59 * LOCAL VARIABLES * 60 ************************************************************************/ 61 62 static int comp_id; /* component ID */ 63 static int shm_id; /* shared memory ID */ 64 static scope_rt_control_t ctrl_struct; /* realtime control structure */ 65 66 /*********************************************************************** 67 * LOCAL FUNCTION DECLARATIONS * 68 ************************************************************************/ 69 70 static void init_rt_control_struct(void *shmem); 71 static void init_shm_control_struct(void); 72 73 static void sample(void *arg, long period); 74 static void capture_sample(void); 75 static int check_trigger(void); 76 77 /*********************************************************************** 78 * INIT AND EXIT CODE * 79 ************************************************************************/ 80 81 int rtapi_app_main(void) 82 { 83 int retval; 84 void *shm_base; 85 long skip; 86 /* connect to the HAL */ 87 comp_id = hal_init("scope_rt"); 88 if (comp_id < 0) { 89 rtapi_print_msg(RTAPI_MSG_ERR, "SCOPE: ERROR: hal_init() failed\n"); 90 return -1; 91 } 92 /* connect to scope shared memory block */ 93 skip = (sizeof(scope_shm_control_t) + 3) & ~3; 94 shm_size = skip + num_samples * sizeof(scope_data_t); 95 shm_id = rtapi_shmem_new(SCOPE_SHM_KEY, comp_id, shm_size); 96 if (shm_id < 0) { 97 rtapi_print_msg(RTAPI_MSG_ERR, 98 "SCOPE RT: ERROR: failed to get shared memory (key=0x%x, size=%lu)\n", 99 SCOPE_SHM_KEY, 100 shm_size 101 ); 102 hal_exit(comp_id); 103 return -1; 104 } 105 retval = rtapi_shmem_getptr(shm_id, &shm_base); 106 if (retval < 0) { 107 rtapi_print_msg(RTAPI_MSG_ERR, 108 "SCOPE: ERROR: failed to map shared memory\n"); 109 rtapi_shmem_delete(shm_id, comp_id); 110 hal_exit(comp_id); 111 return -1; 112 } 113 114 /* init control structure */ 115 ctrl_rt = &ctrl_struct; 116 init_rt_control_struct(shm_base); 117 118 /* export scope data sampling function */ 119 retval = hal_export_funct("scope.sample", sample, NULL, 0, 0, comp_id); 120 if (retval != 0) { 121 rtapi_print_msg(RTAPI_MSG_ERR, 122 "SCOPE_RT: ERROR: sample funct export failed\n"); 123 hal_exit(comp_id); 124 return -1; 125 } 126 rtapi_print_msg(RTAPI_MSG_DBG, "SCOPE_RT: installed sample function\n"); 127 hal_ready(comp_id); 128 return 0; 129 } 130 131 void rtapi_app_exit(void) 132 { 133 /* is sample function linked to a thread? */ 134 if (ctrl_shm->thread_name[0] != '\0') { 135 /* need to unlink it before we release the scope shared memory */ 136 hal_del_funct_from_thread("scope.sample", ctrl_shm->thread_name); 137 } 138 rtapi_shmem_delete(shm_id, comp_id); 139 hal_exit(comp_id); 140 } 141 142 /*********************************************************************** 143 * REALTIME FUNCTIONS * 144 ************************************************************************/ 145 146 static void sample(void *arg, long period) 147 { 148 int n; 149 150 ctrl_shm->watchdog = 0; 151 if (ctrl_shm->state == RESET) { 152 /* sampling interrupted, reset everything */ 153 ctrl_shm->curr = 0; 154 ctrl_shm->start = ctrl_shm->curr; 155 ctrl_shm->samples = 0; 156 ctrl_shm->force_trig = 0; 157 /* reset completed, set new state */ 158 ctrl_shm->state = IDLE; 159 } 160 ctrl_rt->mult_cntr++; 161 if (ctrl_rt->mult_cntr < ctrl_shm->mult) { 162 /* not time to do anything yet */ 163 return; 164 } 165 /* reset counter */ 166 ctrl_rt->mult_cntr = 0; 167 /* run the sampling state machine */ 168 switch (ctrl_shm->state) { 169 case IDLE: 170 /* do nothing while waiting for INIT */ 171 break; 172 case INIT: 173 /* init start pointer, curr pointer, sample count */ 174 ctrl_shm->curr = 0; 175 ctrl_shm->start = ctrl_shm->curr; 176 ctrl_shm->samples = 0; 177 ctrl_shm->force_trig = 0; 178 ctrl_rt->auto_timer = 0; 179 /* get info about channels */ 180 for (n = 0; n < 16; n++) { 181 ctrl_rt->data_addr[n] = SHMPTR(ctrl_shm->data_offset[n]); 182 ctrl_rt->data_type[n] = ctrl_shm->data_type[n]; 183 ctrl_rt->data_len[n] = ctrl_shm->data_len[n]; 184 } 185 /* set next state */ 186 ctrl_shm->state = PRE_TRIG; 187 break; 188 case PRE_TRIG: 189 /* acquire a sample */ 190 capture_sample(); 191 /* increment sample counter */ 192 ctrl_shm->samples++; 193 /* check if all pre-trigger samples captured */ 194 if (ctrl_shm->samples >= ctrl_shm->pre_trig) { 195 /* yes - start waiting for trigger */ 196 ctrl_shm->state = TRIG_WAIT; 197 /* dummy call to preset 'compare_result' */ 198 check_trigger(); 199 } 200 break; 201 case TRIG_WAIT: 202 /* acquire a sample */ 203 capture_sample(); 204 /* increment sample counter */ 205 ctrl_shm->samples++; 206 /* check if trigger condition met */ 207 if (check_trigger()) { 208 /* yes - start acquiring post trigger data */ 209 ctrl_shm->state = POST_TRIG; 210 } else { 211 /* no, discard oldest pre-trig sample */ 212 ctrl_shm->samples--; 213 ctrl_shm->start += ctrl_shm->sample_len; 214 /* is there a valid sample here, or end of buffer? */ 215 if ((ctrl_shm->start + ctrl_shm->sample_len) > ctrl_shm->buf_len) { 216 /* end of buffer, wrap back to beginning */ 217 ctrl_shm->start = 0; 218 } 219 } 220 break; 221 case POST_TRIG: 222 /* acquire a sample */ 223 capture_sample(); 224 /* increment sample counter */ 225 ctrl_shm->samples++; 226 /* check if all post-trigger samples captured */ 227 if (ctrl_shm->samples >= ctrl_shm->rec_len) { 228 /* yes - stop sampling and cleanup */ 229 ctrl_shm->state = DONE; 230 } 231 break; 232 case DONE: 233 /* do nothing while GUI displays waveform */ 234 break; 235 default: 236 /* shouldn't get here - if we do, set a legal state */ 237 ctrl_shm->state = IDLE; 238 break; 239 } 240 /* done */ 241 } 242 243 static void capture_sample(void) 244 { 245 scope_data_t *dest; 246 int n; 247 248 dest = &(ctrl_rt->buffer[ctrl_shm->curr]); 249 /* loop through all channels to acquire data */ 250 for (n = 0; n < 16; n++) { 251 /* capture 1, 2, or 4 bytes, based on data size */ 252 switch (ctrl_rt->data_len[n]) { 253 case 1: 254 dest->d_u8 = *((unsigned char *) (ctrl_rt->data_addr[n])); 255 dest++; 256 break; 257 case 4: 258 dest->d_u32 = *((unsigned long *) (ctrl_rt->data_addr[n])); 259 dest++; 260 break; 261 case 8: 262 { 263 ireal_t sample_a, sample_b; 264 do { 265 sample_a = *((volatile ireal_t *) (ctrl_rt->data_addr[n])); 266 sample_b = *((volatile ireal_t *) (ctrl_rt->data_addr[n])); 267 } while( sample_a != sample_b ); 268 dest->d_ireal = sample_a; 269 dest++; 270 } 271 break; 272 default: 273 break; 274 } 275 } 276 /* increment sample pointer */ 277 ctrl_shm->curr += ctrl_shm->sample_len; 278 /* is there room in the buffer for another sample? */ 279 if ((ctrl_shm->curr + ctrl_shm->sample_len) > ctrl_shm->buf_len) { 280 /* no, wrap back to beginning of buffer */ 281 ctrl_shm->curr = 0; 282 } 283 } 284 285 // TODO: type-independent way to get high bit 286 // #define SIGN_BIT (~(((ireal_t)~(ireal_t)0)>>1)) 287 static int check_trigger(void) 288 { 289 static int compare_result = 0; 290 int prev_compare_result; 291 scope_data_t *value, *level; 292 293 /* has user forced trigger? */ 294 if (ctrl_shm->force_trig != 0) { 295 return 1; 296 } 297 /* is auto trigger enabled? */ 298 if (ctrl_shm->auto_trig != 0) { 299 /* yes, has the delay time expired? */ 300 if (++ctrl_rt->auto_timer >= ctrl_shm->rec_len) { 301 return 1; 302 } 303 } else { 304 /* no auto, reset delay timer */ 305 ctrl_rt->auto_timer = 0; 306 } 307 /* if no trigger channel is selected we're done */ 308 if (ctrl_shm->trig_chan == 0) { 309 return 0; 310 } 311 /* point a scope_data_t union at the signal value */ 312 value = ctrl_rt->data_addr[ctrl_shm->trig_chan - 1]; 313 /* and at the trigger level */ 314 level = &(ctrl_shm->trig_level); 315 /* save previous compare result */ 316 prev_compare_result = compare_result; 317 /* compare actual value to trigger level */ 318 switch (ctrl_rt->data_type[ctrl_shm->trig_chan - 1]) { 319 case HAL_BIT: 320 /* for bits, we don't even look at the trigger level */ 321 compare_result = value->d_u8; 322 break; 323 case HAL_FLOAT: 324 { 325 ireal_t tmp1, tmp2; 326 /* don't want to use the FPU in this function, so we use */ 327 /* a hack - see http://en.wikipedia.org/wiki/IEEE_754 */ 328 /* this _only_ works with IEEE-754 floating point numbers */ 329 /* and will probably fail for infinities, NANs, etc. */ 330 /* OK, here we go! */ 331 /* get the value as an integer */ 332 tmp1 = value->d_ireal; 333 /* get the trigger as an integer */ 334 tmp2 = level->d_ireal; 335 /* is the value negative? (highest bit) */ 336 if (tmp1 & 0x8000000000000000ull) { 337 /* yes, is the trigger level negative? */ 338 if (tmp2 & 0x8000000000000000ull) { 339 /* yes, make both positive */ 340 tmp1 ^= 0x8000000000000000ull; 341 tmp2 ^= 0x8000000000000000ull; 342 /* and compare them as unsigned ints */ 343 /* because of negation, we reverse the compare */ 344 compare_result = (tmp1 < tmp2); 345 } else { 346 /* trigger level positive, value negative */ 347 compare_result = 0; 348 } 349 } else { 350 /* value is positive, is trigger level negative? */ 351 if (tmp2 & 0x8000000000000000ull) { 352 /* trigger level negative, value positive */ 353 compare_result = 1; 354 } else { 355 /* both are positive */ 356 /* compare them as unsigned ints */ 357 compare_result = (tmp1 > tmp2); 358 } 359 } 360 } 361 break; 362 case HAL_S32: 363 compare_result = (value->d_s32 > level->d_s32); 364 break; 365 case HAL_U32: 366 compare_result = (value->d_u32 > level->d_u32); 367 break; 368 default: 369 compare_result = 0; 370 break; 371 } 372 /* test for rising edge */ 373 if (ctrl_shm->trig_edge && compare_result && !prev_compare_result) { 374 return 1; 375 } 376 /* test for falling edge */ 377 if (!ctrl_shm->trig_edge && !compare_result && prev_compare_result) { 378 return 1; 379 } 380 return 0; 381 } 382 383 /*********************************************************************** 384 * LOCAL FUNCTION DEFINITIONS * 385 ************************************************************************/ 386 387 static void init_rt_control_struct(void *shmem) 388 { 389 char *cp; 390 int n, skip; 391 392 /* first clear entire struct to all zeros */ 393 cp = (char *) ctrl_rt; 394 for (n = 0; n < sizeof(scope_rt_control_t); n++) { 395 cp[n] = 0; 396 } 397 /* save pointer to shared control structure */ 398 ctrl_shm = shmem; 399 /* round size of shared struct up to a multiple of 4 for alignment */ 400 skip = (sizeof(scope_shm_control_t) + 3) & ~3; 401 /* the rest of the shared memory area is the data buffer */ 402 ctrl_rt->buffer = (scope_data_t *) (((char *) (shmem)) + skip); 403 init_shm_control_struct(); 404 /* init any non-zero fields */ 405 406 /* done */ 407 } 408 409 static void init_shm_control_struct(void) 410 { 411 char *cp; 412 int skip, n; 413 414 /* first clear entire struct to all zeros */ 415 cp = (char *) ctrl_shm; 416 for (n = 0; n < sizeof(scope_shm_control_t); n++) { 417 cp[n] = 0; 418 } 419 /* round size of shared struct up to a multiple of 4 for alignment */ 420 ctrl_shm->shm_size = shm_size; 421 skip = (sizeof(scope_shm_control_t) + 3) & ~3; 422 /* remainder of shmem area is buffer */ 423 ctrl_shm->buf_len = (shm_size - skip) / sizeof(scope_data_t); 424 /* init any non-zero fields */ 425 ctrl_shm->mult = 1; 426 ctrl_shm->state = IDLE; 427 }