/*************************************************************************** counter.c - description */ /*!@defgroup count counter - counter.c The counter functions are demostrated through a ncurses form that displays the counters,trigger,flags and zeros. Most of it's fields are dynamically updateable using the arrow keys. This module is designed to showcase the flexiblity of the counters and for testing their functionality. For a good ncurses/forms tutorial try http://en.tldp.org/HOWTO/NCURSES-Programming-HOWTO/ */ /*************************************************************************** ------------------- begin : Tue Dec 30 2003 copyright : (C) 2003 by NZG email : ngustavson@emacinc.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define COLOR_FIELDS uncomment this to display the fields in color #define COLOR_FORE COLOR_BLACK #define COLOR_BACK COLOR_GREEN /*!standard EMAC macro for main arguement number check*/ #define ARGCHECK(num){if(argc<=num){printf("invalid number of arguements\n");help();return -1;}} /*!standard EMAC macro for opening a linux device*/ #define OPENDEV(filed,dev){if((filed = open(dev, O_RDWR))<=0){printf("couldn't open %s\n",dev);return -1;}} /*here are some graphics locations*/ /*!y location of the first counter field*/ #define YSTART 4 /*!y location of the first field header*/ #define HEADER_Y YSTART-1 /*!distance between the fields*/ #define FIELDSEP 7 /*!number of fields*/ #define FIELDNUM 6 /*!field definitions*/ enum FIELDNUMBERS {COUNT,RELOAD,TRIG,FLAG,ZERO,FREQ}; /*field locations*/ #define NUM_X 3 + FIELDSEP #define COUNT_X NUM_X + FIELDSEP #define RELOAD_X COUNT_X + FIELDSEP #define TRIG_X RELOAD_X + FIELDSEP #define FLAG_X TRIG_X + FIELDSEP #define ZERO_X FLAG_X + FIELDSEP #define FREQ_X ZERO_X + FIELDSEP #define FIRST_FIELD COUNT_X /*!array index calculation based on counter number and field*/ #define ARRAYCALC (channel*FIELDNUM + fieldnum) //array element number calculation /*coordinates for extra messages*/ #define MESSAGE_Y (YSTART+COUNTNUM + 2) #define MESSAGE_X 0 /*global variables for saving the location of the cursor*/ int current_y,current_x; #define CURSEMESSAGE(fmt, args...) {getsyx(current_y,current_x);mvprintw(MESSAGE_Y,MESSAGE_X,fmt, ## args);setsyx(current_y,current_x);} /*!modified flag, used to indicate that a field has been modified, if it hasn't then no commands are sent to the 37e12*/ volatile int modified; /*!global field array, for easier pointer math*/ FIELD *counter_field[COUNTNUM*FIELDNUM+1];//+1 required for null terminated list /*!global form array*/ FORM *counter_form; /*!global fd, easy way to pass to the field hook*/ int global_fd; /*!global Cop_Data pointer, easy way to pass to the field hook*/ Cop_Data *Cop; /*!trigger strings for the trigger field, these are in order of the trigger bits in counter.h*/ const char *trig_strings[] = {" ","rise ","fall ","any ",NULL}; /*!flag strings for the flags field hese are in order of the flag bits in counter.h*/ const char *flag_strings[] = {" ","__N ","_R_ ","_RN ","S__ ","S_N ","SR_ ","SRN ",NULL}; /*!Get the index from the string in the field This function returns the array index of the string, Ncurses doesn't seem to store this information. I tried just comparing the pointers, but, contrary to the documentation, they do not appear to be the same, the strings seem to get copied. @param enumptr pointer to the string @param fieldptr pointer to the array of strings that enumptr is an element of */ static int indexenum(const char *enumptr, const char **fieldptr) { int index; for(index=0;index<10;index++) { if(!(strcmp(enumptr,fieldptr[index]))) return index; } return 0; } /*Destroy the form destroys the global forms created at initialization */ static void destroyform() { int channel,fieldnum; FORM *form = counter_form; unpost_form(form); free_form(form); for(channel=0;channelcounter_config[counter]; int trigger,threshold,flags; if(!modified)//no change to this field, just exit return; switch(fieldnum) { case COUNT: count = atoi(dataptr); E12_CounterLoad(fd, counter,count, Cop); break; //change to the reload(threshold) value, call E12_Count config, pass other variables from the shadow case RELOAD: threshold = atoi(dataptr); E12_CountConfig(fd, counter, oldconfig->trigger, threshold,oldconfig->flags,Cop); break; //change the trigger value, this program starts and stops the counter based on whether the trigger is enabled case TRIG: trigger = indexenum(dataptr, trig_strings); E12_CountConfig(fd, counter, trigger, oldconfig->threshold,oldconfig->flags,Cop); if(trigger) E12_CountCommand(fd,counter, ICSTART); else E12_CountCommand(fd, counter,ICSTOP); CURSEMESSAGE("trigger changed to %u\n",trigger); break; //change to the flag value, call E12_Count config, pass other variables from the shadow case FLAG: flags = indexenum(dataptr, flag_strings); E12_CountConfig(fd, counter, oldconfig->trigger, oldconfig->threshold,flags,Cop); CURSEMESSAGE("flags changed to %u\n",flags); break; } modified=0; //field has been updated } /*Initialize the Form This function sets up the form that is used to control the counters */ static int InitForm() { int channel,fieldnum; /* Initialize curses */ initscr(); start_color(); cbreak(); noecho(); keypad(stdscr, TRUE); //halfdelay(1); nodelay(stdscr, TRUE); modified=0; //flag indicating a change has happened to a field, and a commit change is pending. /* Initialize the fields */ init_pair(1, COLOR_FORE, COLOR_BACK); init_pair(2, COLOR_FORE, COLOR_BACK); for(channel=0;channel<(COUNTNUM);channel++) { for(fieldnum=0;fieldnum<(FIELDNUM);fieldnum++) { counter_field[ARRAYCALC] = new_field(1, FIELDSEP, YSTART+channel, FIRST_FIELD+(fieldnum*FIELDSEP), 0, 0); /* Set field options */ #ifdef COLOR_FIELDS set_field_fore(counter_field[ARRAYCALC], COLOR_PAIR(1)); set_field_back(counter_field[ARRAYCALC], COLOR_PAIR(2)); #endif set_field_back(counter_field[ARRAYCALC], A_UNDERLINE); /* Print a line for the option */ field_opts_off(counter_field[ARRAYCALC], O_AUTOSKIP); /* Don't go to next field when this */ switch(fieldnum) { case COUNT: set_field_type(counter_field[ARRAYCALC], /* field to alter */ TYPE_INTEGER, /* type to associate */ 0, /* # places to zero-pad to */ 0, 65535); /* valid range */ break; case RELOAD: set_field_type(counter_field[ARRAYCALC], /* field to alter */ TYPE_INTEGER, /* type to associate */ 0, /* # places to zero-pad to */ 0, 65535); /* valid range */ break; case TRIG: set_field_type(counter_field[ARRAYCALC], /* field to alter */ TYPE_ENUM, /* type to associate */ trig_strings, /* # valid TRIG strings*/ 0, 0); break; case FLAG: set_field_type(counter_field[ARRAYCALC], /* field to alter */ TYPE_ENUM, /* type to associate */ flag_strings, /* # valid FLAG strings */ 0, 0); break; } } } counter_field[COUNTNUM*FIELDNUM+1] = NULL; /* Create the form and post it */ counter_form = new_form(counter_field); set_field_term(counter_form, field_commit_change); post_form(counter_form); refresh(); mvprintw(HEADER_Y, NUM_X, "count"); mvprintw(HEADER_Y, COUNT_X, "value"); mvprintw(HEADER_Y, RELOAD_X, "load "); mvprintw(HEADER_Y, TRIG_X, "trig "); mvprintw(HEADER_Y, FLAG_X, "flags"); mvprintw(HEADER_Y, ZERO_X, "zeros"); mvprintw(HEADER_Y, FREQ_X, "freq "); for(channel=0;channel<(COUNTNUM);channel++) mvprintw(YSTART+channel, NUM_X+2, "%u",channel); refresh(); set_current_field(counter_form, counter_field[0]); /* Set focus to the colored field */ return 0; } /*!handle zerocrossings This function checks to see which channels have experienced a zerocrossing and updates the appropriate fields. It also calculates an average frequency of the channel and displayes it. */ static int handlecrossing(Cop_Data *Cop) { int channel; int fieldnum; int freq = 0; char freqstring[FIELDSEP+1]; char zerostring[FIELDSEP+1]; for(channel=0;channelparsemask.zero&(1<counter_data[channel].laststamp,Cop->counter_data[channel].timestamp,Cop->counter_data[channel].laststart,Cop->counter_data[channel].countstart); if(Cop->counter_data[channel].zerocross)//if this is a zerocrossing and not the initial timestamp freq = E12_CounterCalcFreq(channel, Cop); sprintf(freqstring,"%u",freq); fieldnum=FREQ; set_field_buffer(counter_field[ARRAYCALC],0,freqstring); sprintf(zerostring,"%u",Cop->counter_data[channel].zerocross); fieldnum=ZERO; set_field_buffer(counter_field[ARRAYCALC],0,zerostring); } } return 0; } /*Update the fields from the Cop data structure @param Cop the data structure to update the fields from */ void UpdateFields(Cop_Data *Cop) { int channel; int fieldnum; char buffer[FIELDSEP+1]; for(channel=0;channelcounter_config[channel].threshold); set_field_buffer(counter_field[ARRAYCALC],0,buffer); fieldnum = TRIG; set_field_buffer(counter_field[ARRAYCALC],0,trig_strings[Cop->counter_config[channel].trigger]); fieldnum = FLAG; set_field_buffer(counter_field[ARRAYCALC],0,flag_strings[Cop->counter_config[channel].flags]); } } /*Display current counter readings. The counters that are not "masked" are refreshed by reading from the 37e12 and updating the fields. @param fd the device to address @param form the form to update @param Cop the cop data structure storing counter information @param mask bitmask of the counters to refresh(in this program this is all but the currently selected field) */ static int DisplayCounters(int fd, FORM *form, Cop_Data *Cop, unsigned char mask) { int channel; char buffer[1000]; int fieldnum=0; char countstr[FIELDSEP+1]; __u32 typemask=0; int rxnum=0; for(channel=0;channelcounter_data[channel].counter); set_field_buffer(counter_field[ARRAYCALC],0,countstr); typemask=0; } } return 0; } /*Display counters not currently being modified This is a wrapper around Display counters that sets the mask. The mask is used to prevent counters currently being modified(the cursor is on that field) from changing dynamically. */ static int DisplayUnusedCounters(int fd, FORM *form, Cop_Data *Cop) { FIELD *field = current_field(form); int index = field_index(field); int counter = index/FIELDNUM; int fieldnum = index%FIELDNUM; unsigned char mask = 0xff; if(fieldnum==COUNT) { mask&=~(1<