The sketch is rather large, so I've attempted to distill it into the meaningful bits.
#include <QueueArray.h>
#include <TimerThree.h>
#include "p2k.h"
void setup()
{
// Initialize interrupts at 500us intervals
Timer3.initialize(500);
Serial.begin(115200);
pinMode(13, OUTPUT);
DDRA = B11111111;
DDRC = B11111111;
boot_time = millis();
// We have not processed our HELLO packet yet, so disable all high power drives
hello_processed = false;
}
// ISR
volatile unsigned long _isr_blink_ctr = 0;
volatile unsigned int _isr_led_state = LOW;
void ISR_rt_tasks(void) {
unsigned long isr_begin = micros();
// Increment interrupt counter here
// Check to see if its time to tick the solenoids, switches or lamps.
switches_handler(); // Defined this huge function down at the bottom of the post
isr_time = micros() - isr_begin;
}
// Main loop
void loop()
{
// c will hold our incoming command object
struct P2KCommand c;
// If we have data on the serial line, read it
if (Serial.available() > 0)
{
// Only parse the command after we've received a full P2KCommand object
if (StreamSend::receiveObject(Serial, &c, sizeof(struct P2KCommand)) == GOOD_PACKET)
{
if (_led_state == LOW) _led_state = HIGH;
else _led_state = LOW;
digitalWrite(13, _led_state);
if (c.opcode == CMD_HELLO)
{
// Once we receive a HELLO command from the host, boot the driver board
// and enable the high power relay.
hello_processed = true;
init_driverboard();
schedule_driver((byte)DRIVER_SOLENOID, (byte)HEALTH_LED, 0xFF00FF00, (byte)0);
last_heartbeat = millis();
schedule_driver((byte)DRIVER_LAMP, (byte)LAMP_START_BUTTON, 0xFFFF0000, (byte)0);
enable_lamp((byte)LAMP_COINDOOR, 1);
Timer3.attachInterrupt(ISR_rt_tasks);
outbound_command.int1 = 0;
outbound_command.int2 = 0;
outbound_command.opcode = CMD_HELLO;
outbound_command.int3 = 0;
outbound_command.int4 = 0;
send_msg(outbound_command);
}
else if (c.opcode == CMD_HEARTBEAT)
{
_start_btn_state = !_start_btn_state;
// Update the heartbeat time so that our emulated software watchdog stays alert
last_heartbeat = millis();
//schedule_driver((byte)DRIVER_LAMP, (byte)LAMP_START_BUTTON, 0xFF00FF00, (byte)0);
outbound_command.int1 = 0;
outbound_command.int2 = 0;
outbound_command.opcode = CMD_HEARTBEAT;
outbound_command.int3 = 0;
outbound_command.int4 = isr_time;
send_msg(outbound_command);
}
}
}
// Check if we have messages in the Queue
_evtq_process_ctr = (_evtq_process_ctr + 1) % 100;
// Only do this once every few thousand cycles otherwise our interrupt handling code might get crazy.
// This snags the junk from the ring buffer written by the ISR and shoots it out over the serial line
if (_evtq_process_ctr == 0) {
if (!eventq.isEmpty()) {
P2KCommand c = eventq.dequeue();
c.int4 = isr_time;
if (millis() - boot_time >= BURST_IGNORE_TIME)
send_msg(c);
}
}
// If we have not processed a HELLO from the host, don't do any other driver board stuff.
if (hello_processed == false) return;
// Has the watchdog timed out?
if (millis() - last_heartbeat >= WATCHDOG_TIMEOUT)
{
// Blank out everything.
p->send_data(REGISTER_SOLENOID_D, 0x0);
p->send_data(REGISTER_SOLENOID_A, 0x0);
p->send_data(REGISTER_SOLENOID_B, 0x0);
p->send_data(REGISTER_SOLENOID_C, 0x0);
p->send_data(REGISTER_SOLENOID_FLIPPER, 0x0);
p->send_data(REGISTER_SOLENOID_LOGIC, 0x0);
hello_processed = false;
Timer3.detachInterrupt();
digitalWrite(13, LOW);
}
}
void switches_handler()
{
byte direct_switch_banks[] = {
REGISTER_SWITCH_FLIPPER, REGISTER_SWITCH_MISC, REGISTER_SWITCH_COIN, REGISTER_SWITCH_ZERO,
REGISTER_SWITCH_DIP, REGISTER_LAMP_A_DIAGNOSTIC, REGISTER_LAMP_B_DIAGNOSTIC,
REGISTER_FUSE_A_DIAGNOSTIC, REGISTER_FUST_B_DIAGNOSTIC };
sw_col_val = (1 << sw_col);
p->send_data(REGISTER_SWITCH_COLUMN, sw_col_val);
sw_data = p->receive_data(REGISTER_SWITCH_ROW);
for (sw_row = 0; sw_row < 8; sw_row++)
{
sw_bit = 1 << sw_row;
sw_sw = (sw_col << 4) + sw_row;
// Is the change bit set for this and is the switch state the same as the last time, then fire a debounce event
// and unset the change bit
if ((sw_bit & switch_matrix_changing[sw_col]) && (sw_bit & sw_data) == (sw_bit & switch_matrix[sw_col]))
{
switch_matrix_changing[sw_col] -= sw_bit;
broadcast_event(SWITCH_MATRIX, sw_sw, (sw_bit & sw_data) != 0, 1);
}
// The state has changed
// If the change bit is set for this, and the state has changed, unset the change bit
// If the change bit is set for this, and the state has NOT changed, fire a debounce event
// If the change bit is not set for this, set the change bit
if ((sw_bit & sw_data) != (sw_bit & switch_matrix[sw_col]))
{
// Is the change bit set? The state has also changed
if ((sw_bit & switch_matrix_changing[sw_col]))
{
// Unset the change bit
switch_matrix_changing[sw_col] -= sw_bit;
}
else
{
// Set the change bit
switch_matrix_changing[sw_col] += sw_bit;
}
if ((sw_bit & sw_data) == 0) switch_matrix[sw_col] -= sw_bit;
else switch_matrix[sw_col] += sw_bit;
broadcast_event(SWITCH_MATRIX, sw_sw, (sw_bit & sw_data) != 0, 0);
}
}
// Direct switches -- Only check every few columns
if (sw_col % 2 == 0)
{
sw_bank = direct_switch_banks[sw_bank_cnt];
sw_data = p->receive_data(sw_bank);
// Check if any switches have changed since last time
for (sw_switches = 0; sw_switches < 8; sw_switches++)
{
sw_bit = 1 << sw_switches;
sw_sw = (sw_bank << 4) + sw_switches;
// Is the change bit set for this and is the switch state the same as the last time, then fire a debounce event
// and unset the change bit
if ((sw_bit & direct_switches_changing[sw_bank]) && (sw_bit & sw_data) == (sw_bit & direct_switches[sw_bank]))
{
direct_switches_changing[sw_bank] -= sw_bit;
broadcast_event(SWITCH_DIRECT, sw_sw, (sw_bit & sw_data) != 0, 1);
}
if ((sw_bit & sw_data) != (sw_bit & direct_switches[sw_bank]))
{
// Is the change bit set? The state has also changed
if ((sw_bit & direct_switches_changing[sw_bank]))
{
// Unset the change bit
direct_switches_changing[sw_bank] -= sw_bit;
}
else
{
// Set the change bit
direct_switches_changing[sw_bank] += sw_bit;
}
if ((sw_bit & sw_data) == 0) direct_switches[sw_bank] -= sw_bit;
else direct_switches[sw_bank] += sw_bit;
broadcast_event(SWITCH_DIRECT, sw_sw, (sw_bit & sw_data) != 0, 0);
}
}
sw_bank_cnt = ++sw_bank_cnt % 2;
}
sw_col = ++sw_col % 8;
}