Removed Ultimatic keyer mode. Added digital outputs for keyer and PTT (T/R switch) lines. Streamlined the code for the semaphores. Made it more configurable / less #defines.

This commit is contained in:
Rob French 2020-02-26 21:28:59 -06:00
parent 9892ccb1a4
commit 1b4775f452
3 changed files with 144 additions and 114 deletions

View File

@ -1 +1 @@
#Quisk version 4.1.52 #Quisk version 4.1.52-upr1

View File

@ -68,7 +68,7 @@ Boston, MA 02110-1301, USA.
Speed calculation - Using standard PARIS timing, dot_period(mS) = 1200/WPM Speed calculation - Using standard PARIS timing, dot_period(mS) = 1200/WPM
*/ */
//#define DEBUG #define DEBUG
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -90,16 +90,20 @@ static void* keyer_thread(void *arg);
static pthread_t keyer_thread_id; static pthread_t keyer_thread_id;
// GPIO pins // GPIO pins
#define KEYER_OUT_GPIO 26 //#define LEFT_PADDLE_GPIO 22
#define LEFT_PADDLE_GPIO 22 //#define RIGHT_PADDLE_GPIO 27
#define RIGHT_PADDLE_GPIO 27 //#define KEY_OUT_GPIO 23
//#define PTT_OUT_GPIO 24
static int left_paddle_gpio = 22;
static int right_paddle_gpio = 27;
static int keyer_out_gpio = 23;
static int tr_switch_gpio = 24;
// Keyer modes // Keyer modes
#define KEYER_STRAIGHT 0 #define KEYER_STRAIGHT 0
#define KEYER_MODE_A 1 #define KEYER_MODE_A 1
#define KEYER_MODE_B 2 #define KEYER_MODE_B 2
#define KEYER_ULTIMATIC 3 #define NUM_KEYER_MODES 3
#define NUM_KEYER_MODES 4
#define NSEC_PER_SEC (1000000000) #define NSEC_PER_SEC (1000000000)
@ -114,42 +118,45 @@ enum {
DOTHELD, DOTHELD,
DASHHELD, DASHHELD,
LETTERSPACE, LETTERSPACE,
HANGTIME,
EXITLOOP EXITLOOP
}; };
enum {
NONE = 0,
DOT = 1,
DASH = 2
};
static int dot_memory = 0; static int dot_memory = 0;
static int dash_memory = 0; static int dash_memory = 0;
static int key_state = 0; static int key_state = 0;
static int kdelay = 0; static int kdelay = 0;
static int dot_delay = 0; static int dot_delay = 0;
static int dash_delay = 0; static int dash_delay = 0;
static int kcwl = 0; static volatile int kcwl = 0;
static int kcwr = 0; static volatile int kcwr = 0;
static int *kdot; static int *kdot;
static int *kdash; static int *kdash;
static int cw_keyer_speed = 20; static int cw_keyer_speed = 20;
static int cw_keyer_weight = 55; static int cw_keyer_weight = 55;
static int cw_keys_reversed = 0; static int cw_keys_reversed = 0;
static int cw_keyer_mode = KEYER_MODE_B; static int cw_keyer_mode = KEYER_MODE_A;
static int cw_keyer_spacing = 0; static int cw_keyer_spacing = 0;
static int cw_active_state = 0; static int cw_active_state = 0;
static sem_t cw_event; static sem_t cw_event;
#if defined(DEBUG)
static int last_pressed = NONE; static int cw_event_value;
#endif
static int cw_hangtime_msec = 250;
static int running, keyer_out = 0; static int running, keyer_out = 0;
static inline int kstate() { // Function prototypes
return (*kdash<<1)&(*kdot);
}
static int prev_state = 0; void keyer_update(void);
void keyer_event(int, int, uint32_t);
void keyer_event_left(void);
void keyer_event_right(void);
void clear_memory(void);
static void set_keyer_out(int);
static void* keyer_thread(void*);
/////
void keyer_update() { void keyer_update() {
dot_delay = 1200 / cw_keyer_speed; dot_delay = 1200 / cw_keyer_speed;
@ -163,49 +170,42 @@ void keyer_update() {
kdot = &kcwl; kdot = &kcwl;
kdash = &kcwr; kdash = &kcwr;
} }
// need to actually dynamically set this at some point...
cw_hangtime_msec = (int)(0.25f * 1000.0f);
} }
void keyer_event(int gpio, int level, uint32_t tick) { void keyer_event(int gpio, int level, uint32_t tick) {
int state = (cw_active_state == 0) ? (level == 0) : (level != 0); int state = (cw_active_state == 0) ? (level == 0) : (level != 0);
int okdash = *kdash;
int okdot = *kdot;
int new_state;
if (gpio == LEFT_PADDLE_GPIO) if (gpio == left_paddle_gpio)
kcwl = state; kcwl = state;
else // RIGHT_PADDLE_GPIO else // RIGHT_PADDLE_GPIO
kcwr = state; kcwr = state;
if (*kdash > okdash) if (state) // || cw_keyer_mode == KEYER_STRAIGHT)
last_pressed = DASH;
else if (*kdot > okdot)
last_pressed = DOT;
else {
new_state = kstate();
last_pressed = new_state & prev_state;
}
if (state || cw_keyer_mode == KEYER_STRAIGHT)
sem_post(&cw_event); sem_post(&cw_event);
} }
// Added to support WiringPi, which uses a different type of callback. // Added to support WiringPi, which uses a different type of callback.
void keyer_event_left() void keyer_event_left()
{ {
int level = digitalRead(LEFT_PADDLE_GPIO); int level = digitalRead(left_paddle_gpio);
keyer_event(LEFT_PADDLE_GPIO, level, 0); keyer_event(left_paddle_gpio, level, 0);
#if defined(DEBUG) #if defined(DEBUG)
printf("Left Paddle Pressed\n"); sem_getvalue(&cw_event, &cw_event_value);
fprintf(stdout, "left paddle pressed - %d\n", cw_event_value);
#endif #endif
} }
// Added to support WiringPi, which uses a different type of callback. // Added to support WiringPi, which uses a different type of callback.
void keyer_event_right() void keyer_event_right()
{ {
int level = digitalRead(RIGHT_PADDLE_GPIO); int level = digitalRead(right_paddle_gpio);
keyer_event(RIGHT_PADDLE_GPIO, level, 0); keyer_event(right_paddle_gpio, level, 0);
#if defined(DEBUG) #if defined(DEBUG)
printf("Right Paddle Pressed\n"); sem_getvalue(&cw_event, &cw_event_value);
fprintf(stdout, "right paddle pressed - %d\n", cw_event_value);
#endif #endif
} }
@ -215,38 +215,62 @@ void clear_memory() {
} }
static inline void set_keyer_out(int state) { static void set_keyer_out(int state) {
// if (keyer_out != state) { if (state && tr_switch_gpio) {
keyer_out = state; digitalWrite(tr_switch_gpio, 1);
// write(1, &buf[state], 1); // write output to stdout for Pi-HFIQ }
keyer_out = state;
// if (state) digitalWrite(keyer_out_gpio, keyer_out);
// beep_mute = 0;
// else
// beep_mute = 1;
// }
} }
static void* keyer_thread(void *arg) { static void* keyer_thread(void *arg) {
struct timespec loop_delay; struct timespec loop_delay;
int interval = 1000000; // 1 ms int interval = 1000000; // 1 ms
int hangtime_elapsed = 0;
while(running) {
while (running) {
#if defined(DEBUG)
sem_getvalue(&cw_event, &cw_event_value);
fprintf(stdout, "waiting - %d\n", cw_event_value);
#endif
sem_wait(&cw_event); sem_wait(&cw_event);
key_state = CHECK; key_state = CHECK;
while (key_state != EXITLOOP) { while (key_state != EXITLOOP) {
if (keyer_out)
hangtime_elapsed = 0;
switch(key_state) { switch(key_state) {
case CHECK: // check for key press
case HANGTIME:
if (hangtime_elapsed >= cw_hangtime_msec) {
if (tr_switch_gpio) {
#if defined(DEBUG)
sem_getvalue(&cw_event, &cw_event_value);
fprintf(stdout, "hangtime complete, %d msec - %d\n", cw_hangtime_msec, cw_event_value);
#endif
digitalWrite(tr_switch_gpio, 0);
key_state = EXITLOOP;
}
}
// INTENTIONALLY FALLING THROUGH TO 'CHECK' STATE (no 'break')
case CHECK: // check for key press
if (cw_keyer_mode == KEYER_STRAIGHT) { // Straight/External key or bug if (cw_keyer_mode == KEYER_STRAIGHT) { // Straight/External key or bug
if (*kdash) { // send manual dashes if (*kdash) { // send manual dashes
set_keyer_out(1); if (!keyer_out) {
key_state = EXITLOOP; set_keyer_out(1);
key_state = HANGTIME; //EXITLOOP;
}
} }
else if (*kdot) // and automatic dots else if (*kdot) // and automatic dots
key_state = PREDOT; key_state = PREDOT;
else { else if (key_state == HANGTIME) {
set_keyer_out(0); if (keyer_out)
set_keyer_out(0);
} else {
// NOTE: not working right
//set_keyer_out(0);
key_state = EXITLOOP; key_state = EXITLOOP;
} }
} }
@ -255,16 +279,19 @@ static void* keyer_thread(void *arg) {
key_state = PREDOT; key_state = PREDOT;
else if (*kdash) else if (*kdash)
key_state = PREDASH; key_state = PREDASH;
else { else if (key_state != HANGTIME) {
set_keyer_out(0); // Do we really need to do this? Aren't these covered in other states?
//set_keyer_out(0);
key_state = EXITLOOP; key_state = EXITLOOP;
} }
} }
break; break;
case PREDOT: // need to clear any pending dots or dashes case PREDOT: // need to clear any pending dots or dashes
clear_memory(); clear_memory();
key_state = SENDDOT; key_state = SENDDOT;
break; break;
case PREDASH: case PREDASH:
clear_memory(); clear_memory();
key_state = SENDDASH; key_state = SENDDASH;
@ -288,11 +315,7 @@ static void* keyer_thread(void *arg) {
} }
if (*kdash) { // set dash memory if (*kdash) { // set dash memory
if (cw_keyer_mode == KEYER_ULTIMATIC) { dash_memory = 1;
if (last_pressed == DASH)
dash_memory = 1;
} else
dash_memory = 1;
} }
break; break;
@ -314,11 +337,7 @@ static void* keyer_thread(void *arg) {
} }
if (*kdot) { // set dot memory if (*kdot) { // set dot memory
if (cw_keyer_mode == KEYER_ULTIMATIC) { dot_memory = 1;
if (last_pressed == DOT)
dot_memory = 1;
} else
dot_memory = 1;
} }
break; break;
@ -327,7 +346,7 @@ static void* keyer_thread(void *arg) {
if (kdelay == dot_delay) { if (kdelay == dot_delay) {
kdelay = 0; kdelay = 0;
if(!*kdot && cw_keyer_mode == KEYER_STRAIGHT) // just return if in bug mode if(!*kdot && cw_keyer_mode == KEYER_STRAIGHT) // just return if in bug mode
key_state = EXITLOOP; key_state = HANGTIME;
else if (dash_memory) // dash has been set during the dot so service else if (dash_memory) // dash has been set during the dot so service
key_state = PREDASH; key_state = PREDASH;
else key_state = DOTHELD; // dot is still active so service else key_state = DOTHELD; // dot is still active so service
@ -335,11 +354,7 @@ static void* keyer_thread(void *arg) {
else kdelay++; else kdelay++;
if (*kdash) { // set dash memory if (*kdash) { // set dash memory
if (cw_keyer_mode == KEYER_ULTIMATIC) { dash_memory = 1;
if (last_pressed == DASH)
dash_memory = 1;
} else
dash_memory = 1;
} }
break; break;
@ -355,19 +370,13 @@ static void* keyer_thread(void *arg) {
else kdelay++; else kdelay++;
if (*kdot) { // set dot memory if (*kdot) { // set dot memory
if (cw_keyer_mode == KEYER_ULTIMATIC) { dot_memory = 1;
if (last_pressed == DOT)
dot_memory = 1;
} else
dot_memory = 1;
} }
break; break;
// check if dot paddle is still held, if so repeat the dot. Else check if Letter space is required // check if dot paddle is still held, if so repeat the dot. Else check if Letter space is required
case DOTHELD: case DOTHELD:
if ((cw_keyer_mode == KEYER_ULTIMATIC) && (last_pressed == DASH)) if (*kdot) // dot has been set during the dash so service
key_state = PREDASH;
else if (*kdot) // dot has been set during the dash so service
key_state = PREDOT; key_state = PREDOT;
else if (*kdash) // has dash paddle been pressed else if (*kdash) // has dash paddle been pressed
key_state = PREDASH; key_state = PREDASH;
@ -375,14 +384,12 @@ static void* keyer_thread(void *arg) {
clear_memory(); clear_memory();
key_state = LETTERSPACE; key_state = LETTERSPACE;
} }
else key_state = EXITLOOP; else key_state = HANGTIME;
break; break;
// check if dash paddle is still held, if so repeat the dash. Else check if Letter space is required // check if dash paddle is still held, if so repeat the dash. Else check if Letter space is required
case DASHHELD: case DASHHELD:
if ((cw_keyer_mode == KEYER_ULTIMATIC) && (last_pressed == DOT)) if (*kdash) // dash has been set during the dot so service
key_state = PREDOT;
else if (*kdash) // dash has been set during the dot so service
key_state = PREDASH; key_state = PREDASH;
else if (*kdot) // has dot paddle been pressed else if (*kdot) // has dot paddle been pressed
key_state = PREDOT; key_state = PREDOT;
@ -390,7 +397,7 @@ static void* keyer_thread(void *arg) {
clear_memory(); clear_memory();
key_state = LETTERSPACE; key_state = LETTERSPACE;
} }
else key_state = EXITLOOP; else key_state = HANGTIME;
break; break;
// Add letter space (3 x dot delay) to end of character and check if a paddle is pressed during this time. // Add letter space (3 x dot delay) to end of character and check if a paddle is pressed during this time.
@ -402,7 +409,7 @@ static void* keyer_thread(void *arg) {
key_state = PREDOT; key_state = PREDOT;
else if (dash_memory) else if (dash_memory)
key_state = PREDASH; key_state = PREDASH;
else key_state = EXITLOOP; // no memories set so restart else key_state = HANGTIME; // no memories set so restart
} }
else kdelay++; else kdelay++;
@ -410,19 +417,23 @@ static void* keyer_thread(void *arg) {
if (*kdot) dot_memory = 1; if (*kdot) dot_memory = 1;
if (*kdash) dash_memory = 1; if (*kdash) dash_memory = 1;
break; break;
default: default:
key_state = EXITLOOP; key_state = EXITLOOP;
} }
clock_gettime(CLOCK_MONOTONIC, &loop_delay); if (!keyer_out)
loop_delay.tv_nsec += interval; hangtime_elapsed++;
while (loop_delay.tv_nsec >= NSEC_PER_SEC) {
loop_delay.tv_nsec -= NSEC_PER_SEC; if (key_state != EXITLOOP) {
loop_delay.tv_sec++; clock_gettime(CLOCK_MONOTONIC, &loop_delay);
loop_delay.tv_nsec += interval;
while (loop_delay.tv_nsec >= NSEC_PER_SEC) {
loop_delay.tv_nsec -= NSEC_PER_SEC;
loop_delay.tv_sec++;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &loop_delay, NULL);
} }
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &loop_delay, NULL);
} }
} }
} }
@ -431,23 +442,41 @@ int open_key_gpiokeyer(const char * name)
{ {
int i; int i;
if (sscanf(name, "gpio:%d,%d,%d,%d", &left_paddle_gpio, &right_paddle_gpio, &keyer_out_gpio, &tr_switch_gpio) < 4) {
fprintf(stderr, "Insufficient parameters for GPIO Keyer: %s\n", name);
return -1;
}
#if defined(DEBUG)
fprintf(stdout, "GPIO Keyer selected:\n - left paddle GPIO: %d\n - right paddle GPIO: %d\n - keyer out GPIO: %d\n - T/R switch GPIO: %d\n",
left_paddle_gpio, right_paddle_gpio, keyer_out_gpio, tr_switch_gpio);
#endif
if (wiringPiSetupGpio () < 0) { if (wiringPiSetupGpio () < 0) {
fprintf(stderr, "Unable to setup wiringPi: %s\n", strerror (errno)); fprintf(stderr, "Unable to setup wiringPi: %s\n", strerror (errno));
return -1; return -1;
} }
pinMode(RIGHT_PADDLE_GPIO, INPUT); pinMode(right_paddle_gpio, INPUT);
pullUpDnControl(RIGHT_PADDLE_GPIO, PUD_UP); pullUpDnControl(right_paddle_gpio, PUD_UP);
usleep(100000); usleep(100000);
wiringPiISR(RIGHT_PADDLE_GPIO, INT_EDGE_BOTH, keyer_event_right); wiringPiISR(right_paddle_gpio, INT_EDGE_BOTH, keyer_event_right);
pinMode(LEFT_PADDLE_GPIO, INPUT); pinMode(left_paddle_gpio, INPUT);
pullUpDnControl(LEFT_PADDLE_GPIO, PUD_UP); pullUpDnControl(left_paddle_gpio, PUD_UP);
usleep(100000); usleep(100000);
wiringPiISR(LEFT_PADDLE_GPIO, INT_EDGE_BOTH, keyer_event_left); wiringPiISR(left_paddle_gpio, INT_EDGE_BOTH, keyer_event_left);
pinMode(KEYER_OUT_GPIO, OUTPUT); if (keyer_out_gpio) {
digitalWrite(KEYER_OUT_GPIO, 0); pinMode(keyer_out_gpio, OUTPUT);
digitalWrite(keyer_out_gpio, 0);
}
if (tr_switch_gpio) {
pinMode(tr_switch_gpio, OUTPUT);
digitalWrite(tr_switch_gpio, 0);
}
keyer_update(); keyer_update();
@ -473,23 +502,24 @@ void close_key_gpiokeyer(void)
int is_key_down_gpiokeyer(void) int is_key_down_gpiokeyer(void)
{ {
static int retval; static int retval;
// sem_wait(&cw_event);
retval = keyer_out; retval = keyer_out;
// sem_post(&cw_event);
return retval; return retval;
} }
void quisk_set_gpio_keyer_mode(int mode) void quisk_set_gpio_keyer_mode(int mode)
{ {
fprintf(stderr, "MODE CHANGE\n"); #if defined(DEBUG)
fprintf(stdout, "MODE CHANGE\n");
#endif
if ((mode > -1) && (mode < NUM_KEYER_MODES)) if ((mode > -1) && (mode < NUM_KEYER_MODES))
cw_keyer_mode = mode; cw_keyer_mode = mode;
} }
void quisk_set_gpio_keyer_speed(int wpm) void quisk_set_gpio_keyer_speed(int wpm)
{ {
fprintf(stderr, "SPEED CHANGE\n"); #if defined(DEBUG)
fprintf(stdout, "SPEED CHANGE\n");
#endif
cw_keyer_speed = wpm; cw_keyer_speed = wpm;
keyer_update(); keyer_update();
} }

View File

@ -100,7 +100,7 @@ int quisk_open_key(const char * name)
ret = open_key_enet(name); ret = open_key_enet(name);
} }
#if defined(ENABLE_GPIO_KEYER) #if defined(ENABLE_GPIO_KEYER)
else if (!strncmp(name, "GPIO", 4)){ // Raspberry Pi GPIO keyer else if (!strncmp(name, "gpio", 4)){ // Raspberry Pi GPIO keyer
key_method = GpioKeyer; key_method = GpioKeyer;
ret = open_key_gpiokeyer(name); ret = open_key_gpiokeyer(name);
} }