commit 6ce19797274ce39d8b09e20c539508d840c36e9b Author: IMback Date: Fri Oct 5 21:13:22 2018 +0200 initial commit diff --git a/eeprom.h b/eeprom.h new file mode 100644 index 0000000..46e0772 --- /dev/null +++ b/eeprom.h @@ -0,0 +1,40 @@ +void EEPROM_write_char(uint16_t address, unsigned char data) +{ + /* Wait for completion of previous write */ + while(EECR & (1< +#include +#include +#include +#include +#include "serial.h" +#include "writepin.h" +#include "train.h" +#include "eeprom.h" + +const char *bit_rep[16] = +{ + [ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011", + [ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111", + [ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011", + [12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111", +}; + +#define MAX_TRAINS 16 +#define COMMAND_BUFFER_SIZE 64 +#define SNPRINTF_BUFFER_SIZE 64 + +char buffer[SNPRINTF_BUFFER_SIZE]; + +uint16_t storedTrains = 0; +Train trains[MAX_TRAINS]; + +#define TICK_MAX 255 +uint8_t tick = 0; + +bool autoff = true; +bool powerIsOn = true; + +ISR(TIMER0_OVF_vect) +{ + if(powerIsOn) + { + ++tick; + if(tick == TICK_MAX) + { + for(uint16_t i = 0; i < storedTrains; i++) trains[i].resendSpeed(); + if(autoff) + { + bool trainsRunning = false; + for(uint16_t i = 0; i < storedTrains; i++) trainsRunning = trainsRunning || trains[i].getSpeed(); + if(!trainsRunning) + { + powerIsOn = false; + Train::setOutput(Train::OFF); + } + } + tick = 0; + } + else + { + + Train::setOutput(Train::HIGH); + _delay_us(200); + Train::setOutput(Train::LOW); + } + } +} + +void save_state() +{ + EEPROM_write_char( 0, storedTrains ); + EEPROM_write_char( 1, autoff ); + for(uint16_t i = 0; i < storedTrains; i++) + { + EEPROM_write_char( i+32, trains[i].getAddress()); + } + +} + +void restore_state() +{ + storedTrains = EEPROM_read_char(0); + autoff = EEPROM_read_char(1); + if(storedTrains > MAX_TRAINS ) + { + for(uint16_t i = 0; i < MAX_TRAINS+32; i++) EEPROM_write_char(i, 0); + storedTrains = 0; + } + else for(uint8_t i = 0; i <= storedTrains; i++) + { + uint8_t address = EEPROM_read_char(32+i); + trains[i].setAddress(address); + } +} + +inline static void printHelp(Serial* serial) +{ + serial->write_p(PSTR("Available Commands: \n\ + help : Show this prompt.\n\ + train add [address] : Add train.\n\ + train delete : Delete last train.\n\ + train list : Print list of saved trains.\n\ + train [nn] stop : Stop nth train.\n\ + train [nn] speed [sp] : Set nth train speed.\n\ + train [nn] function [x] : Toggle x'ed fuction on train n.\n\ + train [nn] reverse : Reverse train n.\n\ + stop : stop all trains\n\ + power off : power off the rail\n\ + power on : power on the rail\n\ + power auto : power off the rail when no trains are moveing\n\ + dump : prints epprom contence\n\ + erase : Erase epprom.\n")); +} + + +void trainDispatch(char* inBuffer, Serial* serial) +{ + if(powerIsOn == false) + { + powerIsOn = true; + Train::setOutput(Train::LOW); + _delay_ms(100); + } + if( strcmp(inBuffer, "add") == 0 ) + { + char* token = strtok(NULL, " "); + uint8_t address = strtol(token, nullptr, 2 ); + if(address != 0 && storedTrains < MAX_TRAINS) + { + trains[storedTrains].setAddress(address); + + uint8_t size = snprintf(buffer, SNPRINTF_BUFFER_SIZE, "TRAIN saved! NUMBER: %u ADRESS: %s%s\n", storedTrains, bit_rep[address >> 4], bit_rep[address & 0x0F]); + serial->write(buffer, size); + + storedTrains++; + save_state(); + } + else serial->write_p(PSTR("Usage: train add [address]")); + } + else if( strcmp(inBuffer, "delete") == 0) + { + serial->write_p(PSTR("Train: ")); + serial->write(storedTrains); + serial->write_p(PSTR(" deleted\n")); + storedTrains--; + } + else if( strcmp(inBuffer, "list") == 0 ) + { + serial->write_p(PSTR("Trains:\n")); + for(uint8_t i = 0; i < storedTrains; i++) + { + snprintf(buffer, SNPRINTF_BUFFER_SIZE, "NUMBER: %u ID: %s%s CURRENT SPD: %u\n", i, bit_rep[trains[i].getAddress() >> 4], bit_rep[trains[i].getAddress() & 0x0F], trains[i].getSpeed()); + serial->write(buffer, SNPRINTF_BUFFER_SIZE); + } + serial->putChar('\n'); + } + else\ + { + uint8_t id = strtol(inBuffer, nullptr, 10); + if(id < storedTrains ) + { + char* token = strtok(NULL, " "); + if( strcmp(token, "speed") == 0 ) + { + token = strtok(NULL, " "); + trains[id].setSpeed(atoi(token)); + } + else if( strcmp(token, "function") == 0 ) + { + token = strtok(NULL, " "); + trains[id].sendFunction(atoi(token)); + } + else if( strcmp(token, "reverse") == 0 ) trains[id].reverse(); + else if( strcmp(token, "stop") )trains[id].setSpeed(0); + else serial->write_p(PSTR("Not a valid command\n")); + } + else serial->write_p(PSTR("Id out of range.\n")); + } +} + +void powerDispatch(char* token, Serial* serial) +{ + if(strcmp(token, "off") == 0) + { + for(uint16_t i = 0; i < storedTrains; i++) + { + trains[i].setSpeed(0); + } + Train::setOutput(Train::OFF); + powerIsOn = false; + } + else if( strcmp(token, "on") == 0) + { + for(uint16_t i = 0; i < storedTrains; i++) + { + trains[i].setSpeed(0); + } + Train::setOutput(Train::LOW); + } + else if(strcmp(token, "auto") == 0) + { + token = strtok(NULL, " "); + if(strcmp(token, "on") == 0) + { + autoff = true; + serial->write_p(PSTR("auto power off turned on.\n")); + save_state(); + } + else if(strcmp(token, "off") == 0) + { + autoff = false; + serial->write_p(PSTR("auto power off turned off.\n")); + save_state(); + } + else + { + serial->write_p(PSTR("argument must be \"on\" or \"off\". This feature is currently ")); + autoff ? serial->write_p(PSTR("on.\n")) : serial->write_p(PSTR("off.\n")); + } + } +} + + +void serialDispatch(Serial* serial) +{ + if(serial->dataIsWaiting()) + { + char buffer[COMMAND_BUFFER_SIZE]; + unsigned int length = serial->getString(buffer, COMMAND_BUFFER_SIZE); + if(length > 2) + { + serial->write_p(PSTR("Got: ")); + serial->putChar('\"'); + serial->write(buffer, length); + serial->write("\"\n"); + char* token = strtok(buffer, " "); + if(length > 4 && strcmp(token, "train") == 0) + { + token = strtok(NULL, " "); + trainDispatch(token, serial); + } + else if(length > 4 && strncmp(token, "erase", 4) == 0) + { + for(uint16_t i = 0; i < MAX_TRAINS+1; i++) EEPROM_write_char(i, 0); + serial->write_p(PSTR("EEPROM erased\n")); + storedTrains = 0; + } + else if(length > 3 && strcmp(token, "dump") == 0) + { + for(uint16_t i = 0; i < MAX_TRAINS+32; i++) + { + if(i != 0) serial->putChar(','); + serial->write((uint16_t)EEPROM_read_char(i)); + } + serial->putChar('\n'); + } + else if(length > 3 && strcmp(token, "stop") == 0) + { + for(uint16_t i = 0; i < storedTrains; i++) + { + trains[i].setSpeed(0); + } + } + else if(length > 3 && strcmp(token, "power") == 0) + { + token = strtok(NULL, " "); + powerDispatch(token, serial); + } + else if(length > 3 && strcmp(token, "help") == 0) + { + printHelp(serial); + } + else + { + serial->putChar('\"'); + serial->write(buffer, length); + serial->putChar('\"'); + serial->write_p(PSTR(" is not a valid command\n")); + } + } + } +} + +int main() +{ + TCNT0 = 0; + TIMSK0 = 0b00000001; //turn on timer0 interupt on OCIEB + TCCR0B = (1<. +*/ +#pragma once + +#include + +template < int BUFFER_SIZE > +class RingBuffer +{ +private: + + uint_fast16_t _headIndex = 0; + uint_fast16_t _tailIndex = 0; + uint8_t _buffer[BUFFER_SIZE]; + +public: + + RingBuffer() + { + flush(); + } + + uint_fast16_t remaining() + { + return (_headIndex-_tailIndex); + } + + bool isOverun(uint_fast16_t margin = 0) + { + return remaining() >= BUFFER_SIZE - margin; + } + + bool isEmpty() + { + return _tailIndex >= _headIndex; + } + + uint8_t read() + { + if(!isEmpty()) + { + _tailIndex++; + return _buffer[(_tailIndex - 1) % BUFFER_SIZE]; + } + else return '\0'; + } + + unsigned int read( uint8_t* buffer, unsigned int length ) + { + unsigned int i = 0; + for(; i < length && !isEmpty(); i++) + { + buffer[i] = read(); + } + return i; + } + + void write( uint8_t in ) + { + if (_headIndex - BUFFER_SIZE > 0 && _tailIndex - BUFFER_SIZE > 0) + { + _headIndex -= BUFFER_SIZE; + _tailIndex -= BUFFER_SIZE; + } + _buffer[_headIndex % BUFFER_SIZE] = in; + _headIndex++; + } + + void write( uint8_t* buffer, unsigned int length ) + { + for(uint8_t i = 0; i < length; i++) write(buffer[i]); + } + + void flush() + { + _headIndex = 0; + _tailIndex = 0; + for(int i = 0; i < BUFFER_SIZE; i++) _buffer[i] = ' '; + } + + unsigned int getString(uint8_t terminator, char* buffer, const unsigned int bufferLength) + { + unsigned int i = 0; + for(; i <= remaining() && i <= BUFFER_SIZE && _buffer[(_tailIndex+i) % BUFFER_SIZE] != terminator; i++); + + if( i < remaining() && i > 0) + { + if(i > bufferLength-1) i = bufferLength-1; + read((uint8_t*)buffer, i); + buffer[i]='\0'; + _tailIndex++; + } + else if(i == 0) _tailIndex++; + else i = 0; + + return i; + } +}; diff --git a/serial.cpp b/serial.cpp new file mode 100644 index 0000000..6e95273 --- /dev/null +++ b/serial.cpp @@ -0,0 +1,127 @@ +#include "serial.h" +#include "ringbuffer.h" + +RingBuffer rxBuffer; + +bool stopped = false; + +ISR (USART_RX_vect) //I have seen worse interrupt sintax +{ + rxBuffer.write(UDR0); + if(serialFlowControl && !stopped && rxBuffer.isOverun(32)) + { + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = 0x13; + stopped = true; + } +} + +Serial::Serial() +{ + UBRR0H = UBRRH_VALUE; + UBRR0L = UBRRL_VALUE; + UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); + UCSR0B = _BV(RXEN0) | _BV(TXEN0); //Enable RX and TX + UCSR0B |= (1 << RXCIE0); //Enable Rx interuppt +} + +void Serial::putChar(const char c) +{ + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; +} + +void Serial::write(const char* in, const unsigned int length) +{ + for(unsigned int i = 0; i < length && in[i] != '\0'; i++) + { + putChar(in[i]); + } +} + +void Serial::write_p(const char in[]) +{ + cli(); + char ch = pgm_read_byte(in); + while (ch != '\0') + { + putChar(ch); + in++; + ch = pgm_read_byte(in); + } + sei(); +} + +void Serial::write(const char in[]) +{ + for(unsigned int i = 0; i < strlen(in); i++) + { + putChar(in[i]); + } +} + + +void Serial::write(int32_t in) +{ + if(in == 0) + { + putChar('0'); + } + else + { + bool flag = false; + char str[64] = { 0 }; + int16_t i = 62; + if (in < 0) + { + flag = true; + in = abs(in); + } + + while (in != 0 && i > 0) + { + str[i--] = (in % 10) + '0'; + in /= 10; + } + + if (flag) str[i--] = '-'; + write(str + i + 1, 64-(i+1)); + } +} + +bool Serial::dataIsWaiting() +{ + return !rxBuffer.isEmpty(); +} + +char Serial::getChar() +{ + if(!rxBuffer.isEmpty()) + { + if(serialFlowControl && stopped && !rxBuffer.isOverun(32)) + { + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = 0x11; + stopped = false; + } + return rxBuffer.read(); + } + else return '\0'; +} + +unsigned int Serial::getString(char* buffer, const int bufferLength) +{ + return rxBuffer.getString(_terminator, buffer, bufferLength); +} + +unsigned int Serial::read(char* buffer, const unsigned int length ) +{ + return rxBuffer.read((uint8_t*)buffer, length); +} + +void Serial::flush() +{ + rxBuffer.flush(); +} + +void Serial::setTerminator(char terminator){_terminator = terminator;} diff --git a/serial.h b/serial.h new file mode 100644 index 0000000..7153bf2 --- /dev/null +++ b/serial.h @@ -0,0 +1,37 @@ +#ifndef SERIAL_H +#define SERIAL_H + +#define BAUD 38400 +#define SERIAL_BUFFER_SIZE 128 + +#include +#include +#include +#include +#include +#include + +const bool serialFlowControl = false; + +class Serial +{ +private: + char _terminator = '\n'; + +public: + Serial(); + void putChar(const char c); + void write(const char* in, const unsigned int length); + void write(const char in[]); + void write_p(const char in[]); //for flash space strigns + void write(const int32_t in); + unsigned int read( char* buffer, const unsigned int length ); + bool dataIsWaiting(); + char getChar(); + unsigned int getString(char* buffer, const int bufferLength); + void flush(); + void setTerminator(const char terminator); +}; + +#endif + diff --git a/train.cpp b/train.cpp new file mode 100644 index 0000000..ab2cada --- /dev/null +++ b/train.cpp @@ -0,0 +1,116 @@ +#include "train.h" + +static volatile unsigned char *_port = &PORTD; + +Train::Train(const uint8_t address): _address(address) +{ +} + +Train::Train() +{ + writePin(_port, _pinHigh, false); + writePin(_port, _pinLow, true); + _address = 0; +} + +void Train::setAddress(const uint8_t address) +{ + _address = address; +} + +uint8_t Train::getAddress() +{ + return _address; +} + +void Train::setOutput(const uint8_t state) +{ + if(state == HIGH) + { + writePin(_port, _pinLow, true); + _delay_us(5); + writePin(_port, _pinHigh, true); + } + else if (state == LOW) + { + writePin(_port, _pinHigh, false); + _delay_us(5); + writePin(_port, _pinLow, false); + } + else + { + writePin(_port, _pinHigh, false); + writePin(_port, _pinLow, true); + } +} + +void Train::sendBit(const bool bit) +{ + if(bit) + { + setOutput(HIGH); + _delay_us(170); + setOutput(LOW); + _delay_us(25); + } + else + { + setOutput(HIGH); + _delay_us(20); + setOutput(LOW); + _delay_us(175); + } +} + +void Train::sendAddress() +{ + for(uint8_t i = 0; i < 8; i++) + { + sendBit(_address & (1 << i)); + } +} + +void Train::sendData(const uint8_t data) +{ + for(uint8_t j = 0; j < SEND_COUNT; j++) + { + sendAddress(); + for(uint8_t i = 0; i < 5; i++) + { + sendBit(data & (1 << i)); + sendBit(data & (1 << i)); + } + _delay_ms(2); + } +} + +void Train::setSpeed(uint8_t speed) +{ + if(speed > 0) ++speed; + if(speed <= 15) + { + lastSpeed = speed; + sendData(speed << 1); + } +} + +void Train::resendSpeed() +{ + uint8_t data = lastSpeed; + sendData(data << 1); +} + +uint8_t Train::getSpeed() +{ + return lastSpeed; +} + +void Train::reverse() +{ + sendData(1 << 1); +} + +void Train::sendFunction(const uint8_t function) +{ + sendData((function << 1) | 1); +} diff --git a/train.h b/train.h new file mode 100644 index 0000000..07ed7b1 --- /dev/null +++ b/train.h @@ -0,0 +1,49 @@ +#pragma once + +#include "writepin.h" +#include +#include +#include + +class Train +{ +private: + uint8_t _address; + + static const unsigned char _pinHigh = PD3; + static const unsigned char _pinLow = PD2; + + static const uint8_t SEND_COUNT = 4; + + uint8_t lastSpeed = 0; + + void sendBit(const bool bit); + void sendAddress(); + void sendData(const uint8_t data); + +public: + + static const uint8_t HIGH = 2; + static const uint8_t LOW = 1; + static const uint8_t OFF = 0; + + static void setOutput(const uint8_t state); + + Train(const uint8_t address); + Train(); + + void resendSpeed(); + + void setSpeed(uint8_t speed); + + void reverse(); + + uint8_t getAddress(); + + uint8_t getSpeed(); + + void setAddress(const uint8_t address); + + void sendFunction(const uint8_t function); + +}; diff --git a/writepin.h b/writepin.h new file mode 100644 index 0000000..4ef214f --- /dev/null +++ b/writepin.h @@ -0,0 +1,13 @@ +#ifndef WRITEPIN_H +#define WRITEPIN_H +#include + +inline void writePin(volatile unsigned char *port, const unsigned char pin, const bool state) //waste 2 cycles +{ + *port &= ~(1 << pin); + if(state) *port |= (1 << pin); +} + +inline bool readPin( volatile unsigned char *inPort, const unsigned char pin){ return (bool) (*inPort & (1 << pin));} + +#endif