|
|
|
@ -74,6 +74,9 @@
|
|
|
|
|
|
|
|
|
|
#define OPERACAKE_MAX_BOARDS 8
|
|
|
|
|
|
|
|
|
|
#define INVALID_RANGE 0xFF;
|
|
|
|
|
static uint8_t current_range = INVALID_RANGE;
|
|
|
|
|
|
|
|
|
|
i2c_bus_t* const oc_bus = &i2c0;
|
|
|
|
|
|
|
|
|
|
enum operacake_switching_mode {
|
|
|
|
@ -151,38 +154,6 @@ void operacake_get_boards(uint8_t *addresses) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operacake_set_mode(uint8_t address, uint8_t mode) {
|
|
|
|
|
if (address >= OPERACAKE_MAX_BOARDS)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
operacake_boards[address].mode = mode;
|
|
|
|
|
|
|
|
|
|
if (mode == MODE_TIME) {
|
|
|
|
|
// Switch Opera Cake to pin-control mode
|
|
|
|
|
uint8_t config_pins = (uint8_t)~(OPERACAKE_PIN_OE(1) | OPERACAKE_PIN_LEDEN(1) | OPERACAKE_PIN_LEDEN2(1));
|
|
|
|
|
operacake_write_reg(oc_bus, address, OPERACAKE_REG_CONFIG, config_pins);
|
|
|
|
|
operacake_write_reg(oc_bus, address, OPERACAKE_REG_OUTPUT, OPERACAKE_GPIO_ENABLE | OPERACAKE_EN_LEDS);
|
|
|
|
|
} else {
|
|
|
|
|
operacake_write_reg(oc_bus, address, OPERACAKE_REG_CONFIG, OPERACAKE_CONFIG_ALL_OUTPUT);
|
|
|
|
|
operacake_set_ports(address, operacake_boards[address].PA, operacake_boards[address].PB);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If any boards are in MODE_TIME, enable the sctimer events.
|
|
|
|
|
bool enable_sctimer = false;
|
|
|
|
|
for (int i = 0; i < OPERACAKE_MAX_BOARDS; i++) {
|
|
|
|
|
if (operacake_boards[i].mode == MODE_TIME)
|
|
|
|
|
enable_sctimer = true;
|
|
|
|
|
}
|
|
|
|
|
operacake_sctimer_enable(enable_sctimer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t operacake_get_mode(uint8_t address) {
|
|
|
|
|
if (address >= OPERACAKE_MAX_BOARDS)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return operacake_boards[address].mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t port_to_pins(uint8_t port) {
|
|
|
|
|
switch(port) {
|
|
|
|
|
case OPERACAKE_PA1:
|
|
|
|
@ -206,46 +177,98 @@ uint8_t port_to_pins(uint8_t port) {
|
|
|
|
|
return 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t operacake_set_ports(uint8_t address, uint8_t PA, uint8_t PB) {
|
|
|
|
|
/*
|
|
|
|
|
* Issue I2C command to activate ports.
|
|
|
|
|
*/
|
|
|
|
|
uint8_t operacake_activate_ports(uint8_t address, uint8_t PA, uint8_t PB)
|
|
|
|
|
{
|
|
|
|
|
uint8_t side, pa, pb, reg;
|
|
|
|
|
/* Start with some error checking,
|
|
|
|
|
* which should have been done either
|
|
|
|
|
* on the host or elsewhere in firmware
|
|
|
|
|
*/
|
|
|
|
|
if((PA > OPERACAKE_PB4) || (PB > OPERACAKE_PB4)) {
|
|
|
|
|
/* Ensure PA and PB are within the valid range. */
|
|
|
|
|
if ((PA > OPERACAKE_PB4) || (PB > OPERACAKE_PB4)) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
/* Check which side PA and PB are on */
|
|
|
|
|
if(((PA <= OPERACAKE_PA4) && (PB <= OPERACAKE_PA4))
|
|
|
|
|
|| ((PA > OPERACAKE_PA4) && (PB > OPERACAKE_PA4))) {
|
|
|
|
|
/* Ensure PA and PB are on opposite sides. */
|
|
|
|
|
if (((PA <= OPERACAKE_PA4) && (PB <= OPERACAKE_PA4))
|
|
|
|
|
|| ((PA > OPERACAKE_PA4) && (PB > OPERACAKE_PA4))) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(PA > OPERACAKE_PA4) {
|
|
|
|
|
if (PA > OPERACAKE_PA4) {
|
|
|
|
|
side = OPERACAKE_CROSSOVER;
|
|
|
|
|
} else {
|
|
|
|
|
side = OPERACAKE_SAMESIDE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa = port_to_pins(PA);
|
|
|
|
|
pb = port_to_pins(PB);
|
|
|
|
|
|
|
|
|
|
reg = (OPERACAKE_GPIO_DISABLE | side | pa | pb | OPERACAKE_EN_LEDS);
|
|
|
|
|
operacake_write_reg(oc_bus, address, OPERACAKE_REG_OUTPUT, reg);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operacake_set_mode(uint8_t address, uint8_t mode) {
|
|
|
|
|
if (address >= OPERACAKE_MAX_BOARDS)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
operacake_boards[address].mode = mode;
|
|
|
|
|
current_range = INVALID_RANGE;
|
|
|
|
|
|
|
|
|
|
if (mode == MODE_TIME) {
|
|
|
|
|
// Switch Opera Cake to pin-control mode
|
|
|
|
|
uint8_t config_pins = (uint8_t)~(OPERACAKE_PIN_OE(1) | OPERACAKE_PIN_LEDEN(1) | OPERACAKE_PIN_LEDEN2(1));
|
|
|
|
|
operacake_write_reg(oc_bus, address, OPERACAKE_REG_CONFIG, config_pins);
|
|
|
|
|
operacake_write_reg(oc_bus, address, OPERACAKE_REG_OUTPUT, OPERACAKE_GPIO_ENABLE | OPERACAKE_EN_LEDS);
|
|
|
|
|
} else {
|
|
|
|
|
operacake_write_reg(oc_bus, address, OPERACAKE_REG_CONFIG, OPERACAKE_CONFIG_ALL_OUTPUT);
|
|
|
|
|
operacake_activate_ports(address, operacake_boards[address].PA, operacake_boards[address].PB);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If any boards are in MODE_TIME, enable the sctimer events.
|
|
|
|
|
bool enable_sctimer = false;
|
|
|
|
|
for (int i = 0; i < OPERACAKE_MAX_BOARDS; i++) {
|
|
|
|
|
if (operacake_boards[i].mode == MODE_TIME)
|
|
|
|
|
enable_sctimer = true;
|
|
|
|
|
}
|
|
|
|
|
operacake_sctimer_enable(enable_sctimer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t operacake_get_mode(uint8_t address) {
|
|
|
|
|
if (address >= OPERACAKE_MAX_BOARDS)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return operacake_boards[address].mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set manual mode ports.
|
|
|
|
|
*/
|
|
|
|
|
uint8_t operacake_set_ports(uint8_t address, uint8_t PA, uint8_t PB)
|
|
|
|
|
{
|
|
|
|
|
/* Ensure PA and PB are within the valid range. */
|
|
|
|
|
if ((PA > OPERACAKE_PB4) || (PB > OPERACAKE_PB4)) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
/* Ensure PA and PB are on opposite sides. */
|
|
|
|
|
if (((PA <= OPERACAKE_PA4) && (PB <= OPERACAKE_PA4))
|
|
|
|
|
|| ((PA > OPERACAKE_PA4) && (PB > OPERACAKE_PA4))) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Keep track of manual settings for when we switch in/out of time/frequency modes.
|
|
|
|
|
operacake_boards[address].PA = PA;
|
|
|
|
|
operacake_boards[address].PB = PB;
|
|
|
|
|
|
|
|
|
|
// Only apply register settings if the board is in manual mode.
|
|
|
|
|
if (operacake_boards[address].mode != MODE_MANUAL)
|
|
|
|
|
return 0;
|
|
|
|
|
// Immediately apply register settings if the board is in manual mode.
|
|
|
|
|
if (operacake_boards[address].mode == MODE_MANUAL) {
|
|
|
|
|
return operacake_activate_ports(address, PA, PB);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa = port_to_pins(PA);
|
|
|
|
|
pb = port_to_pins(PB);
|
|
|
|
|
|
|
|
|
|
reg = (OPERACAKE_GPIO_DISABLE | side
|
|
|
|
|
| pa | pb | OPERACAKE_EN_LEDS);
|
|
|
|
|
operacake_write_reg(oc_bus, address, OPERACAKE_REG_OUTPUT, reg);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Opera Glasses
|
|
|
|
|
* frequency mode aka "Opera Glasses"
|
|
|
|
|
*/
|
|
|
|
|
typedef struct {
|
|
|
|
|
uint16_t freq_min;
|
|
|
|
@ -264,18 +287,19 @@ uint8_t operacake_add_range(uint16_t freq_min, uint16_t freq_max, uint8_t port)
|
|
|
|
|
ranges[range_idx].freq_min = freq_min;
|
|
|
|
|
ranges[range_idx].freq_max = freq_max;
|
|
|
|
|
ranges[range_idx].portA = port;
|
|
|
|
|
ranges[range_idx].portB = 7;
|
|
|
|
|
if(port <= OPERACAKE_PA4) {
|
|
|
|
|
ranges[range_idx].portB = range_idx+4;
|
|
|
|
|
} else {
|
|
|
|
|
ranges[range_idx].portB = OPERACAKE_PA1;
|
|
|
|
|
}
|
|
|
|
|
/* Make the B port mirror the A port. */
|
|
|
|
|
ranges[range_idx].portB = (port + 4) % 8;
|
|
|
|
|
range_idx++;
|
|
|
|
|
current_range = INVALID_RANGE;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operacake_clear_ranges(void)
|
|
|
|
|
{
|
|
|
|
|
range_idx = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define FREQ_ONE_MHZ (1000000ull)
|
|
|
|
|
static uint8_t current_range = 0xFF;
|
|
|
|
|
|
|
|
|
|
uint8_t operacake_set_range(uint32_t freq_mhz) {
|
|
|
|
|
if(range_idx == 0) {
|
|
|
|
@ -289,13 +313,17 @@ uint8_t operacake_set_range(uint32_t freq_mhz) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Use the last range if there was no match. */
|
|
|
|
|
if (range == range_idx) {
|
|
|
|
|
range--;
|
|
|
|
|
}
|
|
|
|
|
if(range == current_range) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < OPERACAKE_MAX_BOARDS; i++) {
|
|
|
|
|
if (operacake_is_board_present(i) && operacake_get_mode(i) == MODE_FREQUENCY) {
|
|
|
|
|
operacake_set_ports(i, ranges[range].portA, ranges[range].portB);
|
|
|
|
|
operacake_activate_ports(i, ranges[range].portA, ranges[range].portB);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|