From 6ce19797274ce39d8b09e20c539508d840c36e9b Mon Sep 17 00:00:00 2001 From: IMback Date: Fri, 5 Oct 2018 21:13:22 +0200 Subject: [PATCH] initial commit --- eeprom.h | 40 +++++++ main.cpp | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++ ringbuffer.h | 112 +++++++++++++++++++ serial.cpp | 127 ++++++++++++++++++++++ serial.h | 37 +++++++ train.cpp | 116 ++++++++++++++++++++ train.h | 49 +++++++++ writepin.h | 13 +++ 8 files changed, 795 insertions(+) create mode 100644 eeprom.h create mode 100644 main.cpp create mode 100755 ringbuffer.h create mode 100644 serial.cpp create mode 100644 serial.h create mode 100644 train.cpp create mode 100644 train.h create mode 100644 writepin.h 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