diff options
Diffstat (limited to 'src/sim/sim.ino')
-rw-r--r-- | src/sim/sim.ino | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/sim/sim.ino b/src/sim/sim.ino new file mode 100644 index 0000000..fb12269 --- /dev/null +++ b/src/sim/sim.ino @@ -0,0 +1,287 @@ +/* SIM card for ATMEL + * + * (C) 2020 by Andreas Eversberg <jolly@eversberg.eu> + * All Rights Reserved + * + * 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 <http://www.gnu.org/licenses/>. + */ + +extern "C" +{ + #include "sim.h" + #include "eeprom.h" +} + +/* settings for ATTINY85 */ +#if defined(__AVR_ATtiny85__) +#define SERIAL_DATA 4 +#define SERIAL_DELAY 124 +#define SERIAL_TIMEOUT 1200 /* > two bytes */ +#else +/* settings for Arduino UNO with 16 MHz */ +#define STATUS_LED LED_BUILTIN +#define RESET_PIN 6 +#define SERIAL_DATA 7 +#define SERIAL_DELAY 410 +#define SERIAL_TIMEOUT 2500 /* > two bytes */ +#endif +/* to set fused for ATTINY85: + * avrdude -c usbasp-clone -p t85 -U lfuse:w:0xc0:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m + */ + +/* timing test TX (010101010011) */ +//#define TEST_TX +/* timing test RX (000000000001) */ +//#define TEST_RX +/* timing test timeout (pause + 000000000001) */ +//#define TEST_TO + +sim_sim_t sim; + +#include <avr/eeprom.h> +#include <util/delay.h> + +uint8_t eeprom_read(enum eeprom_locations loc) +{ + return eeprom_read_byte((uint8_t *)loc); +} + +void eeprom_write(enum eeprom_locations loc, uint8_t value) +{ + eeprom_write_byte((uint8_t *)loc, value); +} + +size_t eeprom_length(void) +{ + return 512; +} + +#ifdef RESET_PIN +volatile uint8_t *reset_in; +uint8_t reset_bit; + +/* init reset pin */ +void reset_init(uint8_t pin) +{ + uint8_t port; + volatile uint8_t *mode, *out; + + reset_bit = digitalPinToBitMask(pin); + port = digitalPinToPort(pin); + + mode = portModeRegister(port); + out = portOutputRegister(port); + reset_in = portInputRegister(port); + + *mode &= ~reset_bit; /* intput */ + *out |= reset_bit; /* pullup */ +} +#endif + +volatile uint8_t *serial_mode, *serial_out, *serial_in; +uint8_t serial_bit; +uint16_t serial_delay; + +/* init serial pin */ +void serial_init(uint8_t pin, uint16_t delay) +{ + uint8_t port; + + serial_delay = delay; + serial_bit = digitalPinToBitMask(pin); + port = digitalPinToPort(pin); + + serial_mode = portModeRegister(port); + serial_out = portOutputRegister(port); + serial_in = portInputRegister(port); + + *serial_mode &= ~serial_bit; /* input */ + *serial_out |= serial_bit; /* pullup */ +} + +/* wait some time so the stop bits haven been elapsed before transmitting a block */ +void serial_start_tx(void) +{ + /* wait some time, so previous stop bits have been elapsed */ + _delay_loop_2(serial_delay * 3); /* 2..3 bits of time */ +} + +/* transmit a byte */ +void serial_tx(uint8_t b) +{ + uint8_t i, c = 0; + + /* start bit */ + *serial_mode |= serial_bit; /* output */ + *serial_out &= ~serial_bit; /* low */ + _delay_loop_2(serial_delay); + /* 8 data bits */ + for (i = 8; i > 0; --i) { + if (b & 1) + *serial_out |= serial_bit; /* high */ + else + *serial_out &= ~serial_bit; /* low */ + _delay_loop_2(serial_delay); + c ^= b; + b>>= 1; + } + /* even parity */ + if (c & 1) + *serial_out |= serial_bit; /* high */ + else + *serial_out &= ~serial_bit; /* low */ + _delay_loop_2(serial_delay); + /* 2 stop bits */ + *serial_out |= serial_bit; /* high */ + _delay_loop_2(serial_delay); + _delay_loop_2(serial_delay); + *serial_mode &= ~serial_bit; /* input */ +} + +/* receive a byte */ +uint8_t serial_rx(void) +{ + uint8_t i, b = 0; + + /* center read */ + _delay_loop_2(serial_delay >> 1); + /* 8 data bits */ + for (i = 8; i > 0; --i) { + _delay_loop_2(serial_delay); + b >>= 1; + if ((*serial_in & serial_bit)) + b |= 0x80; + } + /* parity */ + _delay_loop_2(serial_delay); + /* move into (first) stop bit */ + _delay_loop_2(serial_delay); + + return b; +} + +void setup() { + uint8_t byte, ver; + +#ifdef STATUS_LED + pinMode(STATUS_LED, OUTPUT); +#endif + + /* intial eeprom init */ + byte = eeprom_read(EEPROM_MAGIC + 0); + ver = eeprom_read(EEPROM_MAGIC + 1); + if (byte != 'C' || ver != '0' + EEPROM_VERSION) + sim_init_eeprom(); + +#ifdef RESET_PIN + reset_init(RESET_PIN); +#endif + serial_init(SERIAL_DATA, SERIAL_DELAY); +#ifdef TEST_TX + while (true) + serial_tx(0x55); +#endif +#ifdef TEST_RX + *serial_mode |= serial_bit; /* output */ + while (true) { + /* show low for start bit up to end of first stop bit */ + *serial_out &= ~serial_bit; /* low */ + serial_rx(); + _delay_loop_2(serial_delay >> 1); + *serial_out |= serial_bit; /* high */ + _delay_loop_2(serial_delay); + } +#endif +#ifdef TEST_TO + uint16_t to; + int rx; + rx_again: + rx = 1; + /* wait until start bit is received or timeout */ + for (to = 0; to <= SERIAL_TIMEOUT;) { + if (!(*serial_in & serial_bit)) { + serial_tx(0x33); + goto rx_again; + } +#ifdef RESET_PIN + if (!(*reset_in & reset_bit)) { + serial_tx(0xf0); + goto rx_again; + } +#endif + if (rx) + to++; + } + serial_tx(0x55); + goto rx_again; +#endif +} + +void loop() { +#if !defined(TEST_TX) && !defined(TEST_RX) && !defined (TEST_TO) + uint16_t to; + int c, rx; + +reset_again: +#ifdef RESET_PIN + /* wait until reset is released */ + while(!(*reset_in & reset_bit)); +#endif + sim_reset(&sim, 0); + +tx_again: +#ifdef STATUS_LED + digitalWrite(STATUS_LED, LOW); +#endif + /* send buffer until no more data to be transmitted */ + serial_start_tx(); + while ((c = sim_tx(&sim)) >= 0) { +#ifdef RESET_PIN + /* perform reset, when low */ + if (!(*reset_in & reset_bit)) + goto reset_again; +#endif + /* perform transmission of a byte */ + serial_tx(c); + } + /* wait until start bit is received or timeout */ + rx = 0; + for (to = 0; to <= SERIAL_TIMEOUT;) { + /* perform RX, when low (start bit) */ + if (!(*serial_in & serial_bit)) { + c = serial_rx(); + /* if block was completly received, go to tx_again */ + if (sim_rx(&sim, c) < 0) + goto tx_again; + /* start counting timeout condition */ + rx = 1; + to = 0; +#ifdef STATUS_LED + digitalWrite(STATUS_LED, HIGH); +#endif + } +#ifdef RESET_PIN + /* perform reset, when low */ + if (!(*reset_in & reset_bit)) + goto reset_again; +#endif + /* only if we have an ongoing reception, we count for the timeout condition */ + if (rx) + to++; + } + /* perform timeout */ + sim_timeout(&sim); + goto tx_again; +#endif +} |