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
*/
//#define DEBUG
#define DEBUG
#include <stdio.h>
#include <stdlib.h>
@ -90,16 +90,20 @@ static void* keyer_thread(void *arg);
static pthread_t keyer_thread_id;
// GPIO pins
#define KEYER_OUT_GPIO 26
#define LEFT_PADDLE_GPIO 22
#define RIGHT_PADDLE_GPIO 27
//#define LEFT_PADDLE_GPIO 22
//#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
#define KEYER_STRAIGHT 0
#define KEYER_MODE_A 1
#define KEYER_MODE_B 2
#define KEYER_ULTIMATIC 3
#define NUM_KEYER_MODES 4
#define NUM_KEYER_MODES 3
#define NSEC_PER_SEC (1000000000)
@ -114,42 +118,45 @@ enum {
DOTHELD,
DASHHELD,
LETTERSPACE,
HANGTIME,
EXITLOOP
};
enum {
NONE = 0,
DOT = 1,
DASH = 2
};
static int dot_memory = 0;
static int dash_memory = 0;
static int key_state = 0;
static int kdelay = 0;
static int dot_delay = 0;
static int dash_delay = 0;
static int kcwl = 0;
static int kcwr = 0;
static volatile int kcwl = 0;
static volatile int kcwr = 0;
static int *kdot;
static int *kdash;
static int cw_keyer_speed = 20;
static int cw_keyer_weight = 55;
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_active_state = 0;
static sem_t cw_event;
static int last_pressed = NONE;
#if defined(DEBUG)
static int cw_event_value;
#endif
static int cw_hangtime_msec = 250;
static int running, keyer_out = 0;
static inline int kstate() {
return (*kdash<<1)&(*kdot);
}
// Function prototypes
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() {
dot_delay = 1200 / cw_keyer_speed;
@ -163,49 +170,42 @@ void keyer_update() {
kdot = &kcwl;
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 okdash = *kdash;
int okdot = *kdot;
int new_state;
if (gpio == LEFT_PADDLE_GPIO)
if (gpio == left_paddle_gpio)
kcwl = state;
else // RIGHT_PADDLE_GPIO
kcwr = state;
if (*kdash > okdash)
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)
if (state) // || cw_keyer_mode == KEYER_STRAIGHT)
sem_post(&cw_event);
}
// Added to support WiringPi, which uses a different type of callback.
void keyer_event_left()
{
int level = digitalRead(LEFT_PADDLE_GPIO);
keyer_event(LEFT_PADDLE_GPIO, level, 0);
int level = digitalRead(left_paddle_gpio);
keyer_event(left_paddle_gpio, level, 0);
#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
}
// Added to support WiringPi, which uses a different type of callback.
void keyer_event_right()
{
int level = digitalRead(RIGHT_PADDLE_GPIO);
keyer_event(RIGHT_PADDLE_GPIO, level, 0);
int level = digitalRead(right_paddle_gpio);
keyer_event(right_paddle_gpio, level, 0);
#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
}
@ -215,38 +215,62 @@ void clear_memory() {
}
static inline void set_keyer_out(int state) {
// if (keyer_out != state) {
keyer_out = state;
// write(1, &buf[state], 1); // write output to stdout for Pi-HFIQ
// if (state)
// beep_mute = 0;
// else
// beep_mute = 1;
// }
static void set_keyer_out(int state) {
if (state && tr_switch_gpio) {
digitalWrite(tr_switch_gpio, 1);
}
keyer_out = state;
digitalWrite(keyer_out_gpio, keyer_out);
}
static void* keyer_thread(void *arg) {
struct timespec loop_delay;
int interval = 1000000; // 1 ms
while(running) {
int hangtime_elapsed = 0;
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);
key_state = CHECK;
while (key_state != EXITLOOP) {
if (keyer_out)
hangtime_elapsed = 0;
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 (*kdash) { // send manual dashes
set_keyer_out(1);
key_state = EXITLOOP;
if (!keyer_out) {
set_keyer_out(1);
key_state = HANGTIME; //EXITLOOP;
}
}
else if (*kdot) // and automatic dots
key_state = PREDOT;
else {
set_keyer_out(0);
else if (key_state == HANGTIME) {
if (keyer_out)
set_keyer_out(0);
} else {
// NOTE: not working right
//set_keyer_out(0);
key_state = EXITLOOP;
}
}
@ -255,16 +279,19 @@ static void* keyer_thread(void *arg) {
key_state = PREDOT;
else if (*kdash)
key_state = PREDASH;
else {
set_keyer_out(0);
else if (key_state != HANGTIME) {
// Do we really need to do this? Aren't these covered in other states?
//set_keyer_out(0);
key_state = EXITLOOP;
}
}
break;
case PREDOT: // need to clear any pending dots or dashes
clear_memory();
key_state = SENDDOT;
break;
case PREDASH:
clear_memory();
key_state = SENDDASH;
@ -288,11 +315,7 @@ static void* keyer_thread(void *arg) {
}
if (*kdash) { // set dash memory
if (cw_keyer_mode == KEYER_ULTIMATIC) {
if (last_pressed == DASH)
dash_memory = 1;
} else
dash_memory = 1;
dash_memory = 1;
}
break;
@ -314,11 +337,7 @@ static void* keyer_thread(void *arg) {
}
if (*kdot) { // set dot memory
if (cw_keyer_mode == KEYER_ULTIMATIC) {
if (last_pressed == DOT)
dot_memory = 1;
} else
dot_memory = 1;
dot_memory = 1;
}
break;
@ -327,7 +346,7 @@ static void* keyer_thread(void *arg) {
if (kdelay == dot_delay) {
kdelay = 0;
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
key_state = PREDASH;
else key_state = DOTHELD; // dot is still active so service
@ -335,11 +354,7 @@ static void* keyer_thread(void *arg) {
else kdelay++;
if (*kdash) { // set dash memory
if (cw_keyer_mode == KEYER_ULTIMATIC) {
if (last_pressed == DASH)
dash_memory = 1;
} else
dash_memory = 1;
dash_memory = 1;
}
break;
@ -355,19 +370,13 @@ static void* keyer_thread(void *arg) {
else kdelay++;
if (*kdot) { // set dot memory
if (cw_keyer_mode == KEYER_ULTIMATIC) {
if (last_pressed == DOT)
dot_memory = 1;
} else
dot_memory = 1;
dot_memory = 1;
}
break;
// check if dot paddle is still held, if so repeat the dot. Else check if Letter space is required
case DOTHELD:
if ((cw_keyer_mode == KEYER_ULTIMATIC) && (last_pressed == DASH))
key_state = PREDASH;
else if (*kdot) // dot has been set during the dash so service
if (*kdot) // dot has been set during the dash so service
key_state = PREDOT;
else if (*kdash) // has dash paddle been pressed
key_state = PREDASH;
@ -375,14 +384,12 @@ static void* keyer_thread(void *arg) {
clear_memory();
key_state = LETTERSPACE;
}
else key_state = EXITLOOP;
else key_state = HANGTIME;
break;
// check if dash paddle is still held, if so repeat the dash. Else check if Letter space is required
case DASHHELD:
if ((cw_keyer_mode == KEYER_ULTIMATIC) && (last_pressed == DOT))
key_state = PREDOT;
else if (*kdash) // dash has been set during the dot so service
if (*kdash) // dash has been set during the dot so service
key_state = PREDASH;
else if (*kdot) // has dot paddle been pressed
key_state = PREDOT;
@ -390,7 +397,7 @@ static void* keyer_thread(void *arg) {
clear_memory();
key_state = LETTERSPACE;
}
else key_state = EXITLOOP;
else key_state = HANGTIME;
break;
// 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;
else if (dash_memory)
key_state = PREDASH;
else key_state = EXITLOOP; // no memories set so restart
else key_state = HANGTIME; // no memories set so restart
}
else kdelay++;
@ -410,19 +417,23 @@ static void* keyer_thread(void *arg) {
if (*kdot) dot_memory = 1;
if (*kdash) dash_memory = 1;
break;
default:
key_state = EXITLOOP;
}
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++;
if (!keyer_out)
hangtime_elapsed++;
if (key_state != EXITLOOP) {
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;
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) {
fprintf(stderr, "Unable to setup wiringPi: %s\n", strerror (errno));
return -1;
}
pinMode(RIGHT_PADDLE_GPIO, INPUT);
pullUpDnControl(RIGHT_PADDLE_GPIO, PUD_UP);
pinMode(right_paddle_gpio, INPUT);
pullUpDnControl(right_paddle_gpio, PUD_UP);
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);
pullUpDnControl(LEFT_PADDLE_GPIO, PUD_UP);
pinMode(left_paddle_gpio, INPUT);
pullUpDnControl(left_paddle_gpio, PUD_UP);
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);
digitalWrite(KEYER_OUT_GPIO, 0);
if (keyer_out_gpio) {
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();
@ -473,23 +502,24 @@ void close_key_gpiokeyer(void)
int is_key_down_gpiokeyer(void)
{
static int retval;
// sem_wait(&cw_event);
retval = keyer_out;
// sem_post(&cw_event);
return retval;
}
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))
cw_keyer_mode = mode;
}
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;
keyer_update();
}

View File

@ -100,7 +100,7 @@ int quisk_open_key(const char * name)
ret = open_key_enet(name);
}
#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;
ret = open_key_gpiokeyer(name);
}