From: Jan "Yenya" Kasprzak Date: Wed, 20 May 2015 19:11:15 +0000 (+0200) Subject: Firmware - initial revision. X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=commitdiff_plain;h=a3e62ade9b96ec057b5c624103e9738ddcb39fa0;hp=4677b0d00de7ae1e577cc8f5971b35e1bf7c0768;p=openparking.git Firmware - initial revision. --- diff --git a/Makefile b/Makefile index 076d26c..23528a0 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,25 @@ + +PROGRAM=firmware +SRC=firmware.c rs485.c +OBJ=$(SRC:.c=.o) + + +MCU=atmega328p +# AVRDUDE_MCU=$(MCU) +AVRDUDE_MCU=atmega328p +AVRDUDE_PROGRAMMER=arduino + +CFLAGS=-Wall -Os -mmcu=$(MCU) -DUSE_LOGGING=1 -DF_CPU=16000000UL -std=gnu99 +LDFLAGS= +AVRDUDE_FLAGS= -p$(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -P /dev/ttyUSB0 -b 57600 + +FORMAT=ihex + +CC=avr-gcc +OBJCOPY=avr-objcopy +OBJDUMP=avr-objdump +AVRDUDE=avrdude + WEB_DIR=/home/kas/html/board/ WEB_FILES=pcb-back.png pcb-front.png schematics.png handlebar.png GERBER_FILES=\ @@ -10,13 +32,14 @@ GERBER_FILES=\ board.outline.gbr \ board.plated-drill.cnc -all: - @echo "Usage: make [clean|web|gerber|fab|board.pcb]" +all: $(PROGRAM).hex $(PROGRAM).eep clean: rm -f board.cmd board.jpg board.net board.new.pcb board.pcb- \ board.png board.ps board.sch~ pcb-back.png pcb-front.png \ - schematics.png *.gbr *.cnc + schematics.png *.gbr *.cnc \ + $(PROGRAM).hex $(PROGRAM).eep $(PROGRAM).elf *.o *.s \ + eeprom.raw version.c web: $(WEB_FILES) test -d $(WEB_DIR) && install -m 644 $(WEB_FILES) $(WEB_DIR) @@ -61,3 +84,49 @@ fab: $(GERBER_FILES) echo "You may also want to do git tag gerber-fab-$$D" ' .PHONY: gerber + +program: $(PROGRAM).hex $(PROGRAM).eep + $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i -U eeprom:w:$(PROGRAM).eep:i + +program_flash: $(PROGRAM).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i + +program_eeprom: $(PROGRAM).eep + $(AVRDUDE) $(AVRDUDE_FLAGS) eeprom:w:$(PROGRAM).eep:i + +dump_eeprom: + $(AVRDUDE) $(AVRDUDE_FLAGS) -U eeprom:r:eeprom.raw:r + od -tx1 eeprom.raw + +objdump: $(PROGRAM).elf + $(OBJDUMP) --disassemble $< + +.PRECIOUS : $(OBJ) $(PROGRAM).elf + +%.hex: %.elf + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +%.eep: %.elf + $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -O $(FORMAT) $< $@ + +%.elf: $(OBJ) + $(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS) + +%.o: %.c lights.h Makefile + $(CC) -c $(CFLAGS) $< -o $@ + +%.s: %.c lights.h Makefile + $(CC) -S -c $(CFLAGS) $< -o $@ + +%.o: %.S + $(CC) -c $(CFLAGS) $< -o $@ + +clean: + +version.c: + ./version.pl > version.c + +.PHONY: all clean dump_eeprom program program_flash program_eeprom objdump \ + version.c + diff --git a/firmware.c b/firmware.c new file mode 100755 index 0000000..67508e6 --- /dev/null +++ b/firmware.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include "rs485.h" + +#define TIMEOUT 0x2FF + +#define N_TRIGGERS 3 +#define N_SENSORS 12 +#define N_TRIG_SENSORS 4 + +static int16_t distances[N_SENSORS]; + +static void pull_trigger(uint8_t trig) +{ + switch (trig) { + case 0: PORTD |= _BV(PD7); _delay_us(10); PORTD &= ~_BV(PD7); break; + case 1: PORTB |= _BV(PB4); _delay_us(10); PORTB &= ~_BV(PB4); break; + case 2: PORTC |= _BV(PC4); _delay_us(10); PORTC &= ~_BV(PC4); break; + } +} + +static uint16_t get_pin(uint8_t trig) +{ + switch (trig) { + case 0: return (PIND & 0x78) >> 3; + case 1: return PINB & 0x0F; + default: return PINC & 0x0F; + } +} + +static void do_measurement(unsigned char trig) +{ + uint16_t starttimes[N_TRIG_SENSORS], starttime; + uint8_t to_start = (1 << N_TRIG_SENSORS) - 1; + uint8_t to_measure = 0, i; + + pull_trigger(trig); + + starttime = TCNT1; + + while (to_start || to_measure) { + uint8_t bits = 0; + uint16_t now = TCNT1; + + if (now-starttime >= TIMEOUT) + break; + + bits = get_pin(trig); + + for (i = 0; i < N_TRIG_SENSORS; i++) { + uint8_t mask = 1 << i; + + if ((to_start & mask) && (bits & mask)) { + // echo start + starttimes[i] = now; + to_start &= ~mask; + to_measure |= mask; + } else if ((to_measure & mask) && !(bits & mask)) { + // echo end + to_measure &= ~mask; + distances[trig*N_TRIG_SENSORS + i] + = now - starttimes[i]; + } + } + } + + for (i = 0; i < N_TRIG_SENSORS; i++) + if (to_start & (1 << i)) + distances[trig*N_TRIG_SENSORS + i] = -1; + else if (to_measure & (1 << i)) + distances[trig*N_TRIG_SENSORS + i] = 0; +} + +static void do_measurements() +{ + uint8_t trig; + + for (trig = 0; trig < N_TRIGGERS; trig++) { + do_measurement(trig); + _delay_ms(200); + } +} + +static void led_set(uint8_t led, uint8_t state) +{ + if (led == 0) { + if (state) { + PORTD |= _BV(PD4); + // PORTC |= _BV(PC5); + } else { + PORTD &= ~_BV(PD4); + // PORTC &= ~_BV(PC5); + } + } else { + if (state) { + PORTB |= _BV(PB5); + } else { + PORTB &= ~_BV(PB5); + } + } +} + +int main() +{ + char obuf[120]; + + rs485_init(); + + // output pins + DDRD |= _BV(PD7); // Trig D + DDRB |= _BV(PB4) | _BV(PB5); // Trig B, LED 2 + DDRC |= _BV(PC4) | _BV(PC5); // Trig C, LED 1 + // temporary LED + DDRD |= _BV(PD4); + + // set up the timer + TCCR1A = 0; + TCCR1B = _BV(CS12)|_BV(CS10); // CLK/1024 + + // enable interrupts + sei(); + + while(1) { + do_measurements(); + + sprintf(obuf, "%3d %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d\r\n", + distances[0], distances[1], distances[2], + distances[3], distances[4], distances[5], + distances[6], distances[7], distances[8], + distances[9], distances[10], distances[11]); + + rs485_send(obuf); + led_set(0, + distances[4] > 100 || distances[11] > 100); + } +} + diff --git a/rs485.c b/rs485.c new file mode 100755 index 0000000..6d16a2a --- /dev/null +++ b/rs485.c @@ -0,0 +1,114 @@ +/* + * Loosely modelled after AVR-RS485 by Yoshinori Kohyama (http://algobit.jp/), + * available at https://github.com/kohyama/AVR-RS485/ + * + * All bugs by Jan "Yenya" Kasprzak :-) + */ + +#include +#include +#include + +#define BUFSIZE 128 // must be a power of two + +// configure the control pin +#define ctl_pin_setup() do { DDRD |= _BV(PD2); } while (0) +#define ctl_pin_on() do { PORTD |= _BV(PD2); } while (0) +#define ctl_pin_off() do { PORTD &= ~_BV(PD2); } while (0) + +#define BUFMASK (BUFSIZE-1) +#if (BUFSIZE & BUFMASK) +#error BUFSIZE must be a power of two +#endif + +#if BUFSIZE > 255 +typedef uint16_t bufptr_t; +#else +typedef uint8_t bufptr_t; +#endif + +#define bufptr_inc(x) ((x + 1) & BUFMASK) + +static volatile bufptr_t rx_head, rx_tail, tx_head, tx_tail; +static volatile char rxbuf[BUFSIZE], txbuf[BUFSIZE]; + +#define UART_BAUD 9600 +#define UBRR_VAL ((F_CPU + 8UL * UART_BAUD) / (16UL*UART_BAUD) - 1) +#define wait_one_byte() _delay_us(10*1000000/UART_BAUD) + +void rs485_init() +{ + rx_head = rx_tail = 0; + tx_head = tx_tail = 0; + + ctl_pin_off(); + ctl_pin_setup(); + + UBRR0 = UBRR_VAL; + UCSR0A = 0; + UCSR0B = _BV(RXCIE0)|_BV(RXEN0)|_BV(TXEN0); + UCSR0C = _BV(UCSZ01)|_BV(UCSZ00); +} + +void rs485_send(char *p) +{ + bufptr_t next; + + if (*p == '\0') + return; + + cli(); + + next = bufptr_inc(tx_head); + while (next != tx_tail && *p != '\0') { + txbuf[tx_head] = *p++; + tx_head = next; + next = bufptr_inc(tx_head); + } + ctl_pin_on(); + UCSR0B |= _BV(UDRIE0); + sei(); +} + +bufptr_t rs485_readln(char *buf, bufptr_t size) +{ + int n = 0; + + do { + cli(); + while (rx_head != rx_tail && n + 1 < size) { + buf[n++] = rxbuf[rx_tail]; + rx_tail = bufptr_inc(rx_tail); + } + sei(); + } while (n == 0 || buf[n - 1] != 0x0a); + buf[n] = '\0'; + + return n; +} + +ISR(USART_RX_vect) +{ + bufptr_t next; + + cli(); + next = bufptr_inc(rx_head); + rxbuf[rx_head] = UDR0; + if (next != rx_tail) + rx_head = next; + sei(); +} + +ISR(USART_UDRE_vect) +{ + cli(); + if (tx_head == tx_tail) { + UCSR0B &= ~_BV(UDRIE0); + wait_one_byte(); + ctl_pin_off(); + } else { + UDR0 = txbuf[tx_tail]; + tx_tail = bufptr_inc(tx_tail); + } + sei(); +} diff --git a/rs485.h b/rs485.h new file mode 100755 index 0000000..ab2735c --- /dev/null +++ b/rs485.h @@ -0,0 +1,14 @@ +#ifndef _RS485_H +#define _RS485_H + +/* + * Loosely modelled after AVR-RS485 by Yoshinori Kohyama (http://algobit.jp/), + * available at https://github.com/kohyama/AVR-RS485/ + * + * All bugs by Jan "Yenya" Kasprzak :-) + */ + +void rs485_init(); +void rs485_send(char *p); + +#endif /* _RS485_H */