/* testtimer.c * test file for testing AT91RM9200 tc, deals with the Timer/Counter 1 for example to run 1000 loops at 60 Hz: /usr/src/timer/testtimer -p 16667 -w -l 1000 & # put in background, then do: /usr/src/timer/testtimer -v # to run the 32K timer ioctl */ #include #include #include #include #include #include #include #include #include #include #include #include const char* program_name; /* The name of this program. */ /* A string listing valid short options letters. */ const char* const short_options = "d:l:p:rswyfFv"; /* An array describing valid long options. */ const struct option long_options[] = { { "device", 1, NULL, 'd' }, { "loop", 1, NULL, 'l' }, { "period", 1, NULL, 'p' }, { "restart", 0, NULL, 'r' }, { "set_clock", 0, NULL, 's' }, { "wait", 0, NULL, 'w' }, { "delay", 0, NULL, 'y' }, { "enable_fast", 0, NULL, 'f' }, { "disable_fast", 0, NULL, 'F' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } /* Required at end of array. */ }; struct TIMER_DATA { pthread_t timer_thread_id1; pthread_t timer_thread_id2; pthread_t report_thread_id; unsigned int val_max; unsigned int val_min; unsigned int val_max2; unsigned int val_min2; unsigned int cntr2; struct timer_ioctl_data old_timer; int loop_cnt; int fd; pthread_mutex_t mutex; /* Protects access to value */ pthread_cond_t cond1; /* Signals change to value */ int verbose; /* be verbose? */ #define DEFAULT_INITIAL_COUNT 10 /* initial count */ const char *filename; }; struct TIMER_DATA timer_data = { .loop_cnt = 10, .verbose = 0, /* be verbose? */ .filename = NULL, .val_max = 0, .val_min = -1, .val_max2 = 0, .val_min2 = -1, }; void print_usage (FILE* stream, int exit_code) { fprintf (stream, "Usage: %s options\n", program_name); fprintf (stream, " -d --device device TIMER. [/dev/timer0]\n" " -l --loop count How many times to loop. [2]\n" " -p --period value Period in us. [8333]\n" " -r --restart Restart timer/counter upon delay.\n" " -s --set_clock Set the clock of a TIMER.\n" " -w --wait Test wait on period feature.\n" " -y --delay Delay for 1 second after 20 count.\n" " -f --enable_fast Enable 288 kHz fast clock out.\n" " -F --disable_fast Disable 288 kHz fast clock out.\n" " -v --verbose Print verbose messages.\n"); exit (exit_code); } char *filename = "/dev/wait_timer0"; char *filename2 = "/dev/wait_timer0"; int do_wait = 0; int period = 1000000 / 240; /* 240 Hz */ unsigned int *timer_values; #define TIMER_START_CNT 1 /* one time */ unsigned int loop_cnt = TIMER_START_CNT; void *timer_thread(struct TIMER_DATA *p_data); void *report_thread(struct TIMER_DATA *p_data); static int set_realtime_priority(int sched_mode) { struct sched_param schp; /* * set the process to realtime privs */ memset(&schp, 0, sizeof(schp)); schp.sched_priority = sched_get_priority_max(sched_mode); if (sched_setscheduler(0, sched_mode, &schp) != 0) { perror("sched_setscheduler"); return -1; } return 0; } int main(int argc, char **argv) { struct TIMER_DATA *p_data; int next_option; unsigned int cntr; int restart = 0; int set_clock = 0; int enable_fast_clock = 0; int disable_fast_clock = 0; int err, rc, delay = 0; pthread_attr_t attr2 = {0}; p_data = &timer_data; do { next_option = getopt_long (argc, argv, short_options, long_options, NULL); switch (next_option) { case 'd': /* -d or --device */ /* This option takes an argument, the name of the file. */ filename = optarg; break; case 'l': /* -l or --loop */ /* This option takes an argument, the loop count. */ p_data->loop_cnt = strtol (optarg, NULL, 0); break; case 'p': /* -p or --period */ /* This option takes an argument, the period in us. */ period = strtol (optarg, NULL, 0); break; case 'r': /* -r or --restart */ restart = 1; break; case 's': /* -v or --verbose */ set_clock = 1; break; case 'y': /* -y or --delay */ delay = 1; break; case 'w': /* -v or --verbose */ do_wait = 1; break; case 'v': /* -v or --verbose */ p_data->verbose = 1; break; case 'f': /* -f or --enable_fast */ enable_fast_clock = 1; break; case 'F': /* -v or --disable_fast */ disable_fast_clock = 1; break; case '?': /* The user specified an invalid option. */ /* Print usage information to standard error, and exit with exit code one (indicating abonormal termination). */ print_usage (stderr, 1); break; case -1: /* Done with options. */ break; default: /* Something else: unexpected. */ abort (); } } while (next_option != -1); timer_values = malloc (p_data->loop_cnt * sizeof (*timer_values)); set_realtime_priority(SCHED_FIFO); rc = pthread_attr_init( &attr2 ); if ( rc == -1 ) { perror( "pthread_attr_init()" ) ; return( errno ) ; } rc = pthread_attr_setschedpolicy( &attr2, SCHED_FIFO ); if ( rc == -1 ) { perror( "pthread_attr_setschedpolicy()" ) ; return( errno ) ; } rc = pthread_attr_setinheritsched(&attr2, PTHREAD_EXPLICIT_SCHED); if ( rc == -1 ) { perror( "pthread_attr_setinheritsched()" ) ; return( errno ) ; } if ((err = pthread_create (&p_data->timer_thread_id1, &attr2, (void *) timer_thread, p_data)) != 0) { printf("Create error!\n"); return(err); } if ((err = pthread_create (&p_data->timer_thread_id2, &attr2, (void *) timer_thread, p_data)) != 0) { printf("Create error!\n"); return(err); } if ((err = pthread_create (&p_data->report_thread_id, &attr2, (void *) report_thread, p_data)) != 0) { printf("Create error!\n"); return(err); } printf("waiting for thread\n"); pthread_join( p_data->timer_thread_id1, NULL); /* let read finish */ pthread_join( p_data->timer_thread_id2, NULL); /* let read finish */ // pthread_join( p_data->report_thread_id, NULL); /* let report finish */ free (timer_values); exit(0); } void *timer_thread(struct TIMER_DATA *p_data) { unsigned int diff, cntr; int fd; struct timer_ioctl_data ticks; struct timer_ioctl_timer timer_ioctl; #define TIMER_START_CNT 1 /* one time */ p_data->val_max = 0; p_data->val_min = -1; if (p_data->verbose) printf ("timer test program\n"); if (do_wait) { if ((fd = open(filename, O_RDWR)) < 0) { printf ("open failed %s\n", filename); return; } p_data->old_timer.flags = 0; /* restart */ p_data->old_timer.ticks = period; /* 60 Hz ticks */ ticks.flags = 0; /* restart */ for (cntr = 0; cntr < p_data->loop_cnt; cntr++) { ticks.ticks = period; /* 60 Hz ticks */ if (ioctl (fd, TIMER_WAIT, &ticks) != 0) /* wait for period */ { perror ("TIMER_WAIT failed"); close (fd); return; } timer_values[cntr] = ticks.crtr_ticks; /* save 8 MHz timer */ if (cntr >= 1) { diff = timer_values[cntr] - timer_values[cntr - 1]; if (diff < p_data->val_min) { p_data->val_min = diff; } if (diff > p_data->val_max) { p_data->val_max = diff; } if (diff < p_data->val_min2) { p_data->val_min2 = diff; } if (diff > p_data->val_max2) { p_data->val_max2 = diff; } } p_data->cntr2++; } printf ( "Jitter results: total count = %d, ticks = %d\nmin = %d ticks (%g %%), max = %d ticks (%g %%)\n", p_data->loop_cnt, p_data->old_timer.ticks, p_data->val_min, (float) ((p_data->old_timer.ticks - p_data->val_min) * 100) / p_data->old_timer.ticks, p_data->val_max, (float) ((p_data->val_max - p_data->old_timer.ticks) * 100) / p_data->old_timer.ticks ); close (fd); } else { /* now read the free running timer */ if ((fd = open(filename2, O_RDWR)) < 0) { printf ("open failed %s\n", filename2); return; } if (ioctl(fd, TIMER_GET_32768_TIMER, &timer_ioctl) != 0) /* write the data */ { perror ("TIMER_GET_32768_TIMER failed"); close (fd); return; } close (fd); if (p_data->verbose) printf("timer 0x%x\n", (unsigned long) timer_ioctl.timer_32768); } return; } void *report_thread(struct TIMER_DATA *p_data) { if (p_data->verbose) printf ("report_thread\n"); while (1) { /* delay in nanoseconds */ struct timespec ts = { 2, 0 } ; if (p_data->verbose) printf("waiting..."); nanosleep( &ts, NULL) ; printf ( "Report jitter results: total count = %d, ticks = %d\nmin = %d ticks (%g %%), max = %d ticks (%g %%)\n", p_data->cntr2, p_data->old_timer.ticks, p_data->val_min2, (float) ((p_data->old_timer.ticks - p_data->val_min2) * 100) / p_data->old_timer.ticks, p_data->val_max2, (float) ((p_data->val_max2 - p_data->old_timer.ticks) * 100) / p_data->old_timer.ticks ); p_data->val_max2 = 0; p_data->val_min2 = -1; p_data->cntr2 = 0; } return; }