]> www.fi.muni.cz Git - openparking.git/commitdiff
Firmware - initial revision.
authorJan "Yenya" Kasprzak <kas@fi.muni.cz>
Wed, 20 May 2015 19:11:15 +0000 (21:11 +0200)
committerJan "Yenya" Kasprzak <kas@fi.muni.cz>
Wed, 20 May 2015 19:11:15 +0000 (21:11 +0200)
Makefile
firmware.c [new file with mode: 0755]
rs485.c [new file with mode: 0755]
rs485.h [new file with mode: 0755]

index 076d26cdb436e01ca63551a87b40361579b0c3e4..23528a0821dd8cd349a3ebd319e2e9e77b12cb23 100644 (file)
--- 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 (executable)
index 0000000..67508e6
--- /dev/null
@@ -0,0 +1,139 @@
+#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);
+       }
+}
+
diff --git a/rs485.c b/rs485.c
new file mode 100755 (executable)
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 <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();
+}
diff --git a/rs485.h b/rs485.h
new file mode 100755 (executable)
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 <kas@fi.muni.cz> :-)
+ */
+
+void rs485_init();
+void rs485_send(char *p);
+
+#endif /* _RS485_H */