Comments, various minor "productionization" changes.
This commit is contained in:
parent
e440b07548
commit
8a58f0dba8
380
gpiokeyer.c
380
gpiokeyer.c
@ -1,4 +1,3 @@
|
|||||||
//#if defined(ENABLE_GPIO_KEYER)
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* gpiokeyer.c
|
* gpiokeyer.c
|
||||||
* by Robert A. French (KC4UPR)
|
* by Robert A. French (KC4UPR)
|
||||||
@ -89,12 +88,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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// NEED TO FIGURE OUT A SIMPLER WAY TO DO THIS... THIS IS JUST TO GET ENABLE_GPIO_KEYER!
|
//#define DEBUG
|
||||||
//#include <Python.h> // used by quisk.h
|
|
||||||
//#include <complex.h> // Used by quisk.h
|
|
||||||
//#include "quisk.h"
|
|
||||||
|
|
||||||
#define DEBUG
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -112,27 +106,31 @@ Boston, MA 02110-1301, USA.
|
|||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#include <wiringPi.h>
|
#include <wiringPi.h>
|
||||||
|
|
||||||
static void* keyer_thread(void *arg);
|
/***********************************************************************
|
||||||
static pthread_t keyer_thread_id;
|
* Constants
|
||||||
|
**********************************************************************/
|
||||||
// GPIO pins
|
|
||||||
//#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 NUM_KEYER_MODES 3
|
|
||||||
|
|
||||||
#define NSEC_PER_SEC (1000000000)
|
#define NSEC_PER_SEC (1000000000)
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* Types
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
/* KC4UPR: I added an enumeration for the keyer modes, instead of a
|
||||||
|
* macro. Probably superfluous.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
KEYER_STRAIGHT = 0,
|
||||||
|
KEYER_MODE_A,
|
||||||
|
KEYER_MODE_B,
|
||||||
|
NUM_KEYER_MODES
|
||||||
|
};
|
||||||
|
|
||||||
|
/* KC4UPR: Enumeration of the states in the iambic keyer state machine.
|
||||||
|
* I added the HANGTIME state, which is used to differentiate between
|
||||||
|
* the end of the last CW symbol, and the end of the transmission delay
|
||||||
|
* for QSK/semi-QSK operations.
|
||||||
|
*/
|
||||||
enum {
|
enum {
|
||||||
CHECK = 0,
|
CHECK = 0,
|
||||||
PREDOT,
|
PREDOT,
|
||||||
@ -148,44 +146,81 @@ enum {
|
|||||||
EXITLOOP
|
EXITLOOP
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* Function Prototypes
|
||||||
|
*
|
||||||
|
* These are only prototypes for the internal functions... the functions
|
||||||
|
* that are callable from quisk.c and/or Python are all declared in
|
||||||
|
* quisk.h, and then defined at the end of this file.
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
static void keyer_update(void);
|
||||||
|
static void keyer_event(int, int, uint32_t);
|
||||||
|
static void keyer_event_left(void);
|
||||||
|
static void keyer_event_right(void);
|
||||||
|
static void clear_memory(void);
|
||||||
|
static void set_keyer_out(int);
|
||||||
|
static void* keyer_thread(void *);
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* Variables
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
/* GPIO Pins
|
||||||
|
*
|
||||||
|
* KC4UPR: Converted the GPIO pin definitions from macros to variables,
|
||||||
|
* so that we can set them dynamically at runtime.
|
||||||
|
*/
|
||||||
|
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 Configuration
|
||||||
|
*/
|
||||||
|
static int cw_keyer_mode = KEYER_MODE_A;
|
||||||
|
static int cw_keyer_speed = 20;
|
||||||
|
static int cw_keyer_weight = 55;
|
||||||
|
static int cw_keys_reversed = 0;
|
||||||
|
static int cw_keyer_spacing = 0;
|
||||||
|
static int cw_keyer_enabled = 1;
|
||||||
|
static int cw_hangtime_msec = 250; // not currently (re-)configurable
|
||||||
|
static int cw_active_state = 0; // not currently (re-)configurable
|
||||||
|
|
||||||
|
/* Current Paddle State
|
||||||
|
*/
|
||||||
|
static int kcwl = 0; // left paddle
|
||||||
|
static int kcwr = 0; // right paddle
|
||||||
|
static int *kdot; // dots - point to either left or right
|
||||||
|
static int *kdash; // dashes - point to either right or left
|
||||||
|
|
||||||
|
/* Internal Keyer State
|
||||||
|
*/
|
||||||
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 int running, keyer_out = 0;
|
||||||
static int kcwr = 0;
|
|
||||||
static int *kdot;
|
/* Thread Variables
|
||||||
static int *kdash;
|
*/
|
||||||
static int cw_keyer_speed = 20;
|
static pthread_t keyer_thread_id;
|
||||||
static int cw_keyer_weight = 55;
|
|
||||||
static int cw_keys_reversed = 0;
|
|
||||||
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 sem_t cw_event;
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
static int cw_event_value;
|
static int cw_event_value;
|
||||||
#endif
|
#endif
|
||||||
static int cw_hangtime_msec = 250;
|
|
||||||
|
|
||||||
static int running, keyer_out = 0;
|
/***********************************************************************
|
||||||
static int cw_keyer_enabled = 1;
|
* Internal functions
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
// Function prototypes
|
/* KC4UPR: Update the keyer, i.e. calculate its internal values based
|
||||||
|
* on current settings. This is called once at keyer open, and then
|
||||||
void keyer_update(void);
|
* again any time certain settings are updated.
|
||||||
void keyer_event(int, int, uint32_t);
|
*/
|
||||||
void keyer_event_left(void);
|
static void keyer_update() {
|
||||||
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;
|
dot_delay = 1200 / cw_keyer_speed;
|
||||||
// will be 3 * dot length at standard weight
|
// will be 3 * dot length at standard weight
|
||||||
dash_delay = (dot_delay * 3 * cw_keyer_weight) / 50;
|
dash_delay = (dot_delay * 3 * cw_keyer_weight) / 50;
|
||||||
@ -198,11 +233,19 @@ void keyer_update() {
|
|||||||
kdash = &kcwr;
|
kdash = &kcwr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to actually dynamically set this at some point...
|
/* NOTE: need to actually dynamically set this at some point...
|
||||||
|
* Currently, based on the code below, there is a hardcoded 250ms
|
||||||
|
* semi-QSK delay. ~250ms after the end of the last symbol, the
|
||||||
|
* T/R switch will be disabled.
|
||||||
|
*/
|
||||||
cw_hangtime_msec = (int)(0.25f * 1000.0f);
|
cw_hangtime_msec = (int)(0.25f * 1000.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyer_event(int gpio, int level, uint32_t tick) {
|
/* KC4UPR: The "actual" event handler. Updates the value of the
|
||||||
|
* currently depressed/released paddle.
|
||||||
|
*/
|
||||||
|
static void keyer_event(int gpio, int level, uint32_t tick) {
|
||||||
|
// cw_active_state is for the logic level (active high vs low).
|
||||||
int state = (cw_active_state == 0) ? (level == 0) : (level != 0);
|
int state = (cw_active_state == 0) ? (level == 0) : (level != 0);
|
||||||
|
|
||||||
if (gpio == left_paddle_gpio)
|
if (gpio == left_paddle_gpio)
|
||||||
@ -210,46 +253,89 @@ void keyer_event(int gpio, int level, uint32_t tick) {
|
|||||||
else // RIGHT_PADDLE_GPIO
|
else // RIGHT_PADDLE_GPIO
|
||||||
kcwr = state;
|
kcwr = state;
|
||||||
|
|
||||||
if (state) // || cw_keyer_mode == KEYER_STRAIGHT)
|
/* Post (increment) the semaphore. Why? Because the keyer thread
|
||||||
|
* may be waiting on it, so this lets the keyer thread begin running
|
||||||
|
* the state machine if it was currently between sequences.
|
||||||
|
*/
|
||||||
|
if (state)
|
||||||
sem_post(&cw_event);
|
sem_post(&cw_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Added to support WiringPi, which uses a different type of callback.
|
/* KC4UPR: Helper function added to support WiringPi instead of PiGPIO.
|
||||||
void keyer_event_left()
|
* PiGPIO supports using the same callback for multiple pins, because
|
||||||
|
* the pin is one of the parameters to the interrupt handler. However,
|
||||||
|
* I needed to use WiringPi instead of PiGPIO due to PiGPIO not working
|
||||||
|
* with my soundcard "hat" for the Raspberry Pi.
|
||||||
|
*/
|
||||||
|
static 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)
|
||||||
|
/* KC4UPR: I added current value of the semaphore because I was
|
||||||
|
* curious as to how many times this ISR is getting called. For
|
||||||
|
* whatever reason, despite my debounce circuit on this pin,
|
||||||
|
* this is getting called a lot...
|
||||||
|
*/
|
||||||
sem_getvalue(&cw_event, &cw_event_value);
|
sem_getvalue(&cw_event, &cw_event_value);
|
||||||
fprintf(stdout, "left paddle pressed - %d\n", cw_event_value);
|
fprintf(stdout, "[GPIO Keyer] left paddle pressed; semaphore value: %d\n", cw_event_value);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Added to support WiringPi, which uses a different type of callback.
|
/* KC4UPR: Helper function added to support WiringPi instead of PiGPIO.
|
||||||
void keyer_event_right()
|
* PiGPIO supports using the same callback for multiple pins, because
|
||||||
|
* the pin is one of the parameters to the interrupt handler. However,
|
||||||
|
* I needed to use WiringPi instead of PiGPIO due to PiGPIO not working
|
||||||
|
* with my soundcard "hat" for the Raspberry Pi.
|
||||||
|
*/
|
||||||
|
static 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)
|
||||||
|
/* KC4UPR: I added current value of the semaphore because I was
|
||||||
|
* curious as to how many times this ISR is getting called. For
|
||||||
|
* whatever reason, despite my debounce circuit on this pin,
|
||||||
|
* this is getting called a lot...
|
||||||
|
*/
|
||||||
sem_getvalue(&cw_event, &cw_event_value);
|
sem_getvalue(&cw_event, &cw_event_value);
|
||||||
fprintf(stdout, "right paddle pressed - %d\n", cw_event_value);
|
fprintf(stdout, "[GPIO Keyer] right paddle pressed; semaphore value: %d\n", cw_event_value);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_memory() {
|
/* KC4UPR: Clear the dot and dash memory of the iambic keyer.
|
||||||
|
*/
|
||||||
|
static void clear_memory() {
|
||||||
dot_memory = 0;
|
dot_memory = 0;
|
||||||
dash_memory = 0;
|
dash_memory = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Set the keyer output (which will be read by the function
|
||||||
|
* is_key_down_gpiokeyer(), when called by Quisk). Also writes the
|
||||||
|
* digital output GPIO pins, if they are being used.
|
||||||
|
*/
|
||||||
static void set_keyer_out(int state) {
|
static void set_keyer_out(int state) {
|
||||||
if (state && tr_switch_gpio) {
|
/* First, write to the hardware T/R switch, if we are using it, and
|
||||||
|
* if the specific keyer state is key_down. A logical AND is used
|
||||||
|
* here instead of bitwise, because tr_switch_gpio could have
|
||||||
|
* multiple valid values.
|
||||||
|
*/
|
||||||
|
if (state && tr_switch_gpio)
|
||||||
digitalWrite(tr_switch_gpio, 1 & cw_keyer_enabled);
|
digitalWrite(tr_switch_gpio, 1 & cw_keyer_enabled);
|
||||||
}
|
|
||||||
|
/* Set the key state. This is what will be read by Quisk.
|
||||||
|
*/
|
||||||
keyer_out = state;
|
keyer_out = state;
|
||||||
digitalWrite(keyer_out_gpio, keyer_out & cw_keyer_enabled);
|
|
||||||
|
/* Finally, write to the hardware CW key line, if it was specified.
|
||||||
|
*/
|
||||||
|
if (keyer_out_gpio)
|
||||||
|
digitalWrite(keyer_out_gpio, keyer_out & cw_keyer_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Keyer thread, which processes the key state and moves
|
||||||
|
* through the state machines.
|
||||||
|
*/
|
||||||
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
|
||||||
@ -257,13 +343,22 @@ static void* keyer_thread(void *arg) {
|
|||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
|
/* KC4UPR: Writing out the current semaphore value because
|
||||||
|
* I was curious at the impact of bouncing on how often the
|
||||||
|
* interrupt handler was getting called.
|
||||||
|
*/
|
||||||
sem_getvalue(&cw_event, &cw_event_value);
|
sem_getvalue(&cw_event, &cw_event_value);
|
||||||
fprintf(stdout, "waiting - %d\n", cw_event_value);
|
fprintf(stdout, "[GPIO Keyer] waiting; semaphore value: %d\n", cw_event_value);
|
||||||
#endif
|
#endif
|
||||||
sem_wait(&cw_event);
|
sem_wait(&cw_event);
|
||||||
key_state = CHECK;
|
key_state = CHECK;
|
||||||
|
|
||||||
while (key_state != EXITLOOP) {
|
while (key_state != EXITLOOP) {
|
||||||
|
/* Anytime the keyer output is set (i.e. we are transmitting
|
||||||
|
* a symbol), reset the elapsed hangtime. Once we complete
|
||||||
|
* sending the current symbol (dit/dah/straight key out), we
|
||||||
|
* will start the hangtime counter for the T/R switch.
|
||||||
|
*/
|
||||||
if (keyer_out)
|
if (keyer_out)
|
||||||
hangtime_elapsed = 0;
|
hangtime_elapsed = 0;
|
||||||
|
|
||||||
@ -274,13 +369,21 @@ static void* keyer_thread(void *arg) {
|
|||||||
if (tr_switch_gpio) {
|
if (tr_switch_gpio) {
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
sem_getvalue(&cw_event, &cw_event_value);
|
sem_getvalue(&cw_event, &cw_event_value);
|
||||||
fprintf(stdout, "hangtime complete, %d msec - %d\n", cw_hangtime_msec, cw_event_value);
|
fprintf(stdout, "[GPIO Keyer] hangtime complete, %d msec; semaphore value: %d\n", cw_hangtime_msec, cw_event_value);
|
||||||
#endif
|
#endif
|
||||||
digitalWrite(tr_switch_gpio, 0);
|
digitalWrite(tr_switch_gpio, 0);
|
||||||
key_state = EXITLOOP;
|
key_state = EXITLOOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// INTENTIONALLY FALLING THROUGH TO 'CHECK' STATE (no 'break')
|
/* KC4UPR: The HANGTIME state is intentionally first in
|
||||||
|
* the switch() statement, and it intentionally does NOT
|
||||||
|
* have a break at the end of it. I want it to fall-
|
||||||
|
* through into the CHECK state logic, so it can look to
|
||||||
|
* see if there are any switch actuations, which would
|
||||||
|
* then interrupt the HANGTIME state and start the state
|
||||||
|
* machine over. This is probably terrible coding
|
||||||
|
* practice...
|
||||||
|
*/
|
||||||
|
|
||||||
case CHECK: // check for key press
|
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
|
||||||
@ -296,8 +399,6 @@ static void* keyer_thread(void *arg) {
|
|||||||
if (keyer_out)
|
if (keyer_out)
|
||||||
set_keyer_out(0);
|
set_keyer_out(0);
|
||||||
} else {
|
} else {
|
||||||
// NOTE: not working right
|
|
||||||
//set_keyer_out(0);
|
|
||||||
key_state = EXITLOOP;
|
key_state = EXITLOOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,8 +408,6 @@ static void* keyer_thread(void *arg) {
|
|||||||
else if (*kdash)
|
else if (*kdash)
|
||||||
key_state = PREDASH;
|
key_state = PREDASH;
|
||||||
else if (key_state != HANGTIME) {
|
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;
|
key_state = EXITLOOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -449,6 +548,11 @@ static void* keyer_thread(void *arg) {
|
|||||||
key_state = EXITLOOP;
|
key_state = EXITLOOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If the key out state is 0 (not sending), increment the
|
||||||
|
* hangtime elapsed timer. This will continue incrementing
|
||||||
|
* while we are in the HANGTIME state, until either the
|
||||||
|
* timer completes, or keyer out goes to 1.
|
||||||
|
*/
|
||||||
if (!keyer_out)
|
if (!keyer_out)
|
||||||
hangtime_elapsed++;
|
hangtime_elapsed++;
|
||||||
|
|
||||||
@ -471,41 +575,87 @@ static void* keyer_thread(void *arg) {
|
|||||||
return arg; // did this to ditch compiler warnings... bad idea?
|
return arg; // did this to ditch compiler warnings... bad idea?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* is_key_down.c functions
|
||||||
|
*
|
||||||
|
* These functions are called from is_key_down.c, and implement the
|
||||||
|
* method-specific open/close/set functions for the keyer.
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
/* KC4UPR: Open the GPIO keyer. Its name must start with "gpio". The
|
||||||
|
* overall format is:
|
||||||
|
* gpio:left,right,keyer_out,tr_switch
|
||||||
|
* where:
|
||||||
|
* left - GPIO input pin for the left paddle
|
||||||
|
* right - GPIO input pin for the right paddle
|
||||||
|
* keyer_out - GPIO output pin for the key (actual CW) signal
|
||||||
|
* (unlike the tr_switch line, this is only keyed
|
||||||
|
* for the actual dits and dahs)
|
||||||
|
* (set to zero to not use this)
|
||||||
|
* tr_switch - GPIO output pin for T/R switching (this is
|
||||||
|
* enabled any time keyer_out is enabled, plus a
|
||||||
|
* short, configurable delay after the last time
|
||||||
|
* the keyer output was set)
|
||||||
|
* (set to zero to not use this)
|
||||||
|
* NOTE: The pin numbers to be used are GPIO pin numbers, NOT WiringPi
|
||||||
|
* pin numbers even though WiringPi is used!!!
|
||||||
|
*/
|
||||||
int open_key_gpiokeyer(const char * name)
|
int open_key_gpiokeyer(const char * name)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* KC4UPR: Read the GPIO keyer name, and parse out the pins for
|
||||||
|
* left paddle, right paddle, keyer out, and T/R switch. The last
|
||||||
|
* two can be zero if that functionality is not desired, but they
|
||||||
|
* must be present in the name string.
|
||||||
|
*/
|
||||||
if (sscanf(name, "gpio:%d,%d,%d,%d", &left_paddle_gpio, &right_paddle_gpio, &keyer_out_gpio, &tr_switch_gpio) < 4) {
|
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);
|
fprintf(stderr, "[GPIO Keyer] insufficient parameters: %s\n", name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(DEBUG)
|
#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",
|
fprintf(stdout, "[GPIO Keyer] configuration:\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);
|
left_paddle_gpio, right_paddle_gpio, keyer_out_gpio, tr_switch_gpio);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* KC4UPR: Setup WiringPi. Note that I have specified using the
|
||||||
|
* GPIO pin names rather than the WiringPi pin names.
|
||||||
|
*/
|
||||||
if (wiringPiSetupGpio () < 0) {
|
if (wiringPiSetupGpio () < 0) {
|
||||||
fprintf(stderr, "Unable to setup wiringPi: %s\n", strerror (errno));
|
fprintf(stderr, "[GPIO Keyer] unable to setup wiringPi: %s\n", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Setup the right paddle pin for input. An interrupt will
|
||||||
|
* be triggered on both key down and key up.
|
||||||
|
*/
|
||||||
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);
|
||||||
|
|
||||||
|
/* KC4UPR: Setup the left paddle pin for input. An interrupt will
|
||||||
|
* be triggered on both key down and key up.
|
||||||
|
*/
|
||||||
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);
|
||||||
|
|
||||||
|
/* KC4UPR: Setup the keyer output pin as an output pin, IF the
|
||||||
|
* value is not 0. If the pin value is 0, then this output is
|
||||||
|
* disabled.
|
||||||
|
*/
|
||||||
if (keyer_out_gpio) {
|
if (keyer_out_gpio) {
|
||||||
pinMode(keyer_out_gpio, OUTPUT);
|
pinMode(keyer_out_gpio, OUTPUT);
|
||||||
digitalWrite(keyer_out_gpio, 0);
|
digitalWrite(keyer_out_gpio, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Setup the T/R switch pin as an output pin, IF the
|
||||||
|
* value is not 0. If the pin value is 0, then this output is
|
||||||
|
* disabled.
|
||||||
|
*/
|
||||||
if (tr_switch_gpio) {
|
if (tr_switch_gpio) {
|
||||||
pinMode(tr_switch_gpio, OUTPUT);
|
pinMode(tr_switch_gpio, OUTPUT);
|
||||||
digitalWrite(tr_switch_gpio, 0);
|
digitalWrite(tr_switch_gpio, 0);
|
||||||
@ -517,17 +667,26 @@ int open_key_gpiokeyer(const char * name)
|
|||||||
running = 1;
|
running = 1;
|
||||||
i |= pthread_create(&keyer_thread_id, NULL, keyer_thread, NULL);
|
i |= pthread_create(&keyer_thread_id, NULL, keyer_thread, NULL);
|
||||||
if(i < 0) {
|
if(i < 0) {
|
||||||
fprintf(stderr,"pthread_create for keyer_thread failed %d\n", i);
|
fprintf(stderr,"[GPIO Keyer] pthread_create for keyer_thread failed %d\n", i);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Close the GPIO keyer. This sets the running flag for the
|
||||||
|
* keyer thread to false, posts the semaphore (to ensure the keyer
|
||||||
|
* thread gets off of a semaphore wait), and then joins the thread to
|
||||||
|
* close it.
|
||||||
|
*
|
||||||
|
* NOTE: If Quisk is not closed after closing the GPIO keyer, the
|
||||||
|
* interrupt handlers for the GPIO pins remain present. I think I can
|
||||||
|
* possibly fix this by assigning a NULL ISR???
|
||||||
|
*/
|
||||||
void close_key_gpiokeyer(void)
|
void close_key_gpiokeyer(void)
|
||||||
{
|
{
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
fprintf(stdout, "[GPIO Keyer] closing key\n");
|
fprintf(stdout, "[GPIO Keyer] closing keyer\n");
|
||||||
#endif
|
#endif
|
||||||
running = 0;
|
running = 0;
|
||||||
sem_post(&cw_event);
|
sem_post(&cw_event);
|
||||||
@ -538,6 +697,9 @@ void close_key_gpiokeyer(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Return the state of the keyer. This is a logic AND of the
|
||||||
|
* current keyer_out state, and the cw_keyer_enabled signal.
|
||||||
|
*/
|
||||||
int is_key_down_gpiokeyer(void)
|
int is_key_down_gpiokeyer(void)
|
||||||
{
|
{
|
||||||
static int retval;
|
static int retval;
|
||||||
@ -545,48 +707,94 @@ int is_key_down_gpiokeyer(void)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* Externally callable functions
|
||||||
|
*
|
||||||
|
* These functions are callable from Quisk proper, as well as from any
|
||||||
|
* Quisk Python modules. They are declared in quisk.h, and are used to
|
||||||
|
* (re-)configure various keyer parameters.
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
/* KC4UPR: Set the keyer mode. Keyer modes are defined as an
|
||||||
|
* enumeration in this file. Current enums are:
|
||||||
|
* KEYER_STRAIGHT = 0
|
||||||
|
* KEYER_MODE_A = 1
|
||||||
|
* KEYER_MODE_B = 2
|
||||||
|
* If the requested mode is out of bounds, nothing happens...
|
||||||
|
*/
|
||||||
void quisk_set_gpio_keyer_mode(int mode)
|
void quisk_set_gpio_keyer_mode(int mode)
|
||||||
{
|
{
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
fprintf(stdout, "MODE CHANGE\n");
|
fprintf(stdout, "[GPIO Keyer] mode change: %d - %s\n", mode,
|
||||||
|
((mode>-1)&&(mode<NUM_KEYER_MODES))?"success":"failed ");
|
||||||
#endif
|
#endif
|
||||||
if ((mode > -1) && (mode < NUM_KEYER_MODES))
|
if ((mode > -1) && (mode < NUM_KEYER_MODES))
|
||||||
cw_keyer_mode = mode;
|
cw_keyer_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Set the keyer speed in WPM. Valid from 1-60 WPM.
|
||||||
|
*/
|
||||||
void quisk_set_gpio_keyer_speed(int wpm)
|
void quisk_set_gpio_keyer_speed(int wpm)
|
||||||
{
|
{
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
fprintf(stdout, "SPEED CHANGE\n");
|
fprintf(stdout, "[GPIO Keyer] speed change: %d - %s\n", wpm,
|
||||||
|
((wpm>0)&&(wpm<61))?"success":"failed ");
|
||||||
#endif
|
#endif
|
||||||
cw_keyer_speed = wpm;
|
if ((wpm > 0) && (wpm < 61)) {
|
||||||
keyer_update();
|
cw_keyer_speed = wpm;
|
||||||
|
keyer_update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Set the keyer weight in percent (?). Valid from 33-66.
|
||||||
|
*/
|
||||||
void quisk_set_gpio_keyer_weight(int weight)
|
void quisk_set_gpio_keyer_weight(int weight)
|
||||||
{
|
{
|
||||||
if ((weight > 32) && (weight < 67))
|
#if defined(DEBUG)
|
||||||
|
fprintf(stdout, "[GPIO Keyer] weight change: %d - %s\n", weight,
|
||||||
|
((weight>32)&&(weight<67))?"success":"failed ");
|
||||||
|
#endif
|
||||||
|
if ((weight > 32) && (weight < 67)) {
|
||||||
cw_keyer_weight = weight;
|
cw_keyer_weight = weight;
|
||||||
keyer_update();
|
keyer_update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Reverse the paddles. Default: left = dit, right = dah.
|
||||||
|
*/
|
||||||
void quisk_set_gpio_keyer_reversed(int flag)
|
void quisk_set_gpio_keyer_reversed(int flag)
|
||||||
{
|
{
|
||||||
|
#if defined(DEBUG)
|
||||||
|
fprintf(stdout, "[GPIO Keyer] reverse paddles: %s\n",
|
||||||
|
flag==0?"false":"true ");
|
||||||
|
#endif
|
||||||
cw_keys_reversed = (flag == 0 ? 0 : 1);
|
cw_keys_reversed = (flag == 0 ? 0 : 1);
|
||||||
keyer_update();
|
keyer_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Set strict character spacing. Default: off.
|
||||||
|
*/
|
||||||
void quisk_set_gpio_keyer_strict(int flag)
|
void quisk_set_gpio_keyer_strict(int flag)
|
||||||
{
|
{
|
||||||
|
#if defined(DEBUG)
|
||||||
|
fprintf(stdout, "[GPIO Keyer] strict spacing: %s\n",
|
||||||
|
flag==0?"false":"true ");
|
||||||
|
#endif
|
||||||
cw_keyer_spacing = (flag == 0 ? 0 : 1);
|
cw_keyer_spacing = (flag == 0 ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KC4UPR: Enabled/disable the keyer. If true, then regardless of
|
||||||
|
* state, nothing will be output.
|
||||||
|
*/
|
||||||
void quisk_set_gpio_keyer_enabled(int flag)
|
void quisk_set_gpio_keyer_enabled(int flag)
|
||||||
{
|
{
|
||||||
|
#if defined(DEBUG)
|
||||||
|
fprintf(stdout, "[GPIO Keyer] enabled: %s\n",
|
||||||
|
flag==0?"false":"true ");
|
||||||
|
#endif
|
||||||
cw_keyer_enabled = (flag == 0 ? 0 : 1);
|
cw_keyer_enabled = (flag == 0 ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* EOF
|
* EOF
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
//#endif
|
|
||||||
|
@ -109,7 +109,7 @@ int quisk_open_key(const char * name)
|
|||||||
}
|
}
|
||||||
#if defined(ENABLE_GPIO_KEYER)
|
#if defined(ENABLE_GPIO_KEYER)
|
||||||
/* KC4UPR: Check if the GPIO Keyer was requested, and if so, open
|
/* KC4UPR: Check if the GPIO Keyer was requested, and if so, open
|
||||||
* it. It's name must start with "gpio". The overall format is:
|
* it. Its name must start with "gpio". The overall format is:
|
||||||
* gpio:left,right,keyer_out,tr_switch
|
* gpio:left,right,keyer_out,tr_switch
|
||||||
* where:
|
* where:
|
||||||
* left - GPIO input pin for the left paddle
|
* left - GPIO input pin for the left paddle
|
||||||
@ -123,6 +123,8 @@ int quisk_open_key(const char * name)
|
|||||||
* short, configurable delay after the last time
|
* short, configurable delay after the last time
|
||||||
* the keyer output was set)
|
* the keyer output was set)
|
||||||
* (set to zero to not use this)
|
* (set to zero to not use this)
|
||||||
|
* NOTE: The pin numbers to be used are GPIO pin numbers, NOT
|
||||||
|
* WiringPi pin numbers even though WiringPi is used!!!
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user