+
+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=\
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)
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
+
--- /dev/null
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include <stdio.h>
+#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);
+ }
+}
+
--- /dev/null
+/*
+ * 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 <kas@fi.muni.cz> :-)
+ */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+#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();
+}
--- /dev/null
+#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 <kas@fi.muni.cz> :-)
+ */
+
+void rs485_init();
+void rs485_send(char *p);
+
+#endif /* _RS485_H */