// Copyright 2012 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
#include
#include
#include "avrlib/adc.h"
#include "avrlib/boot.h"
#include "avrlib/gpio.h"
#include "avrlib/watchdog_timer.h"
using namespace avrlib;
enum LedState {
LED_STATE_OFF,
LED_STATE_RED,
LED_STATE_GREEN
};
Gpio in_1;
Gpio out_1_a;
Gpio out_1_b;
Gpio led_1_a;
Gpio led_1_k;
Gpio in_2;
Gpio out_2_a;
Gpio out_2_b;
Gpio led_2_a;
Gpio led_2_k;
Gpio switch_2;
Gpio switch_1;
Adc adc;
const uint16_t kLongPressTime = 6250; // 800 * 8000 / 1024
const uint16_t kLedGateDelay = 0x1ff;
static uint8_t adc_channel;
static uint16_t p[2];
bool toggle_mode[2];
bool latch_mode[2];
bool input_state[2];
bool previous_state[2];
bool switch_state[2];
bool inhibit_switch[2];
uint16_t press_time[2];
uint8_t led_state[2];
uint16_t led_gate_delay[2];
uint32_t rng_state;
const prog_uint16_t linear_table[] PROGMEM = {
0, 0, 259, 518, 777, 1036, 1295, 1554,
1813, 2072, 2331, 2590, 2849, 3108, 3367, 3626,
3885, 4145, 4404, 4663, 4922, 5181, 5440, 5699,
5958, 6217, 6476, 6735, 6994, 7253, 7512, 7771,
8030, 8289, 8548, 8807, 9066, 9325, 9584, 9843,
10102, 10361, 10620, 10879, 11138, 11397, 11656, 11915,
12174, 12434, 12693, 12952, 13211, 13470, 13729, 13988,
14247, 14506, 14765, 15024, 15283, 15542, 15801, 16060,
16319, 16578, 16837, 17096, 17355, 17614, 17873, 18132,
18391, 18650, 18909, 19168, 19427, 19686, 19945, 20204,
20463, 20723, 20982, 21241, 21500, 21759, 22018, 22277,
22536, 22795, 23054, 23313, 23572, 23831, 24090, 24349,
24608, 24867, 25126, 25385, 25644, 25903, 26162, 26421,
26680, 26939, 27198, 27457, 27716, 27975, 28234, 28493,
28753, 29012, 29271, 29530, 29789, 30048, 30307, 30566,
30825, 31084, 31343, 31602, 31861, 32120, 32379, 32638,
32897, 33156, 33415, 33674, 33933, 34192, 34451, 34710,
34969, 35228, 35487, 35746, 36005, 36264, 36523, 36782,
37042, 37301, 37560, 37819, 38078, 38337, 38596, 38855,
39114, 39373, 39632, 39891, 40150, 40409, 40668, 40927,
41186, 41445, 41704, 41963, 42222, 42481, 42740, 42999,
43258, 43517, 43776, 44035, 44294, 44553, 44812, 45072,
45331, 45590, 45849, 46108, 46367, 46626, 46885, 47144,
47403, 47662, 47921, 48180, 48439, 48698, 48957, 49216,
49475, 49734, 49993, 50252, 50511, 50770, 51029, 51288,
51547, 51806, 52065, 52324, 52583, 52842, 53101, 53361,
53620, 53879, 54138, 54397, 54656, 54915, 55174, 55433,
55692, 55951, 56210, 56469, 56728, 56987, 57246, 57505,
57764, 58023, 58282, 58541, 58800, 59059, 59318, 59577,
59836, 60095, 60354, 60613, 60872, 61131, 61390, 61650,
61909, 62168, 62427, 62686, 62945, 63204, 63463, 63722,
63981, 64240, 64499, 64758, 65017, 65276, 65535, 65535,
};
void Init() {
Gpio::set_mode(DIGITAL_OUTPUT);
Gpio::Low();
in_1.set_mode(DIGITAL_INPUT);
in_2.set_mode(DIGITAL_INPUT);
in_1.High();
in_2.High();
switch_1.set_mode(DIGITAL_INPUT);
switch_2.set_mode(DIGITAL_INPUT);
switch_1.High();
switch_2.High();
out_1_a.set_mode(DIGITAL_OUTPUT);
out_1_b.set_mode(DIGITAL_OUTPUT);
led_1_a.set_mode(DIGITAL_OUTPUT);
led_1_k.set_mode(DIGITAL_OUTPUT);
out_2_a.set_mode(DIGITAL_OUTPUT);
out_2_b.set_mode(DIGITAL_OUTPUT);
led_2_a.set_mode(DIGITAL_OUTPUT);
led_2_k.set_mode(DIGITAL_OUTPUT);
led_1_a.Low();
led_2_a.Low();
led_1_k.Low();
led_2_k.Low();
adc.Init();
adc.set_reference(ADC_DEFAULT);
adc.set_alignment(ADC_LEFT_ALIGNED);
adc.StartConversion(0);
uint8_t configuration_byte = ~eeprom_read_byte((uint8_t*) 0);
toggle_mode[0] = configuration_byte & 1;
latch_mode[0] = configuration_byte & 2;
toggle_mode[1] = configuration_byte & 4;
latch_mode[1] = configuration_byte & 8;
led_state[0] = led_state[1] = 0;
switch_state[0] = switch_state[1] = false;
TCCR1A = 0;
TCCR1B = 5;
}
bool Read(uint8_t channel) {
return channel == 0 ? !in_1.value() : !in_2.value();
}
bool ReadSwitch(uint8_t channel) {
return channel == 0 ? !switch_1.value() : !switch_2.value();
}
void GateOn(uint8_t channel, bool outcome) {
if (channel == 0) {
if (outcome) {
out_1_a.Low();
out_1_b.High();
} else {
out_1_a.High();
out_1_b.Low();
}
} else {
if (outcome) {
out_2_a.Low();
out_2_b.High();
} else {
out_2_a.High();
out_2_b.Low();
}
}
}
void GateOff(uint8_t channel) {
if (channel == 0) {
out_1_a.Low();
out_1_b.Low();
} else {
out_2_a.Low();
out_2_b.Low();
}
}
void LedOff(uint8_t channel) {
if (channel == 0) {
led_1_a.Low();
led_1_k.Low();
} else {
led_2_a.Low();
led_2_k.Low();
}
}
void DisplayConfigurationAndSave(uint8_t channel) {
uint8_t configuration_byte = 0;
if (toggle_mode[0]) configuration_byte |= 1;
if (latch_mode[0]) configuration_byte |= 2;
if (toggle_mode[1]) configuration_byte |= 4;
if (latch_mode[1]) configuration_byte |= 8;
eeprom_write_byte((uint8_t*) 0, ~configuration_byte);
}
int main(void) {
ResetWatchdog();
Init();
input_state[0] = input_state[1] = false;
rng_state = 1;
while (1) {
// Whenever an ADC cycle is finished, update the probability variables.
if (adc.ready()) {
uint8_t channel_index = 1 - adc_channel; // ADC pins are swapped!
p[channel_index] = pgm_read_word(linear_table + adc.ReadOut8());
adc_channel = (adc_channel + 1) & 1;
adc.StartConversion(adc_channel);
}
// Scan switches
for (uint8_t i = 0; i < 2; ++i) {
bool new_input_state = ReadSwitch(i);
if (!switch_state[i] && new_input_state) {
press_time[i] = TCNT1;
inhibit_switch[i] = false;
}
if (switch_state[i] && !inhibit_switch[i]) {
uint16_t this_press_time = TCNT1 - press_time[i];
if (this_press_time >= kLongPressTime) {
inhibit_switch[i] = true;
latch_mode[i] = !latch_mode[i];
DisplayConfigurationAndSave(i);
} else if (this_press_time >= 64 && !new_input_state) {
toggle_mode[i] = !toggle_mode[i];
DisplayConfigurationAndSave(i);
}
}
switch_state[i] = new_input_state;
}
// Scan inputs
uint32_t random_words = rng_state;
for (uint8_t i = 0; i < 2; ++i) {
bool new_input_state = Read(i);
if (new_input_state || latch_mode[i]) {
// Hold the LED while the trigger is high or when in latch mode
led_gate_delay[i] = kLedGateDelay;
}
if (new_input_state && !input_state[i] /* Rising edge */) {
uint16_t random = random_words & 0xffff;
uint16_t threshold = p[i];
bool outcome = random >= threshold && threshold != 65535;
if (toggle_mode[i]) {
outcome = outcome ^ previous_state[i];
}
previous_state[i] = outcome;
GateOn(i, outcome);
led_state[i] = outcome ? 1 : 2;
} else if (!new_input_state && input_state[i] && !latch_mode[i]) {
GateOff(i);
}
input_state[i] = new_input_state;
random_words >>= 16;
if (led_gate_delay[i]) {
--led_gate_delay[i];
if (!led_gate_delay[i]) {
led_state[i] = 0;
}
}
}
// Refresh LEDs
if (led_state[0] == 0) {
led_1_a.Low();
led_1_k.Low();
} else if (led_state[0] == 1) {
led_1_a.Low();
led_1_k.High();
} else {
led_1_a.High();
led_1_k.Low();
}
if (led_state[1] == 0) {
led_2_a.Low();
led_2_k.Low();
} else if (led_state[1] == 1) {
led_2_a.Low();
led_2_k.High();
} else {
led_2_a.High();
led_2_k.Low();
}
// rng_state = rng_state * 1664525 + 1013904223;
rng_state = (rng_state >> 1) ^ (-(rng_state & 1u) & 0xD0000001u);
}
}