From 2e3ad91af312aaf20b10f1cbe6096cda7e8a97d1 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sat, 3 Apr 2010 09:49:02 +0000 Subject: [PATCH] Add a program for serial port control and associated Arduino firmware Make network client handle error packets Add function to get all trains from the client --- Build | 10 ++ firmware/Makefile | 23 ++++ firmware/ctrl.c | 228 ++++++++++++++++++++++++++++++++++++++ source/network/client.cpp | 9 +- source/network/client.h | 8 +- source/serial/serial.cpp | 130 ++++++++++++++++++++++ source/serial/serial.h | 41 +++++++ 7 files changed, 445 insertions(+), 4 deletions(-) create mode 100644 firmware/Makefile create mode 100644 firmware/ctrl.c create mode 100644 source/serial/serial.cpp create mode 100644 source/serial/serial.h diff --git a/Build b/Build index a69a03d..9be8021 100644 --- a/Build +++ b/Build @@ -77,4 +77,14 @@ package "märklin" library "marklinnet"; }; }; + + program "serial" + { + source "source/serial"; + build_info + { + incpath "source"; + library "marklinnet"; + }; + }; }; diff --git a/firmware/Makefile b/firmware/Makefile new file mode 100644 index 0000000..68d6f4c --- /dev/null +++ b/firmware/Makefile @@ -0,0 +1,23 @@ +# $Id$ + +MCU = atmega328p +CLOCK = 16000000 +CFLAGS = -Wall -Os -ffunction-sections -fdata-sections -mmcu=$(MCU) -DF_CPU=$(CLOCK) +LDFLAGS = -Os -Wl,--gc-sections -mmcu=$(MCU) + +help: + @echo "Targets:" + @echo " %.hex: Build firmware from %.c" + @echo " upload-%: Upload firmware to AVR" + +%.hex: %.elf + avr-objcopy -O ihex $< $@ + +%.elf: %.o + avr-gcc $(LDFLAGS) -o $@ $< + +%.o: %.c + avr-gcc -c $(CFLAGS) -o $@ $< + +upload-%: %.hex + avrdude -p$(MCU) -cstk500v1 -P/dev/ttyUSB0 -b57600 -D -Uflash:w:$<:i diff --git a/firmware/ctrl.c b/firmware/ctrl.c new file mode 100644 index 0000000..523c702 --- /dev/null +++ b/firmware/ctrl.c @@ -0,0 +1,228 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include +#include + +#define BIT(n) (1<<(n)) +#define NOP() __asm__("nop"); + +uint8_t digits[11] = +{ + 0x3F, + 0x06, + 0x5B, + 0x4F, + 0x66, + 0x6D, + 0x7D, + 0x07, + 0x7F, + 0x6F, + 0x40 +}; + +uint16_t read_input(void); +uint16_t read_input_filtered(void); +void write_serial(uint8_t); +void write_7seg(uint8_t); + +uint8_t speed = 0; +uint16_t funcs = 0; +volatile uint8_t ticks = 0; +volatile uint8_t rx_buf[3]; +volatile uint8_t rx_fill = 0; + +ISR(TIMER1_COMPA_vect) +{ + if(ticks<255) + ++ticks; +} + +ISR(USART_RX_vect) +{ + uint8_t c = UDR0; + if(rx_fill==0) + { + if(c=='A' || c=='S') + rx_buf[rx_fill++] = c; + } + else + { + rx_buf[rx_fill++] = c; + if(rx_buf[0]=='A' && rx_fill==3) + { + uint8_t n = (rx_buf[1]-'0')*10+(rx_buf[2]-'0'); + write_7seg(n/10); + write_7seg(n%10); + rx_fill = 0; + } + else if(rx_buf[0]=='S' && rx_fill==3) + { + speed = (rx_buf[1]-'0')*10+(rx_buf[2]-'0'); + rx_fill = 0; + } + } +} + +int main() +{ + uint16_t state = 0; + + DDRD = 0x02; // 00000010 + PORTD = 0xFC; // 11111100 + DDRB = 0x3F; // 00111111 + PORTB = 0x0F; // 00001111 + + write_7seg(10); + write_7seg(10); + + // 9600 baud, 8N1 + UBRR0H = 0; + UBRR0L = 103; + UCSR0C = BIT(UCSZ00) | BIT(UCSZ01); + UCSR0B = BIT(RXEN0) | BIT(TXEN0) | BIT(RXCIE0); + + TCCR1A = 0; + TCCR1B = BIT(WGM12) | BIT(CS12); + OCR1AH = 24; + OCR1AL = 106; + TIMSK1 = BIT(OCIE1A); + + sei(); + + while(1) + { + uint16_t toggled; + uint8_t i; + uint16_t input; + + input = read_input_filtered(); + input ^= 0xFFFC; + + toggled = state^input; + state = input; + + if((toggled&3)==2 && (state&1)) + { + uint8_t action = 0; + if(state&2) + { + if(speed<14) + { + ++speed; + action = 1; + } + } + else + { + if(speed>0) + { + --speed; + action = 1; + } + else if(ticks>10) + action = 2; + } + + if(action==1) + { + write_serial('S'); + write_serial('0'+speed/10); + write_serial('0'+speed%10); + } + else if(action==2) + write_serial('R'); + + ticks = 0; + } + + for(i=0; i<14; ++i) + { + uint16_t bit = 4<>2; + input |= pins&3; + input |= (pins&0x3C)<<(row*4); + } + + return input; +} + +uint16_t read_input_filtered(void) +{ + uint16_t valid = 0xFFFF; + uint16_t prev; + uint16_t input; + uint8_t i; + + prev = read_input(); + for(i=0; i<20; ++i) + { + input = read_input(); + valid &= ~(input^prev); + prev = input; + } + + return input&valid; +} + +void write_serial(uint8_t c) +{ + while(!(UCSR0A&(1<::iterator i = trains.find(addr); + map::const_iterator i = trains.find(addr); if(i==trains.end()) throw KeyError("Unknown train"); return *i->second; @@ -83,4 +83,9 @@ void Client::receive(const TrainRoutePacket &pkt) get_train(pkt.address).process_packet(pkt); } +void Client::receive(const ErrorPacket &pkt) +{ + signal_error.emit(pkt.message); +} + } // namespace Marklin diff --git a/source/network/client.h b/source/network/client.h index 3a9cbc0..39c7eb0 100644 --- a/source/network/client.h +++ b/source/network/client.h @@ -22,10 +22,12 @@ class Client: public Msp::Net::PacketReceiver, Msp::Net::PacketReceiver, Msp::Net::PacketReceiver, Msp::Net::PacketReceiver, - Msp::Net::PacketReceiver + Msp::Net::PacketReceiver, + Msp::Net::PacketReceiver { public: sigc::signal signal_train_added; + sigc::signal signal_error; private: const Catalogue &catalogue; @@ -49,7 +51,8 @@ public: const Catalogue &get_catalogue() const { return catalogue; } const std::list &get_routes() const { return routes; } - NetTrain &get_train(unsigned); + NetTrain &get_train(unsigned) const; + const std::map &get_trains() const { return trains; } private: virtual void receive(const TrainInfoPacket &); @@ -58,6 +61,7 @@ private: virtual void receive(const TrainStatusPacket &); virtual void receive(const RouteInfoPacket &); virtual void receive(const TrainRoutePacket &); + virtual void receive(const ErrorPacket &); }; } // namespace Marklin diff --git a/source/serial/serial.cpp b/source/serial/serial.cpp new file mode 100644 index 0000000..2766752 --- /dev/null +++ b/source/serial/serial.cpp @@ -0,0 +1,130 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include +#include "serial.h" + +#include + +using namespace std; +using namespace Msp; +using namespace Marklin; + +Application::RegApp Serial::reg; + +Serial::Serial(int, char **argv): + client(catalogue), + serial_port(argv[2]), + train(0), + rx_fill(0) +{ + DataFile::load(catalogue, "locos.dat"); + + client.use_event_dispatcher(event_disp); + client.signal_train_added.connect(sigc::mem_fun(this, &Serial::train_added)); + client.signal_error.connect(sigc::mem_fun(this, &Serial::error)); + + string addr_str = argv[1]; + if(addr_str.find(':')==string::npos) + addr_str += ":8315"; + Net::SockAddr *addr = Net::resolve(addr_str); + client.connect(*addr); + delete addr; + + serial_port.set_parameters("9600,8N1"); + event_disp.add(serial_port); + serial_port.signal_data_available.connect(sigc::mem_fun(this, &Serial::data_available)); +} + +void Serial::tick() +{ + event_disp.tick(); +} + +void Serial::train_added(NetTrain &t) +{ + if(!train) + set_train(&t); +} + +void Serial::error(const string &e) +{ + IO::print("%s\n", e); +} + +void Serial::set_train(NetTrain *t) +{ + train = t; + serial_port.write(format("A%02d", train->get_address())); +} + +void Serial::next_train() +{ + const map &trains = client.get_trains(); + + map::const_iterator i = trains.find(train->get_address()); + ++i; + if(i==trains.end()) + i = trains.begin(); + + set_train(i->second); +} + +void Serial::prev_train() +{ + const map &trains = client.get_trains(); + + map::const_iterator i = trains.find(train->get_address()); + if(i==trains.begin()) + i = trains.end(); + --i; + + set_train(i->second); +} + +void Serial::data_available() +{ + char c; + serial_port.read(&c, 1); + if(rx_fill==0) + { + if(c=='S' || c=='H' || c=='L') + rx_buf[rx_fill++] = c; + else if(c=='R') + { + IO::print("Reverse\n"); + train->set_reverse(!train->get_reverse()); + } + else if(c=='N') + next_train(); + else if(c=='P') + prev_train(); + } + else + { + rx_buf[rx_fill++] = c; + if(rx_buf[0]=='H' && rx_fill==2) + { + train->set_function(rx_buf[1]-'0', true); + IO::print("Func %d on\n", rx_buf[1]-'0'); + rx_fill = 0; + } + else if(rx_buf[0]=='L' && rx_fill==2) + { + train->set_function(rx_buf[1]-'0', false); + IO::print("Func %d off\n", rx_buf[1]-'0'); + rx_fill = 0; + } + else if(rx_buf[0]=='S' && rx_fill==3) + { + unsigned speed = (rx_buf[1]-'0')*10+(rx_buf[2]-'0'); + IO::print("Set speed %d\n", speed); + train->set_speed(speed); + rx_fill = 0; + } + } +} diff --git a/source/serial/serial.h b/source/serial/serial.h new file mode 100644 index 0000000..b5427e4 --- /dev/null +++ b/source/serial/serial.h @@ -0,0 +1,41 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#ifndef SERIAL_H_ +#define SERIAL_H_ + +#include +#include +#include "network/client.h" + +class Serial: public Msp::Application +{ +private: + Msp::IO::EventDispatcher event_disp; + Marklin::Catalogue catalogue; + Marklin::Client client; + Msp::IO::Serial serial_port; + Marklin::NetTrain *train; + char rx_buf[3]; + unsigned rx_fill; + + static Msp::Application::RegApp reg; + +public: + Serial(int, char **); + +private: + virtual void tick(); + void train_added(Marklin::NetTrain &); + void error(const std::string &); + void set_train(Marklin::NetTrain *); + void next_train(); + void prev_train(); + void data_available(); +}; + +#endif -- 2.45.2