From 2b427f4bdfc71b055b7db8ffe2b9dd0ea3e4cc9a Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 11 Aug 2012 23:02:47 +0200 Subject: initial commit --- README.md | 58 +++++++++++ client/Makefile | 9 ++ client/enginecontroller.vala | 237 +++++++++++++++++++++++++++++++++++++++++++ client/main.vala | 58 +++++++++++ doc/atmostripe-pinout.txt | 1 + doc/isp-plug.svg | 187 ++++++++++++++++++++++++++++++++++ firmware/Makefile | 32 ++++++ firmware/firmware.c | 170 +++++++++++++++++++++++++++++++ 8 files changed, 752 insertions(+) create mode 100644 README.md create mode 100644 client/Makefile create mode 100644 client/enginecontroller.vala create mode 100644 client/main.vala create mode 100644 doc/atmostripe-pinout.txt create mode 100644 doc/isp-plug.svg create mode 100644 firmware/Makefile create mode 100644 firmware/firmware.c diff --git a/README.md b/README.md new file mode 100644 index 0000000..6bdab4b --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +ATmega Engine Controller +======================== + +The code from firmware/ is supposed to run on a ATmega48 +sending servo signals to cheap brushless controllers as used +in RC models. The ATmega48 gets its instructions via a serial +line from a host system. The code in client/ is a small test +binary for the host system. + +Connection +---------------- + + [Engine 1]--->[BLC 1]--->PB1 + [Engine 2]--->[BLC 2]--->PB2 + [Engine 3]--->[BLC 3]--->PB3 + [Engine 4]--->[BLC 4]--->PD3 + [Engine 5]--->[BLC 5]--->PD5 + [Engine 6]--->[BLC 6]--->PD6 + + [Host TX]--->PD0 + [Host RX]--->PD1 + +Serial Protocol +--------------- + +Each of the following commands will be acknowledged with +```0x2E```, which is the ASCII value for a dot. Available +commands are: + +- **no operation** + + This command simply does nothing. It consists of a + single byte: ```0x00``` + +- **enable** + + Sending the bytes ```0xCA```, ```0xFE```, ```0xBA```, ```0xBE``` activates + the engine controlling. Before sending this command + the controller does not activate any engine. + +- **disable** + + One can disable all engines again by sending the + bytes ```0xDE```, ```0xAD```, ```0xBA```, ```0xBE```. After disabling the + engines with this command all future engine settings + will be ignored. + +- **set speed (of a single engine)** + + To set the speed of a single engine on has to send + two bytes: first the engine number in the system + (```0x01``` - ```0x06```), the the desired speed (```0x00```-```0xFF```). + +- **set speed of all engines simultaneously** + + One can also set the speed of all engines simultaneously + to the same value by first sending ```0xFF``` and then sending + a second byte with the desired speed (```0x00```-```0xFF```). diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 0000000..d274276 --- /dev/null +++ b/client/Makefile @@ -0,0 +1,9 @@ +all: engine + +engine: enginecontroller.vala main.vala + valac -o $@ --pkg posix --pkg linux $^ + +clean: + rm -f engine + +.PHONY: all clean diff --git a/client/enginecontroller.vala b/client/enginecontroller.vala new file mode 100644 index 0000000..23bd572 --- /dev/null +++ b/client/enginecontroller.vala @@ -0,0 +1,237 @@ +public class EngineController { + private Posix.termios newtio; + private Posix.termios restoretio; + private int fd=-1; + private int byterate; + + private void setup(string device, int rate, int bits, int stopbits) { + Posix.speed_t baudrate = Posix.B9600; + + fd = Posix.open(device, Posix.O_RDWR /*| Posix.O_NONBLOCK*/); + + if(fd < 0) { + fd = -1; + stderr.printf("Could not open device!\n"); + return; + } + + Posix.tcflush(fd, Posix.TCIOFLUSH); + + Posix.tcgetattr(fd, out restoretio); + + /* apply settings */ + switch(rate) { + case 300: + baudrate = Posix.B300; + break; + case 600: + baudrate = Posix.B600; + break; + case 1200: + baudrate = Posix.B1200; + break; + case 2400: + baudrate = Posix.B2400; + break; + case 4800: + baudrate = Posix.B4800; + break; + case 9600: + baudrate = Posix.B9600; + break; + case 19200: + baudrate = Posix.B19200; + break; + case 38400: + baudrate = Posix.B38400; + break; + case 57600: + baudrate = Posix.B57600; + break; + case 115200: + baudrate = Posix.B115200; + break; + case 230400: + baudrate = Posix.B230400; + break; + default: + /* not supported */ + rate = 9600; + break; + } + + Posix.cfsetospeed(ref newtio, baudrate); + Posix.cfsetispeed(ref newtio, baudrate); + + switch(bits) { + case 5: + newtio.c_cflag = (newtio.c_cflag & ~Posix.CSIZE) | Posix.CS5; + break; + case 6: + newtio.c_cflag = (newtio.c_cflag & ~Posix.CSIZE) | Posix.CS6; + break; + case 7: + newtio.c_cflag = (newtio.c_cflag & ~Posix.CSIZE) | Posix.CS7; + break; + case 8: + default: + newtio.c_cflag = (newtio.c_cflag & ~Posix.CSIZE) | Posix.CS8; + break; + } + + newtio.c_cflag |= Posix.CLOCAL | Posix.CREAD; + + newtio.c_cflag &= ~(Posix.PARENB | Posix.PARODD); + + /* TODO: parity */ + + newtio.c_cflag &= ~Linux.Termios.CRTSCTS; + + if(stopbits == 2) + newtio.c_cflag |= Posix.CSTOPB; + else + newtio.c_cflag &= ~Posix.CSTOPB; + + newtio.c_iflag = Posix.IGNBRK; + + newtio.c_lflag = 0; + newtio.c_oflag = 0; + + newtio.c_cc[Posix.VTIME]=1; + newtio.c_cc[Posix.VMIN]=1; + + newtio.c_lflag &= ~(Posix.ECHONL|Posix.NOFLSH); + + int mcs=0; + Posix.ioctl(fd, Linux.Termios.TIOCMGET, out mcs); + mcs |= Linux.Termios.TIOCM_RTS; + Posix.ioctl(fd, Linux.Termios.TIOCMSET, out mcs); + + Posix.tcsetattr(fd, Posix.TCSANOW, newtio); + + this.byterate = rate/bits; + } + + public EngineController(string device) { + setup(device, 9600, 8, 1); + } + + private ssize_t read(void *buf, size_t count) { + if(fd >= 0) + return Posix.read(fd, buf, count); + else + stderr.printf("not connected!\n"); + return 0; + } + + private ssize_t write(void *buf, size_t count) { + if(fd >= 0) { + ssize_t size = Posix.write(fd, buf, count); + return size; + } else { + stderr.printf("not connected!\n"); + return 0; + } + } + + public bool disable() { + uint8[4] buf = new uint8[4]; + buf[0] = 0xDE; + buf[1] = 0xAD; + buf[2] = 0xBA; + buf[3] = 0xBE; + + if(fd < 0) { + stderr.printf("not connected!\n"); + return false; + } + + write(buf, 4); + read(buf, 1); + + if(buf[0] == '.') + return true; + else + return false; + } + + public bool enable() { + uint8[4] buf = new uint8[4]; + buf[0] = 0xCA; + buf[1] = 0xFE; + buf[2] = 0xBA; + buf[3] = 0xBE; + + if(fd < 0) { + stderr.printf("not connected!\n"); + return false; + } + + write(buf, 4); + read(buf, 1); + + if(buf[0] == '.') + return true; + else + return false; + } + + public bool no_operation() { + uint8[1] buf = new uint8[1]; + buf[0] = 0x00; + + if(fd < 0) { + stderr.printf("not connected!\n"); + return false; + } + + write(buf, 1); + read(buf, 1); + + if(buf[0] == '.') + return true; + else + return false; + } + + public bool set_speed(uint8 engine, uint8 speed) { + uint8[2] buf = new uint8[2]; + buf[0] = engine+1; + buf[1] = speed; + + if(fd < 0) { + stderr.printf("not connected!\n"); + return false; + } + + if(engine > 5) + return false; + + write(buf, 2); + read(buf, 2); + + if(buf[0] == '.') + return true; + else + return false; + } + + public bool set_speed_all(uint8 speed) { + uint8[2] buf = new uint8[2]; + buf[0] = 0xFF; + buf[1] = speed; + + if(fd < 0) { + stderr.printf("not connected!\n"); + return false; + } + + write(buf, 2); + read(buf, 2); + + if(buf[0] == '.') + return true; + else + return false; + } +} diff --git a/client/main.vala b/client/main.vala new file mode 100644 index 0000000..2c676a5 --- /dev/null +++ b/client/main.vala @@ -0,0 +1,58 @@ +public static void main(string[] args) { + var engines = new EngineController("/dev/ttyAMA0"); + + if(args.length < 2) { + stderr.printf("%s \n", args[0]); + stderr.printf("cmds: engine, enable, disable, nop\n"); + return; + } + + switch(args[1]) { + case "engine": + if(args.length < 4) { + stderr.printf("%s engine \n", args[0]); + return; + } + + uint8 engine = (uint8) int.parse(args[2]); + if("%u".printf(engine) != args[2]) + error("engine must be an integer"); + + uint8 speed = (uint8) int.parse(args[3]); + if("%u".printf(speed) != args[3]) + error("speed must be an integer"); + + if(engine == 6) + if(engines.set_speed_all(speed)) + stdout.printf("set speed of all engines to %u: ok\n", speed); + else + stdout.printf("set speed of all engines to %u: fail\n", speed); + if(engine <= 5) + if(engines.set_speed(engine, speed)) + stdout.printf("set speed of engine %u to %u: ok\n", engine, speed); + else + stdout.printf("set speed of engine %u to %u: fail\n", engine, speed); + break; + case "enable": + if(engines.enable()) + stdout.printf("enable motors: ok\n"); + else + stdout.printf("enable motors: fail\n"); + break; + case "disable": + if(engines.disable()) + stdout.printf("disable motors: ok\n"); + else + stdout.printf("disable motors: fail\n"); + break; + case "nop": + if(engines.no_operation()) + stdout.printf("no operation: ok\n"); + else + stdout.printf("no operation: fail\n"); + break; + default: + stderr.printf("cmd not supported!\n"); + break; + } +} diff --git a/doc/atmostripe-pinout.txt b/doc/atmostripe-pinout.txt new file mode 100644 index 0000000..6031357 --- /dev/null +++ b/doc/atmostripe-pinout.txt @@ -0,0 +1 @@ +Atmostripe ISP Pins: VCC, GND, RST, MOSI, MISO, SCK diff --git a/doc/isp-plug.svg b/doc/isp-plug.svg new file mode 100644 index 0000000..f68795d --- /dev/null +++ b/doc/isp-plug.svg @@ -0,0 +1,187 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + MISO: 1 + SCK: 3 + RESET: 5 + 2: VCC + 4: MOSI + 6: Ground + diff --git a/firmware/Makefile b/firmware/Makefile new file mode 100644 index 0000000..d9b89e7 --- /dev/null +++ b/firmware/Makefile @@ -0,0 +1,32 @@ +SOURCES := firmware.c +OBJECTS := $(patsubst %.c, %.o, $(SOURCES)) +CFLAGS := -O2 -std=c99 -mmcu=atmega48 -Wall -DF_CPU=16000000UL -DDEBUG=1 +LDFLAGS := -mmcu=atmega48 -lm + +all: fw.hex + +%.o: %.c + @echo "[CC] $<" + @avr-gcc -o $@ $(CFLAGS) -c $< + +fw.hex: fw.elf + @echo "[HEX] $@" + @avr-objcopy $^ -O ihex -R .eeprom $@ + +fw.elf: ${OBJECTS} + @echo "[LD] $@" + @avr-gcc -o $@ $(LDFLAGS) $^ + +fw.S: fw.elf + @avr-objdump -d fw.elf -h -m avr > fw.S + +clean: + @rm -f ${OBJECTS} fw.hex fw.elf fw.S + +backup: + avrdude -c buspirate -P /dev/ttyUSB0 -p m48 -U flash:r:"backup.hex":i noreset + +flash: + avrdude -c buspirate -P /dev/ttyUSB0 -p m48 -U flash:w:"fw.hex":i reset + +.PHONY: all clean backup flash diff --git a/firmware/firmware.c b/firmware/firmware.c new file mode 100644 index 0000000..9a04d41 --- /dev/null +++ b/firmware/firmware.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include + +/* 500Hz -> 2ms */ +#define MAXPULSFREQ 500 + +/* timer1 value for a 2ms puls */ +#define TIMER_MAXPULS F_CPU/MAXPULSFREQ + +/* min/max values */ +#define MINPULS 13500 +#define MAXPULS 30000 + +/* baud rate for serial communication */ +#define BAUD 9600UL + +/* hardware settings for baud rate */ +#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1) +#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) +#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) + +/* compile time check if baud rate is ok */ +#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010)) + #error systematic error of baud rate is > 1% +#endif + + +static uint16_t servo_pulslength[20]; +static uint8_t activated = 0; + +/* init IO pins */ +static inline void init_io() { + /* PB1 = left:red, PB2 = left:green, PB3 = left:blue */ + DDRB = (1 << PB1) | (1 << PB2) | (1 << PB3); + + /* PD3 = right:red, PD5 = right:green, PD6 = right:blue */ + DDRD = (1 << PD3) | (1 << PD5) | (1 << PD6); + + /* These pins are broken, so enable them */ + DDRC = (1 << PC4) | (1 << PC5) | (1 << PC6); + PORTC = (1 << PC4) | (1 << PC5) | (1 << PC6); +} + +/* INTERRUPT: timer for software pwm to control servos */ +ISR(TIMER1_OVF_vect) { + static uint8_t servo_indexhalf = 0; + + switch(servo_indexhalf) { + case 0 : PORTB |= (1 << PB1); break; + case 1 : PORTB &= ~(1 << PB1); break; + case 2 : PORTB |= (1 << PB2); break; + case 3 : PORTB &= ~(1 << PB2); break; + case 4 : PORTB |= (1 << PB3); break; + case 5 : PORTB &= ~(1 << PB3); break; + case 6 : PORTD |= (1 << PD3); break; + case 7 : PORTD &= ~(1 << PD3); break; + case 8 : PORTD |= (1 << PD5); break; + case 9 : PORTD &= ~(1 << PD5); break; + case 10: PORTD |= (1 << PD6); break; + case 11: PORTD &= ~(1 << PD6); break; + } + + /* set time for the next interrupt */ + TCNT1 = servo_pulslength[servo_indexhalf]; + + servo_indexhalf++; + + if(servo_indexhalf == 20) + servo_indexhalf = 0; +} + +/* setup serial communication */ +static inline void init_uart() { + UBRR0 = UBRR_VAL; + UCSR0B |= (1 << TXEN0); + UCSR0B |= (1 << RXEN0); + + /* Frame Format: Asynchron 8N1 */ + UCSR0C = (1<= 0x01 && c <= 0x06) { + uint8_t speed = uart_getc(); + servo_set(c-1, speed); + /* set speed for all engines */ + } else if(c == 0xFF) { + uint8_t speed = uart_getc(); + for(int i=0;i<6;i++) + servo_set(i, speed); + /* activate engines */ + } else if(c == 0xCA) { + if(uart_getc() != 0xFE) continue; + if(uart_getc() != 0xBA) continue; + if(uart_getc() != 0xBE) continue; + activated=1; + /* disable engines */ + } else if(c == 0xDE) { + if(uart_getc() != 0xAD) continue; + if(uart_getc() != 0xBA) continue; + if(uart_getc() != 0xBE) continue; + activated=0; + for(int i=0;i<6;i++) + servo_set(i, 0); + /* ignore unknown commands */ + } else if(c != 0x00) { + continue; + } + + /* acknowledge command */ + uart_putc('.'); + } + + return 0; +} -- cgit v1.2.3