You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
403 lines
8.3 KiB
403 lines
8.3 KiB
/** @file system.c
|
|
@author M. P. Hayes, UCECE
|
|
@date 15 May 2007
|
|
@brief Test scaffold for UCFK4.
|
|
*/
|
|
|
|
#include "system.h"
|
|
#include "avrtest.h"
|
|
#include "pio.h"
|
|
#include "mgetkey.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
|
|
|
|
/** This needs to be at least 50 * 5 for a refresh rate of 50 Hz. */
|
|
#define SYSTEM_UPDATE_RATE 400
|
|
|
|
#define SYSTEM_KEYBOARD_RELEASE_PERIOD 0.2
|
|
|
|
#define SYSTEM_DISPLAY_PERSISTENCE_PERIOD 0.01
|
|
|
|
|
|
SFR_t SFR = {0, 0, ~0, 0, 0, ~0, 0, 0, ~0};
|
|
|
|
|
|
/* Define PIO pins driving LED matrix rows and columns. */
|
|
static const pio_t ledmat_rows[] =
|
|
{
|
|
LEDMAT_ROW1_PIO,
|
|
LEDMAT_ROW2_PIO,
|
|
LEDMAT_ROW3_PIO,
|
|
LEDMAT_ROW4_PIO,
|
|
LEDMAT_ROW5_PIO,
|
|
LEDMAT_ROW6_PIO,
|
|
LEDMAT_ROW7_PIO
|
|
};
|
|
|
|
static const pio_t ledmat_cols[] =
|
|
{
|
|
LEDMAT_COL1_PIO,
|
|
LEDMAT_COL2_PIO,
|
|
LEDMAT_COL3_PIO,
|
|
LEDMAT_COL4_PIO,
|
|
LEDMAT_COL5_PIO
|
|
};
|
|
|
|
|
|
typedef enum {NAVSWITCH_NONE, NAVSWITCH_EAST, NAVSWITCH_SOUTH,
|
|
NAVSWITCH_WEST, NAVSWITCH_NORTH, NAVSWITCH_PUSH} system_navswitch_t;
|
|
|
|
typedef enum {BUTTON_NONE, BUTTON_1} system_button_t;
|
|
|
|
|
|
static void system_display_update (void)
|
|
{
|
|
int row;
|
|
int col;
|
|
/* Persistence time (s). */
|
|
double tau = SYSTEM_DISPLAY_PERSISTENCE_PERIOD;
|
|
/* Sample period (s). */
|
|
double delta_t;
|
|
/* Exponential filter parameter. */
|
|
double alpha;
|
|
static uint8_t tdisplay[LEDMAT_ROWS_NUM][LEDMAT_COLS_NUM];
|
|
|
|
delta_t = 1.0 / SYSTEM_UPDATE_RATE;
|
|
alpha = tau / (tau + delta_t);
|
|
|
|
for (row = 0; row < LEDMAT_ROWS_NUM; row++)
|
|
{
|
|
for (col = 0; col < LEDMAT_COLS_NUM; col++)
|
|
{
|
|
uint8_t on;
|
|
|
|
/* Determine if pixel is on. */
|
|
on = !pio_output_get (ledmat_rows[row])
|
|
&& !pio_output_get (ledmat_cols[col]);
|
|
|
|
/* Apply exponential filter to provide some persistence.
|
|
This has an impulse response of exp(-t / tau) u(t). */
|
|
tdisplay[row][col] = tdisplay[row][col] * alpha + (1 - alpha) * 100 * on;
|
|
|
|
printf ("%c", tdisplay[row][col] > 1 ? '@' : '.');
|
|
}
|
|
printf ("\n");
|
|
}
|
|
printf ("\n");
|
|
/* Move cursor up. */
|
|
printf ("\e[%dA", LEDMAT_ROWS_NUM + 1);
|
|
}
|
|
|
|
|
|
static void system_navswitch_set (system_navswitch_t navswitch, bool state)
|
|
{
|
|
switch (navswitch)
|
|
{
|
|
case NAVSWITCH_NONE:
|
|
break;
|
|
|
|
case NAVSWITCH_NORTH:
|
|
pio_input_set (NAVSWITCH_NORTH_PIO, !state);
|
|
break;
|
|
|
|
case NAVSWITCH_EAST:
|
|
pio_input_set (NAVSWITCH_EAST_PIO, !state);
|
|
break;
|
|
|
|
case NAVSWITCH_SOUTH:
|
|
pio_input_set (NAVSWITCH_SOUTH_PIO, !state);
|
|
break;
|
|
|
|
case NAVSWITCH_WEST:
|
|
pio_input_set (NAVSWITCH_WEST_PIO, !state);
|
|
break;
|
|
|
|
case NAVSWITCH_PUSH:
|
|
pio_input_set (NAVSWITCH_PUSH_PIO, !state);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static void system_navswitch_press (system_navswitch_t navswitch)
|
|
{
|
|
system_navswitch_set (navswitch, 1);
|
|
}
|
|
|
|
|
|
static void system_navswitch_release (system_navswitch_t navswitch)
|
|
{
|
|
system_navswitch_set (navswitch, 0);
|
|
}
|
|
|
|
|
|
static void system_navswitch_select (system_navswitch_t navswitch)
|
|
{
|
|
static system_navswitch_t last = NAVSWITCH_NONE;
|
|
static int counter;
|
|
|
|
if (navswitch == NAVSWITCH_NONE)
|
|
{
|
|
if (counter)
|
|
{
|
|
counter--;
|
|
if (!counter)
|
|
{
|
|
system_navswitch_release (last);
|
|
last = NAVSWITCH_NONE;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
counter = SYSTEM_UPDATE_RATE * SYSTEM_KEYBOARD_RELEASE_PERIOD;
|
|
|
|
if (last == navswitch)
|
|
return;
|
|
|
|
system_navswitch_release (last);
|
|
|
|
last = navswitch;
|
|
|
|
system_navswitch_press (navswitch);
|
|
}
|
|
|
|
|
|
static void system_button_set (system_button_t button, bool state)
|
|
{
|
|
switch (button)
|
|
{
|
|
case BUTTON_NONE:
|
|
break;
|
|
|
|
case BUTTON_1:
|
|
/* Active high. */
|
|
pio_input_set (BUTTON1_PIO, state);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static void system_button_press (system_button_t button)
|
|
{
|
|
system_button_set (button, 1);
|
|
}
|
|
|
|
|
|
static void system_button_release (system_button_t button)
|
|
{
|
|
system_button_set (button, 0);
|
|
}
|
|
|
|
|
|
static void system_button_select (system_button_t button)
|
|
{
|
|
static system_button_t last = BUTTON_NONE;
|
|
static int counter;
|
|
|
|
if (button == BUTTON_NONE)
|
|
{
|
|
if (counter)
|
|
{
|
|
counter--;
|
|
if (!counter)
|
|
{
|
|
system_button_release (last);
|
|
last = BUTTON_NONE;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
counter = SYSTEM_UPDATE_RATE * SYSTEM_KEYBOARD_RELEASE_PERIOD;
|
|
|
|
if (last == button)
|
|
return;
|
|
|
|
system_button_release (last);
|
|
|
|
last = button;
|
|
|
|
system_button_press (button);
|
|
}
|
|
|
|
|
|
static void system_keyboard_init (void)
|
|
{
|
|
system_navswitch_release (NAVSWITCH_PUSH);
|
|
system_navswitch_release (NAVSWITCH_NORTH);
|
|
system_navswitch_release (NAVSWITCH_EAST);
|
|
system_navswitch_release (NAVSWITCH_SOUTH);
|
|
system_navswitch_release (NAVSWITCH_WEST);
|
|
|
|
system_button_release (BUTTON_1);
|
|
}
|
|
|
|
|
|
static void system_keyboard_update (void)
|
|
{
|
|
static int state = 0;
|
|
int key;
|
|
|
|
system_navswitch_select (NAVSWITCH_NONE);
|
|
system_button_select (BUTTON_NONE);
|
|
|
|
key = mgetkey ();
|
|
|
|
switch (state)
|
|
{
|
|
case 0:
|
|
switch (key)
|
|
{
|
|
case '\e':
|
|
/* Have escape character. */
|
|
state = 1;
|
|
break;
|
|
|
|
case ' ':
|
|
system_navswitch_select (NAVSWITCH_PUSH);
|
|
break;
|
|
|
|
case '.':
|
|
system_button_select (BUTTON_1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
if (key == '[')
|
|
state = 2;
|
|
else
|
|
state = 0;
|
|
break;
|
|
|
|
case 2:
|
|
state = 0;
|
|
switch (key)
|
|
{
|
|
case 'A':
|
|
system_navswitch_select (NAVSWITCH_NORTH);
|
|
break;
|
|
|
|
case 'B':
|
|
system_navswitch_select (NAVSWITCH_SOUTH);
|
|
break;
|
|
|
|
case 'C':
|
|
system_navswitch_select (NAVSWITCH_EAST);
|
|
break;
|
|
|
|
case 'D':
|
|
system_navswitch_select (NAVSWITCH_WEST);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void system_update (void)
|
|
{
|
|
system_display_update ();
|
|
system_keyboard_update ();
|
|
}
|
|
|
|
|
|
static void
|
|
system_handler (__unused__ int sig, __unused__ siginfo_t *si,
|
|
__unused__ void *uc)
|
|
{
|
|
system_update ();
|
|
}
|
|
|
|
|
|
typedef void (*sighandler_t)(int, siginfo_t *, void *);
|
|
|
|
static int system_interrupt_init (sighandler_t handler, int rate)
|
|
{
|
|
timer_t timerid;
|
|
struct sigevent sev;
|
|
struct itimerspec its;
|
|
long long period_ns;
|
|
sigset_t mask;
|
|
struct sigaction sa;
|
|
static int signum = 0;
|
|
|
|
/* When debugging with gdb use "handle SIG34 noprint". */
|
|
|
|
if (!signum)
|
|
signum = SIGRTMIN;
|
|
|
|
/* Establish handler for timer signal. */
|
|
sa.sa_flags = SA_SIGINFO;
|
|
sa.sa_sigaction = handler;
|
|
sigemptyset (&sa.sa_mask);
|
|
if (sigaction (signum, &sa, NULL) == -1)
|
|
{
|
|
perror ("sigaction");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
/* Block timer signal temporarily. */
|
|
sigemptyset (&mask);
|
|
sigaddset (&mask, signum);
|
|
if (sigprocmask (SIG_SETMASK, &mask, NULL) == -1)
|
|
{
|
|
perror ("sigprocmask");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
/* Create the timer. */
|
|
sev.sigev_notify = SIGEV_SIGNAL;
|
|
sev.sigev_signo = signum;
|
|
sev.sigev_value.sival_ptr = &timerid;
|
|
if (timer_create (CLOCK_REALTIME, &sev, &timerid) == -1)
|
|
{
|
|
perror ("timer_create");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
/* Start the timer */
|
|
period_ns = 1000000000 / rate;
|
|
its.it_value.tv_sec = period_ns / 1000000000;
|
|
its.it_value.tv_nsec = period_ns % 1000000000;
|
|
its.it_interval.tv_sec = its.it_value.tv_sec;
|
|
its.it_interval.tv_nsec = its.it_value.tv_nsec;
|
|
if (timer_settime (timerid, 0, &its, NULL) == -1)
|
|
{
|
|
perror ("timer_settime");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
/* Unlock the timer signal, so that timer notification
|
|
can be delivered. */
|
|
if (sigprocmask (SIG_UNBLOCK, &mask, NULL) == -1)
|
|
{
|
|
perror ("sigprocmask");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
signum++;
|
|
return signum;
|
|
}
|
|
|
|
|
|
void system_init (void)
|
|
{
|
|
system_keyboard_init ();
|
|
|
|
system_interrupt_init (system_handler, SYSTEM_UPDATE_RATE);
|
|
}
|