From 513810e138fbe4b0cbccf3b3ab15930891fde90a Mon Sep 17 00:00:00 2001 From: Michael Hayes Date: Sat, 27 Aug 2011 01:46:08 +0000 Subject: [PATCH] Generate callgraph --- README | 9 + apps/README | 16 + apps/bounce1/Makefile | 54 ++ apps/bounce1/Makefile.test | 48 + apps/bounce1/bounce1.c | 113 +++ apps/bounce2/Makefile | 60 ++ apps/bounce2/Makefile.test | 54 ++ apps/bounce2/bounce2.c | 74 ++ apps/bounce3/Makefile | 63 ++ apps/bounce3/Makefile.test | 57 ++ apps/bounce3/bounce3.c | 73 ++ apps/bounce4/Makefile | 66 ++ apps/bounce4/Makefile.test | 60 ++ apps/bounce4/bounce4.c | 60 ++ apps/bounce5/Makefile | 66 ++ apps/bounce5/Makefile.test | 60 ++ apps/bounce5/bounce5.c | 96 ++ apps/chooser/Makefile | 66 ++ apps/chooser/Makefile.test | 60 ++ apps/chooser/chooser.c | 81 ++ apps/demo1/Makefile | 48 + apps/demo1/Makefile.test | 42 + apps/demo1/demo1.c | 21 + apps/demo2/Makefile | 51 + apps/demo2/Makefile.test | 45 + apps/demo2/demo2.c | 24 + apps/fonttest1/Makefile | 66 ++ apps/fonttest1/Makefile.test | 60 ++ apps/fonttest1/fonttest1.c | 105 ++ apps/game/README | 8 + apps/hello1/Makefile | 57 ++ apps/hello1/Makefile.test | 51 + apps/hello1/hello1.c | 60 ++ apps/hello1/simplefont.h | 79 ++ apps/hello2/Makefile | 63 ++ apps/hello2/Makefile.test | 57 ++ apps/hello2/hello2.c | 44 + apps/hello3/Makefile | 63 ++ apps/hello3/Makefile.test | 57 ++ apps/hello3/hello3.c | 45 + apps/hello4/Makefile | 66 ++ apps/hello4/Makefile.test | 60 ++ apps/hello4/hello4.c | 56 ++ apps/ir_grab1/Makefile | 69 ++ apps/ir_grab1/Makefile.test | 63 ++ apps/ir_grab1/ir_grab1.c | 135 +++ apps/ir_grab2/Makefile | 72 ++ apps/ir_grab2/Makefile.test | 66 ++ apps/ir_grab2/ir_grab2.c | 153 +++ apps/ir_grab3/Makefile | 78 ++ apps/ir_grab3/Makefile.test | 72 ++ apps/ir_grab3/ir_grab3.c | 245 +++++ apps/ir_serial_test1/Makefile | 72 ++ apps/ir_serial_test1/Makefile.test | 66 ++ apps/ir_serial_test1/ir_serial_test1.c | 101 ++ apps/ir_serial_test2/Makefile | 72 ++ apps/ir_serial_test2/Makefile.test | 66 ++ apps/ir_serial_test2/ir_serial_test2.c | 83 ++ apps/ir_serial_test3/Makefile | 72 ++ apps/ir_serial_test3/Makefile.test | 66 ++ apps/ir_serial_test3/ir_serial_test3.c | 77 ++ apps/ir_uart_test1/Makefile | 78 ++ apps/ir_uart_test1/Makefile.test | 63 ++ apps/ir_uart_test1/ir_uart_test1.c | 83 ++ apps/ir_uart_test2/Makefile | 78 ++ apps/ir_uart_test2/Makefile.test | 63 ++ apps/ir_uart_test2/ir_uart_test2.c | 77 ++ apps/led1/Makefile | 57 ++ apps/led1/Makefile.test | 51 + apps/led1/led1.c | 40 + apps/led2/Makefile | 60 ++ apps/led2/Makefile.test | 54 ++ apps/led2/led2.c | 69 ++ apps/led3/Makefile | 60 ++ apps/led3/Makefile.test | 54 ++ apps/led3/led3.c | 68 ++ apps/led4/Makefile | 60 ++ apps/led4/Makefile.test | 54 ++ apps/led4/led4.c | 68 ++ apps/led5/Makefile | 54 ++ apps/led5/Makefile.test | 48 + apps/led5/led5.c | 31 + apps/pio1/Makefile | 48 + apps/pio1/Makefile.test | 42 + apps/pio1/pio1.c | 21 + apps/pio2/Makefile | 48 + apps/pio2/Makefile.test | 42 + apps/pio2/pio2.c | 27 + apps/pio3/Makefile | 51 + apps/pio3/Makefile.test | 45 + apps/pio3/pio3.c | 40 + apps/scribble1/Makefile | 72 ++ apps/scribble1/Makefile.test | 66 ++ apps/scribble1/scribble1.c | 104 ++ apps/snake1/Makefile | 66 ++ apps/snake1/Makefile.test | 60 ++ apps/snake1/snake1.c | 127 +++ apps/space10/Makefile | 81 ++ apps/space10/Makefile.test | 75 ++ apps/space10/flasher.c | 100 ++ apps/space10/flasher.h | 97 ++ apps/space10/space10.c | 363 +++++++ apps/space10/spacey.c | 578 +++++++++++ apps/space10/spacey.h | 152 +++ apps/space10/ticker.c | 6 + apps/space10/ticker.h | 45 + apps/space11/Makefile | 81 ++ apps/space11/Makefile.test | 75 ++ apps/space11/flasher.c | 100 ++ apps/space11/flasher.h | 97 ++ apps/space11/space11.c | 459 +++++++++ apps/space11/spacey.c | 578 +++++++++++ apps/space11/spacey.h | 152 +++ apps/space11/ticker.c | 6 + apps/space11/ticker.h | 45 + apps/space9/Makefile | 81 ++ apps/space9/Makefile.test | 75 ++ apps/space9/flasher.c | 100 ++ apps/space9/flasher.h | 97 ++ apps/space9/space9.c | 432 +++++++++ apps/space9/spacey.c | 578 +++++++++++ apps/space9/spacey.h | 152 +++ apps/space9/ticker.c | 6 + apps/space9/ticker.h | 45 + apps/spacey_remote1/Makefile | 72 ++ apps/spacey_remote1/Makefile.test | 66 ++ apps/spacey_remote1/spacey_remote1.c | 92 ++ apps/steer1/Makefile | 66 ++ apps/steer1/Makefile.test | 60 ++ apps/steer1/steer1.c | 118 +++ apps/task1/Makefile | 57 ++ apps/task1/Makefile.test | 51 + apps/task1/task1.c | 36 + apps/task2/Makefile | 66 ++ apps/task2/Makefile.test | 60 ++ apps/task2/task2.c | 69 ++ apps/tdmdemo1/Makefile | 57 ++ apps/tdmdemo1/Makefile.test | 51 + apps/tdmdemo1/tdmdemo1.c | 80 ++ apps/tdmdemo2/Makefile | 60 ++ apps/tdmdemo2/Makefile.test | 54 ++ apps/tdmdemo2/tdmdemo2.c | 97 ++ apps/updown1/Makefile | 63 ++ apps/updown1/Makefile.test | 57 ++ apps/updown1/updown1.c | 68 ++ apps/updown2/Makefile | 66 ++ apps/updown2/Makefile.test | 60 ++ apps/updown2/updown2.c | 80 ++ doc/Makefile | 15 + doc/README | 4 + doc/UCFK4.pdf | Bin 0 -> 439174 bytes doc/doxygen.config | 1241 ++++++++++++++++++++++++ doc/main_page.dox | 259 +++++ drivers/avr/README | 10 + drivers/avr/adc.c | 90 ++ drivers/avr/adc.h | 63 ++ drivers/avr/bits.h | 22 + drivers/avr/delay.h | 93 ++ drivers/avr/eeprom.c | 71 ++ drivers/avr/eeprom.h | 30 + drivers/avr/ir_uart.c | 85 ++ drivers/avr/ir_uart.h | 95 ++ drivers/avr/pio-simple.c | 306 ++++++ drivers/avr/pio-simple.h | 77 ++ drivers/avr/pio.c | 9 + drivers/avr/pio.h | 253 +++++ drivers/avr/prescale.c | 35 + drivers/avr/prescale.h | 15 + drivers/avr/system.c | 40 + drivers/avr/system.h | 74 ++ drivers/avr/target.h | 73 ++ drivers/avr/timer.c | 74 ++ drivers/avr/timer.h | 97 ++ drivers/avr/timer0.c | 182 ++++ drivers/avr/timer0.h | 48 + drivers/avr/usart1.c | 98 ++ drivers/avr/usart1.h | 60 ++ drivers/button.c | 123 +++ drivers/button.h | 74 ++ drivers/display.c | 101 ++ drivers/display.h | 52 + drivers/ir.c | 95 ++ drivers/ir.h | 38 + drivers/ir_serial.c | 152 +++ drivers/ir_serial.h | 117 +++ drivers/led.c | 50 + drivers/led.h | 44 + drivers/ledmat.c | 78 ++ drivers/ledmat.h | 25 + drivers/navswitch.c | 140 +++ drivers/navswitch.h | 81 ++ drivers/test/avrtest.h | 33 + drivers/test/delay.h | 1 + drivers/test/eeprom.c | 85 ++ drivers/test/eeprom.h | 30 + drivers/test/ir_uart.c | 64 ++ drivers/test/ir_uart.h | 52 + drivers/test/mgetkey.c | 94 ++ drivers/test/mgetkey.h | 19 + drivers/test/pio.c | 7 + drivers/test/pio.h | 265 +++++ drivers/test/system.c | 402 ++++++++ drivers/test/system.h | 74 ++ drivers/test/target.h | 57 ++ drivers/test/timer.c | 72 ++ drivers/test/timer.h | 97 ++ etc/Makefile | 102 ++ etc/Makefile.template | 38 + etc/Makefile.test.template | 30 + etc/README | 2 + etc/graphdeps.py | 213 ++++ etc/makemake.py | 481 +++++++++ fonts/Makefile | 18 + fonts/README | 123 +++ fonts/font3x5_1.h | 162 ++++ fonts/font3x5_1.txt | 358 +++++++ fonts/font5x7_1.h | 234 +++++ fonts/font5x7_1.txt | 764 +++++++++++++++ fonts/font5x7_2.h | 186 ++++ fonts/font5x7_2.txt | 332 +++++++ fonts/fontgen.c | 462 +++++++++ utils/README | 4 + utils/boing.c | 72 ++ utils/boing.h | 42 + utils/font.h | 41 + utils/pacer.c | 30 + utils/pacer.h | 46 + utils/task.c | 76 ++ utils/task.h | 137 +++ utils/tinygl.c | 278 ++++++ utils/tinygl.h | 182 ++++ utils/uint8toa.c | 34 + utils/uint8toa.h | 22 + 233 files changed, 22993 insertions(+) create mode 100644 README create mode 100644 apps/README create mode 100644 apps/bounce1/Makefile create mode 100644 apps/bounce1/Makefile.test create mode 100644 apps/bounce1/bounce1.c create mode 100644 apps/bounce2/Makefile create mode 100644 apps/bounce2/Makefile.test create mode 100644 apps/bounce2/bounce2.c create mode 100644 apps/bounce3/Makefile create mode 100644 apps/bounce3/Makefile.test create mode 100644 apps/bounce3/bounce3.c create mode 100644 apps/bounce4/Makefile create mode 100644 apps/bounce4/Makefile.test create mode 100644 apps/bounce4/bounce4.c create mode 100644 apps/bounce5/Makefile create mode 100644 apps/bounce5/Makefile.test create mode 100644 apps/bounce5/bounce5.c create mode 100644 apps/chooser/Makefile create mode 100644 apps/chooser/Makefile.test create mode 100644 apps/chooser/chooser.c create mode 100644 apps/demo1/Makefile create mode 100644 apps/demo1/Makefile.test create mode 100644 apps/demo1/demo1.c create mode 100644 apps/demo2/Makefile create mode 100644 apps/demo2/Makefile.test create mode 100644 apps/demo2/demo2.c create mode 100644 apps/fonttest1/Makefile create mode 100644 apps/fonttest1/Makefile.test create mode 100644 apps/fonttest1/fonttest1.c create mode 100644 apps/game/README create mode 100644 apps/hello1/Makefile create mode 100644 apps/hello1/Makefile.test create mode 100644 apps/hello1/hello1.c create mode 100644 apps/hello1/simplefont.h create mode 100644 apps/hello2/Makefile create mode 100644 apps/hello2/Makefile.test create mode 100644 apps/hello2/hello2.c create mode 100644 apps/hello3/Makefile create mode 100644 apps/hello3/Makefile.test create mode 100644 apps/hello3/hello3.c create mode 100644 apps/hello4/Makefile create mode 100644 apps/hello4/Makefile.test create mode 100644 apps/hello4/hello4.c create mode 100644 apps/ir_grab1/Makefile create mode 100644 apps/ir_grab1/Makefile.test create mode 100644 apps/ir_grab1/ir_grab1.c create mode 100644 apps/ir_grab2/Makefile create mode 100644 apps/ir_grab2/Makefile.test create mode 100644 apps/ir_grab2/ir_grab2.c create mode 100644 apps/ir_grab3/Makefile create mode 100644 apps/ir_grab3/Makefile.test create mode 100644 apps/ir_grab3/ir_grab3.c create mode 100644 apps/ir_serial_test1/Makefile create mode 100644 apps/ir_serial_test1/Makefile.test create mode 100644 apps/ir_serial_test1/ir_serial_test1.c create mode 100644 apps/ir_serial_test2/Makefile create mode 100644 apps/ir_serial_test2/Makefile.test create mode 100644 apps/ir_serial_test2/ir_serial_test2.c create mode 100644 apps/ir_serial_test3/Makefile create mode 100644 apps/ir_serial_test3/Makefile.test create mode 100644 apps/ir_serial_test3/ir_serial_test3.c create mode 100644 apps/ir_uart_test1/Makefile create mode 100644 apps/ir_uart_test1/Makefile.test create mode 100644 apps/ir_uart_test1/ir_uart_test1.c create mode 100644 apps/ir_uart_test2/Makefile create mode 100644 apps/ir_uart_test2/Makefile.test create mode 100644 apps/ir_uart_test2/ir_uart_test2.c create mode 100644 apps/led1/Makefile create mode 100644 apps/led1/Makefile.test create mode 100644 apps/led1/led1.c create mode 100644 apps/led2/Makefile create mode 100644 apps/led2/Makefile.test create mode 100644 apps/led2/led2.c create mode 100644 apps/led3/Makefile create mode 100644 apps/led3/Makefile.test create mode 100644 apps/led3/led3.c create mode 100644 apps/led4/Makefile create mode 100644 apps/led4/Makefile.test create mode 100644 apps/led4/led4.c create mode 100644 apps/led5/Makefile create mode 100644 apps/led5/Makefile.test create mode 100644 apps/led5/led5.c create mode 100644 apps/pio1/Makefile create mode 100644 apps/pio1/Makefile.test create mode 100644 apps/pio1/pio1.c create mode 100644 apps/pio2/Makefile create mode 100644 apps/pio2/Makefile.test create mode 100644 apps/pio2/pio2.c create mode 100644 apps/pio3/Makefile create mode 100644 apps/pio3/Makefile.test create mode 100644 apps/pio3/pio3.c create mode 100644 apps/scribble1/Makefile create mode 100644 apps/scribble1/Makefile.test create mode 100644 apps/scribble1/scribble1.c create mode 100644 apps/snake1/Makefile create mode 100644 apps/snake1/Makefile.test create mode 100644 apps/snake1/snake1.c create mode 100644 apps/space10/Makefile create mode 100644 apps/space10/Makefile.test create mode 100644 apps/space10/flasher.c create mode 100644 apps/space10/flasher.h create mode 100644 apps/space10/space10.c create mode 100644 apps/space10/spacey.c create mode 100644 apps/space10/spacey.h create mode 100644 apps/space10/ticker.c create mode 100644 apps/space10/ticker.h create mode 100644 apps/space11/Makefile create mode 100644 apps/space11/Makefile.test create mode 100644 apps/space11/flasher.c create mode 100644 apps/space11/flasher.h create mode 100644 apps/space11/space11.c create mode 100644 apps/space11/spacey.c create mode 100644 apps/space11/spacey.h create mode 100644 apps/space11/ticker.c create mode 100644 apps/space11/ticker.h create mode 100644 apps/space9/Makefile create mode 100644 apps/space9/Makefile.test create mode 100644 apps/space9/flasher.c create mode 100644 apps/space9/flasher.h create mode 100644 apps/space9/space9.c create mode 100644 apps/space9/spacey.c create mode 100644 apps/space9/spacey.h create mode 100644 apps/space9/ticker.c create mode 100644 apps/space9/ticker.h create mode 100644 apps/spacey_remote1/Makefile create mode 100644 apps/spacey_remote1/Makefile.test create mode 100644 apps/spacey_remote1/spacey_remote1.c create mode 100644 apps/steer1/Makefile create mode 100644 apps/steer1/Makefile.test create mode 100644 apps/steer1/steer1.c create mode 100644 apps/task1/Makefile create mode 100644 apps/task1/Makefile.test create mode 100644 apps/task1/task1.c create mode 100644 apps/task2/Makefile create mode 100644 apps/task2/Makefile.test create mode 100644 apps/task2/task2.c create mode 100644 apps/tdmdemo1/Makefile create mode 100644 apps/tdmdemo1/Makefile.test create mode 100644 apps/tdmdemo1/tdmdemo1.c create mode 100644 apps/tdmdemo2/Makefile create mode 100644 apps/tdmdemo2/Makefile.test create mode 100644 apps/tdmdemo2/tdmdemo2.c create mode 100644 apps/updown1/Makefile create mode 100644 apps/updown1/Makefile.test create mode 100644 apps/updown1/updown1.c create mode 100644 apps/updown2/Makefile create mode 100644 apps/updown2/Makefile.test create mode 100644 apps/updown2/updown2.c create mode 100644 doc/Makefile create mode 100644 doc/README create mode 100644 doc/UCFK4.pdf create mode 100644 doc/doxygen.config create mode 100644 doc/main_page.dox create mode 100644 drivers/avr/README create mode 100644 drivers/avr/adc.c create mode 100644 drivers/avr/adc.h create mode 100644 drivers/avr/bits.h create mode 100644 drivers/avr/delay.h create mode 100644 drivers/avr/eeprom.c create mode 100644 drivers/avr/eeprom.h create mode 100644 drivers/avr/ir_uart.c create mode 100644 drivers/avr/ir_uart.h create mode 100644 drivers/avr/pio-simple.c create mode 100644 drivers/avr/pio-simple.h create mode 100644 drivers/avr/pio.c create mode 100644 drivers/avr/pio.h create mode 100644 drivers/avr/prescale.c create mode 100644 drivers/avr/prescale.h create mode 100644 drivers/avr/system.c create mode 100644 drivers/avr/system.h create mode 100644 drivers/avr/target.h create mode 100644 drivers/avr/timer.c create mode 100644 drivers/avr/timer.h create mode 100644 drivers/avr/timer0.c create mode 100644 drivers/avr/timer0.h create mode 100644 drivers/avr/usart1.c create mode 100644 drivers/avr/usart1.h create mode 100644 drivers/button.c create mode 100644 drivers/button.h create mode 100644 drivers/display.c create mode 100644 drivers/display.h create mode 100644 drivers/ir.c create mode 100644 drivers/ir.h create mode 100644 drivers/ir_serial.c create mode 100644 drivers/ir_serial.h create mode 100644 drivers/led.c create mode 100644 drivers/led.h create mode 100644 drivers/ledmat.c create mode 100644 drivers/ledmat.h create mode 100644 drivers/navswitch.c create mode 100644 drivers/navswitch.h create mode 100644 drivers/test/avrtest.h create mode 100644 drivers/test/delay.h create mode 100644 drivers/test/eeprom.c create mode 100644 drivers/test/eeprom.h create mode 100644 drivers/test/ir_uart.c create mode 100644 drivers/test/ir_uart.h create mode 100644 drivers/test/mgetkey.c create mode 100644 drivers/test/mgetkey.h create mode 100644 drivers/test/pio.c create mode 100644 drivers/test/pio.h create mode 100644 drivers/test/system.c create mode 100644 drivers/test/system.h create mode 100644 drivers/test/target.h create mode 100644 drivers/test/timer.c create mode 100644 drivers/test/timer.h create mode 100644 etc/Makefile create mode 100644 etc/Makefile.template create mode 100644 etc/Makefile.test.template create mode 100644 etc/README create mode 100755 etc/graphdeps.py create mode 100755 etc/makemake.py create mode 100644 fonts/Makefile create mode 100644 fonts/README create mode 100644 fonts/font3x5_1.h create mode 100644 fonts/font3x5_1.txt create mode 100644 fonts/font5x7_1.h create mode 100644 fonts/font5x7_1.txt create mode 100644 fonts/font5x7_2.h create mode 100644 fonts/font5x7_2.txt create mode 100644 fonts/fontgen.c create mode 100644 utils/README create mode 100644 utils/boing.c create mode 100644 utils/boing.h create mode 100644 utils/font.h create mode 100644 utils/pacer.c create mode 100644 utils/pacer.h create mode 100644 utils/task.c create mode 100644 utils/task.h create mode 100644 utils/tinygl.c create mode 100644 utils/tinygl.h create mode 100644 utils/uint8toa.c create mode 100644 utils/uint8toa.h diff --git a/README b/README new file mode 100644 index 0000000..9d9c4da --- /dev/null +++ b/README @@ -0,0 +1,9 @@ +apps --- contains a sub-directory for each application +drivers --- device driver modules (hardware independent) +drivers/avr --- device driver modules specifically for AVR architecture +drivers/test --- device driver modules for test scaffold +common --- common modules (utilities, etc.) +doc --- documentation +etc --- miscellaneous scripts +fonts --- fonts and font creation program + diff --git a/apps/README b/apps/README new file mode 100644 index 0000000..83b8ab5 --- /dev/null +++ b/apps/README @@ -0,0 +1,16 @@ +This directory contains a number of test applications contained in +their own sub-directories. Within each sub-directory are two +makefiles; one called Makefile and the other called Makefile.test. By +default when make is run it will read Makefile. + +make --- builds the application for the ATmega8 +make program --- programs the application into the ATmega8 flash memory +make clean --- deletes the object and executable files + + +The other makefile, Makefile.test, will compile the application +natively for the PC with a test scaffold replacing the AVR dependent +modules. This allows the application to be run and debugged on a PC. + +make -f Makefile.test --- builds the application for the PC +make -f Makefile.test clean --- deletes the object and executable files diff --git a/apps/bounce1/Makefile b/apps/bounce1/Makefile new file mode 100644 index 0000000..77d2354 --- /dev/null +++ b/apps/bounce1/Makefile @@ -0,0 +1,54 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for bounce1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: bounce1.out + + +# Compile: create object files from C source files. +bounce1.o: bounce1.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +bounce1.out: bounce1.o pacer.o pio.o system.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: bounce1.out + $(OBJCOPY) -O ihex bounce1.out bounce1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash bounce1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/bounce1/Makefile.test b/apps/bounce1/Makefile.test new file mode 100644 index 0000000..ab80f24 --- /dev/null +++ b/apps/bounce1/Makefile.test @@ -0,0 +1,48 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for bounce1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: bounce1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +bounce1-test.o: bounce1.c ../../utils/pacer.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +bounce1: timer-test.o mgetkey-test.o bounce1-test.o pacer-test.o pio-test.o system-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) bounce1 timer-test.o mgetkey-test.o bounce1-test.o pacer-test.o pio-test.o system-test.o + + diff --git a/apps/bounce1/bounce1.c b/apps/bounce1/bounce1.c new file mode 100644 index 0000000..1c96006 --- /dev/null +++ b/apps/bounce1/bounce1.c @@ -0,0 +1,113 @@ +/** @file bounce1.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Simple bouncing dot program + + @defgroup bounce1 Bounce1 application +*/ +#include "system.h" +#include "pacer.h" +#include "pio.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 10 + + +/* Define PIO pins driving LED matrix rows and columns. */ +static pio_t ledmat_rows[] = +{ + LEDMAT_ROW1_PIO, LEDMAT_ROW2_PIO, LEDMAT_ROW3_PIO, LEDMAT_ROW4_PIO, + LEDMAT_ROW5_PIO, LEDMAT_ROW6_PIO, LEDMAT_ROW7_PIO +}; +static pio_t ledmat_cols[] = +{ + LEDMAT_COL1_PIO, LEDMAT_COL2_PIO, LEDMAT_COL3_PIO, + LEDMAT_COL4_PIO, LEDMAT_COL5_PIO +}; + + +/** Turn single LED within matrix on or off. + @param col LED column number + @param row LED row number + @param state LED state */ +static void ledmat_pixel_set (int col, int row, bool state) +{ + if (state) + { + pio_output_low (ledmat_rows[row]); + pio_output_low (ledmat_cols[col]); + } + else + { + pio_output_high (ledmat_rows[row]); + pio_output_high (ledmat_cols[col]); + } +} + + +/** Initialise LED matrix PIO pins. */ +static void ledmat_init (void) +{ + uint8_t row; + uint8_t col; + + for (row = 0; row < 7; row++) + { + pio_config_set (ledmat_rows[row], PIO_OUTPUT_HIGH); + pio_output_high (ledmat_rows[row]); + } + + for (col = 0; col < 5; col++) + { + pio_config_set (ledmat_cols[col], PIO_OUTPUT_HIGH); + pio_output_high (ledmat_cols[col]); + } +} + + +int main (void) +{ + int row; + int col; + int rowinc; + int colinc; + + system_init (); + ledmat_init (); + + row = 3; + col = 2; + rowinc = 1; + colinc = 1; + + ledmat_pixel_set (col, row, 1); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + ledmat_pixel_set (col, row, 0); + + col += colinc; + row += rowinc; + + if (row > 6 || row < 0) + { + row -= rowinc * 2; + rowinc = -rowinc; + } + + if (col > 4 || col < 0) + { + col -= colinc * 2; + colinc = -colinc; + } + + ledmat_pixel_set (col, row, 1); + } +} diff --git a/apps/bounce2/Makefile b/apps/bounce2/Makefile new file mode 100644 index 0000000..d6f20af --- /dev/null +++ b/apps/bounce2/Makefile @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for bounce2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: bounce2.out + + +# Compile: create object files from C source files. +bounce2.o: bounce2.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +bounce2.out: bounce2.o display.o ledmat.o pacer.o pio.o system.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: bounce2.out + $(OBJCOPY) -O ihex bounce2.out bounce2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash bounce2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/bounce2/Makefile.test b/apps/bounce2/Makefile.test new file mode 100644 index 0000000..a393149 --- /dev/null +++ b/apps/bounce2/Makefile.test @@ -0,0 +1,54 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for bounce2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: bounce2 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +bounce2-test.o: bounce2.c ../../utils/pacer.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +bounce2: timer-test.o display-test.o pio-test.o pacer-test.o ledmat-test.o system-test.o mgetkey-test.o bounce2-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) bounce2 timer-test.o display-test.o pio-test.o pacer-test.o ledmat-test.o system-test.o mgetkey-test.o bounce2-test.o + + diff --git a/apps/bounce2/bounce2.c b/apps/bounce2/bounce2.c new file mode 100644 index 0000000..c600ac2 --- /dev/null +++ b/apps/bounce2/bounce2.c @@ -0,0 +1,74 @@ +/** @file bounce2.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Simple bouncing dot program + + @defgroup bounce2 Bounce2 application +*/ +#include "system.h" +#include "pacer.h" +#include "display.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 200 + + +int main (void) +{ + int row; + int col; + int rowinc; + int colinc; + int tick; + + system_init (); + display_init (); + + row = 3; + col = 2; + rowinc = 1; + colinc = 1; + + display_pixel_set (col, row, 1); + + pacer_init (LOOP_RATE); + tick = 0; + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tick++; + + if (tick >= 10) + { + tick = 0; + + /* Erase previous position. */ + display_pixel_set (col, row, 0); + + col += colinc; + row += rowinc; + + if (row > 6 || row < 0) + { + row -= rowinc * 2; + rowinc = -rowinc; + } + + if (col > 4 || col < 0) + { + col -= colinc * 2; + colinc = -colinc; + } + + /* Draw new position. */ + display_pixel_set (col, row, 1); + } + + display_update (); + } +} diff --git a/apps/bounce3/Makefile b/apps/bounce3/Makefile new file mode 100644 index 0000000..c370a8b --- /dev/null +++ b/apps/bounce3/Makefile @@ -0,0 +1,63 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for bounce3 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: bounce3.out + + +# Compile: create object files from C source files. +bounce3.o: bounce3.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +bounce3.out: bounce3.o display.o ledmat.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: bounce3.out + $(OBJCOPY) -O ihex bounce3.out bounce3.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash bounce3.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/bounce3/Makefile.test b/apps/bounce3/Makefile.test new file mode 100644 index 0000000..4d405e5 --- /dev/null +++ b/apps/bounce3/Makefile.test @@ -0,0 +1,57 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for bounce3 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: bounce3 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +bounce3-test.o: bounce3.c ../../utils/pacer.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +bounce3: timer-test.o display-test.o pio-test.o pacer-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o bounce3-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) bounce3 timer-test.o display-test.o pio-test.o pacer-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o bounce3-test.o + + diff --git a/apps/bounce3/bounce3.c b/apps/bounce3/bounce3.c new file mode 100644 index 0000000..b7dc0fe --- /dev/null +++ b/apps/bounce3/bounce3.c @@ -0,0 +1,73 @@ +/** @file bounce3.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Simple bouncing dot program. This has a deliberate bug! + + @defgroup bounce3 Bounce3 application +*/ +#include "system.h" +#include "pacer.h" +#include "tinygl.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 200 + + +int main (void) +{ + tinygl_point_t pos; + int xinc; + int yinc; + int tick; + + system_init (); + tinygl_init (LOOP_RATE); + + pos.y = 3; + pos.x = 2; + yinc = 1; + xinc = 1; + + tinygl_draw_point (pos, 1); + + pacer_init (LOOP_RATE); + tick = 0; + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tick++; + + if (tick >= 10) + { + tick = 0; + + /* Erase previous position. */ + tinygl_draw_point (pos, 0); + + pos.x += xinc; + pos.y += yinc; + + if (pos.y > 6) + { + pos.y -= yinc * 2; + yinc = -yinc; + } + + if (pos.x > 4) + { + pos.x -= xinc * 2; + xinc = -xinc; + } + + /* Draw new position. */ + tinygl_draw_point (pos, 1); + } + + tinygl_update (); + } +} diff --git a/apps/bounce4/Makefile b/apps/bounce4/Makefile new file mode 100644 index 0000000..85180d8 --- /dev/null +++ b/apps/bounce4/Makefile @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for bounce4 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: bounce4.out + + +# Compile: create object files from C source files. +bounce4.o: bounce4.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/boing.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +boing.o: ../../utils/boing.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/boing.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +bounce4.out: boing.o bounce4.o display.o ledmat.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: bounce4.out + $(OBJCOPY) -O ihex bounce4.out bounce4.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash bounce4.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/bounce4/Makefile.test b/apps/bounce4/Makefile.test new file mode 100644 index 0000000..bb764b6 --- /dev/null +++ b/apps/bounce4/Makefile.test @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for bounce4 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: bounce4 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +boing-test.o: ../../utils/boing.c ../../drivers/display.h ../../utils/boing.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +bounce4-test.o: bounce4.c ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../utils/boing.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +bounce4: timer-test.o display-test.o pio-test.o pacer-test.o boing-test.o ledmat-test.o tinygl-test.o bounce4-test.o system-test.o mgetkey-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) bounce4 timer-test.o display-test.o pio-test.o pacer-test.o boing-test.o ledmat-test.o tinygl-test.o bounce4-test.o system-test.o mgetkey-test.o + + diff --git a/apps/bounce4/bounce4.c b/apps/bounce4/bounce4.c new file mode 100644 index 0000000..01cb389 --- /dev/null +++ b/apps/bounce4/bounce4.c @@ -0,0 +1,60 @@ +/** @file bounce4.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Multiple bouncing dot program + + @defgroup bounce4 Bounce4 application +*/ +#include "system.h" +#include "pacer.h" +#include "tinygl.h" +#include "boing.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 200 + + +int main (void) +{ + int tick; + boing_state_t balls[2]; + + system_init (); + tinygl_init (LOOP_RATE); + + pacer_init (LOOP_RATE); + tick = 0; + + balls[0] = boing_init (0, 1, DIR_NE); + balls[1] = boing_init (4, 5, DIR_SE); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tick++; + + if (tick >= 40) + { + int i; + + tick = 0; + + for (i = 0; i < 2; i++) + { + /* Erase previous position. */ + tinygl_draw_point (balls[i].pos, 0); + + balls[i] = boing_update (balls[i]); + + /* Draw new position. */ + tinygl_draw_point (balls[i].pos, 1); + } + } + + tinygl_update (); + } +} diff --git a/apps/bounce5/Makefile b/apps/bounce5/Makefile new file mode 100644 index 0000000..e07e5cd --- /dev/null +++ b/apps/bounce5/Makefile @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for bounce5 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: bounce5.out + + +# Compile: create object files from C source files. +bounce5.o: bounce5.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/boing.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +boing.o: ../../utils/boing.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/boing.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +bounce5.out: boing.o bounce5.o display.o ledmat.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: bounce5.out + $(OBJCOPY) -O ihex bounce5.out bounce5.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash bounce5.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/bounce5/Makefile.test b/apps/bounce5/Makefile.test new file mode 100644 index 0000000..2a18831 --- /dev/null +++ b/apps/bounce5/Makefile.test @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for bounce5 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: bounce5 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +boing-test.o: ../../utils/boing.c ../../drivers/display.h ../../utils/boing.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +bounce5-test.o: bounce5.c ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../utils/boing.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +bounce5: timer-test.o display-test.o pio-test.o pacer-test.o boing-test.o bounce5-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) bounce5 timer-test.o display-test.o pio-test.o pacer-test.o boing-test.o bounce5-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o + + diff --git a/apps/bounce5/bounce5.c b/apps/bounce5/bounce5.c new file mode 100644 index 0000000..01a0a6f --- /dev/null +++ b/apps/bounce5/bounce5.c @@ -0,0 +1,96 @@ +/** @file bounce5.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Multiple bouncing dot program + + @defgroup bounce5 Bounce5 application +*/ +#include "system.h" +#include "pacer.h" +#include "tinygl.h" +#include "boing.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 200 + + +/* Determine if two balls have collided + @param balls array of all the ball states + @param num_balls number of balls in array + @param this_ball index of ball to check + @return index of ball collided with or -1 for no collision */ +static int collision_detect (boing_state_t *balls, int num_balls, int this_ball) +{ + int i; + + for (i = 0; i < num_balls; i++) + { + /* Cannot collide with self. */ + if (i == this_ball) + continue; + + if (balls[i].pos.x == balls[this_ball].pos.x + && balls[i].pos.y == balls[this_ball].pos.y) + return i; + } + return -1; +} + + + +int main (void) +{ + int tick; + boing_state_t balls[3]; + + system_init (); + tinygl_init (LOOP_RATE); + + pacer_init (LOOP_RATE); + tick = 0; + + balls[0] = boing_init (0, 1, DIR_NE); + balls[1] = boing_init (4, 5, DIR_SE); + balls[2] = boing_init (4, 5, DIR_SW); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tick++; + + /* Flash the first two balls at different rates. */ + tinygl_draw_point (balls[0].pos, tick % 2 < 1); + tinygl_draw_point (balls[1].pos, tick % 4 < 2); + + if (tick >= 40) + { + int i; + + tick = 0; + + for (i = 0; i < 3; i++) + { + /* Erase previous position. */ + tinygl_draw_point (balls[i].pos, 0); + + /* Check for collision; if so reverse direction. */ + balls[i] = boing_update (balls[i]); + + /* Perhaps should make ball that is hit reverse as well? */ + if (collision_detect (balls, 2, i) > 0) + { + balls[i] = boing_reverse (balls[i]); + } + + /* Draw previous position. */ + tinygl_draw_point (balls[i].pos, 1); + } + } + + tinygl_update (); + } +} diff --git a/apps/chooser/Makefile b/apps/chooser/Makefile new file mode 100644 index 0000000..ba8c08b --- /dev/null +++ b/apps/chooser/Makefile @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for chooser + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: chooser.out + + +# Compile: create object files from C source files. +chooser.o: chooser.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +chooser.out: chooser.o display.o ledmat.o navswitch.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: chooser.out + $(OBJCOPY) -O ihex chooser.out chooser.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash chooser.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/chooser/Makefile.test b/apps/chooser/Makefile.test new file mode 100644 index 0000000..4aa59cd --- /dev/null +++ b/apps/chooser/Makefile.test @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for chooser + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: chooser + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +chooser-test.o: chooser.c ../../drivers/navswitch.h ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +chooser: timer-test.o display-test.o pio-test.o pacer-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o navswitch-test.o chooser-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) chooser timer-test.o display-test.o pio-test.o pacer-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o navswitch-test.o chooser-test.o + + diff --git a/apps/chooser/chooser.c b/apps/chooser/chooser.c new file mode 100644 index 0000000..df49185 --- /dev/null +++ b/apps/chooser/chooser.c @@ -0,0 +1,81 @@ +/** @file chooser.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Homework winner chooser + + @defgroup chooser Homework winner chooser. +*/ + +#include "system.h" +#include "tinygl.h" +#include "pacer.h" +#include "navswitch.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + + +static void show_num (char ch, uint8_t count) +{ + char buffer[4]; + + /* FIXME! */ + buffer[0] = ch; + buffer[1] = count / 10 + '0'; + buffer[2] = count % 10 + '0'; + buffer[3] = 0; + tinygl_text (buffer); +} + + +int main (void) +{ + int count = 0; + + system_init (); + tinygl_init (LOOP_RATE); + navswitch_init (); + + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + tinygl_text_mode_set (TINYGL_TEXT_MODE_SCROLL_LEFT); + + tinygl_text ("NUM?"); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + tinygl_update (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_PUSH)) + { + show_num ('T', count); + } + else if (navswitch_push_event_p (NAVSWITCH_WEST)) + { + if (count > 0) + count--; + show_num ('N', count); + } + else if (navswitch_push_event_p (NAVSWITCH_EAST)) + { + count++; + show_num ('N', count); + } + + + } + + return 0; +} diff --git a/apps/demo1/Makefile b/apps/demo1/Makefile new file mode 100644 index 0000000..c02d99e --- /dev/null +++ b/apps/demo1/Makefile @@ -0,0 +1,48 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for demo1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: demo1.out + + +# Compile: create object files from C source files. +demo1.o: demo1.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +demo1.out: demo1.o pio.o system.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: demo1.out + $(OBJCOPY) -O ihex demo1.out demo1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash demo1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/demo1/Makefile.test b/apps/demo1/Makefile.test new file mode 100644 index 0000000..f4f73ba --- /dev/null +++ b/apps/demo1/Makefile.test @@ -0,0 +1,42 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for demo1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: demo1 + + +# Compile: create object files from C source files. +demo1-test.o: demo1.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/pio.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +demo1: demo1-test.o mgetkey-test.o pio-test.o system-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) demo1 demo1-test.o mgetkey-test.o pio-test.o system-test.o + + diff --git a/apps/demo1/demo1.c b/apps/demo1/demo1.c new file mode 100644 index 0000000..77234ca --- /dev/null +++ b/apps/demo1/demo1.c @@ -0,0 +1,21 @@ +/** @file demo1.c + @author M.P. Hayes + @date 25 Aug 2011 +*/ + +#include "system.h" +#include "pio.h" + +int main (void) +{ + system_init (); + + pio_config_set (LED1_PIO, PIO_OUTPUT_HIGH); + + while (1) + { + pio_output_toggle (LED1_PIO); + } + + return 0; +} diff --git a/apps/demo2/Makefile b/apps/demo2/Makefile new file mode 100644 index 0000000..28e6c80 --- /dev/null +++ b/apps/demo2/Makefile @@ -0,0 +1,51 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for demo2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: demo2.out + + +# Compile: create object files from C source files. +demo2.o: demo2.c ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +demo2.out: demo2.o led.o pio.o system.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: demo2.out + $(OBJCOPY) -O ihex demo2.out demo2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash demo2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/demo2/Makefile.test b/apps/demo2/Makefile.test new file mode 100644 index 0000000..f7e4a97 --- /dev/null +++ b/apps/demo2/Makefile.test @@ -0,0 +1,45 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for demo2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: demo2 + + +# Compile: create object files from C source files. +demo2-test.o: demo2.c ../../drivers/led.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/pio.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +demo2: demo2-test.o led-test.o mgetkey-test.o pio-test.o system-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) demo2 demo2-test.o led-test.o mgetkey-test.o pio-test.o system-test.o + + diff --git a/apps/demo2/demo2.c b/apps/demo2/demo2.c new file mode 100644 index 0000000..7ef209f --- /dev/null +++ b/apps/demo2/demo2.c @@ -0,0 +1,24 @@ +/** @file demo2.c + @author M.P. Hayes + @date 25 Aug 2011 +*/ + +#include "system.h" +#include "led.h" + +int main (void) +{ + bool state = 0; + + system_init (); + + led_init (); + + while (1) + { + led_set (LED1, state); + state = !state; + } + + return 0; +} diff --git a/apps/fonttest1/Makefile b/apps/fonttest1/Makefile new file mode 100644 index 0000000..27ceac4 --- /dev/null +++ b/apps/fonttest1/Makefile @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for fonttest1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: fonttest1.out + + +# Compile: create object files from C source files. +fonttest1.o: fonttest1.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/navswitch.h ../../fonts/font3x5_1.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +fonttest1.out: display.o fonttest1.o ledmat.o navswitch.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: fonttest1.out + $(OBJCOPY) -O ihex fonttest1.out fonttest1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash fonttest1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/fonttest1/Makefile.test b/apps/fonttest1/Makefile.test new file mode 100644 index 0000000..cf1169d --- /dev/null +++ b/apps/fonttest1/Makefile.test @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for fonttest1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: fonttest1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +fonttest1-test.o: fonttest1.c ../../drivers/navswitch.h ../../utils/font.h ../../drivers/test/system.h ../../fonts/font3x5_1.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +fonttest1: timer-test.o display-test.o pio-test.o pacer-test.o tinygl-test.o ledmat-test.o system-test.o fonttest1-test.o navswitch-test.o mgetkey-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) fonttest1 timer-test.o display-test.o pio-test.o pacer-test.o tinygl-test.o ledmat-test.o system-test.o fonttest1-test.o navswitch-test.o mgetkey-test.o + + diff --git a/apps/fonttest1/fonttest1.c b/apps/fonttest1/fonttest1.c new file mode 100644 index 0000000..33db503 --- /dev/null +++ b/apps/fonttest1/fonttest1.c @@ -0,0 +1,105 @@ +/** @file fonttest1.c + @author M.P. Hayes + @date 3 Sep 2010 + @brief Simple font test program + + @defgroup fonttest1 Simple font test program. +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "../fonts/font3x5_1.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +static font_t *fonts[] = +{ + &font5x7_1, + &font3x5_1 +}; +static tinygl_text_mode_t modes[] = +{ + TINYGL_TEXT_MODE_STEP, + TINYGL_TEXT_MODE_STEP +}; + + +static void choose_font (int font_num) +{ + tinygl_font_set (fonts[font_num]); + tinygl_text_mode_set (modes[font_num]); + tinygl_clear (); +} + + +static char show_char (int font_num, char ch) +{ + char string[2]; + + if (ch < FONT_FIRST (fonts[font_num])) + ch = FONT_LAST (fonts[font_num]); + else if (ch > FONT_LAST (fonts[font_num])) + ch = FONT_FIRST (fonts[font_num]); + + string[0] = ch; + string[1] = 0; + tinygl_text (string); + return ch; +} + + +int main (void) +{ + char c = 'A'; + unsigned int font_num = 0; + + system_init (); + tinygl_init (LOOP_RATE); + + navswitch_init (); + + pacer_init (LOOP_RATE); + + choose_font (font_num); + tinygl_text_speed_set (10); + + c = show_char (font_num, c); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_PUSH)) + { + font_num++; + if (font_num >= ARRAY_SIZE (fonts)) + font_num = 0; + + choose_font (font_num); + c = show_char (font_num, c); + } + else if (navswitch_push_event_p (NAVSWITCH_WEST) + || navswitch_push_event_p (NAVSWITCH_SOUTH)) + { + c = show_char (font_num, c - 1); + } + else if (navswitch_push_event_p (NAVSWITCH_EAST) + || navswitch_push_event_p (NAVSWITCH_NORTH)) + { + c = show_char (font_num, c + 1); + } + + tinygl_update (); + } + + return 0; +} diff --git a/apps/game/README b/apps/game/README new file mode 100644 index 0000000..4ebac1e --- /dev/null +++ b/apps/game/README @@ -0,0 +1,8 @@ +Use this directory for your final game and its module(s). Only the +files in this directory will be marked. + +Ensure you use the svn add command to add your files to the subversion +repository. For example, + +svn add game.c +svn add Makefile diff --git a/apps/hello1/Makefile b/apps/hello1/Makefile new file mode 100644 index 0000000..f0c7432 --- /dev/null +++ b/apps/hello1/Makefile @@ -0,0 +1,57 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for hello1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: hello1.out + + +# Compile: create object files from C source files. +hello1.o: hello1.c ../../drivers/avr/system.h ../../drivers/ledmat.h ../../utils/pacer.h simplefont.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +hello1.out: hello1.o ledmat.o pacer.o pio.o system.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: hello1.out + $(OBJCOPY) -O ihex hello1.out hello1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash hello1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/hello1/Makefile.test b/apps/hello1/Makefile.test new file mode 100644 index 0000000..0f261c5 --- /dev/null +++ b/apps/hello1/Makefile.test @@ -0,0 +1,51 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for hello1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: hello1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +hello1-test.o: hello1.c ../../utils/pacer.h ../../drivers/ledmat.h simplefont.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +hello1: timer-test.o pio-test.o pacer-test.o ledmat-test.o hello1-test.o system-test.o mgetkey-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) hello1 timer-test.o pio-test.o pacer-test.o ledmat-test.o hello1-test.o system-test.o mgetkey-test.o + + diff --git a/apps/hello1/hello1.c b/apps/hello1/hello1.c new file mode 100644 index 0000000..a829353 --- /dev/null +++ b/apps/hello1/hello1.c @@ -0,0 +1,60 @@ +/** @file hello1.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Simple message display program + + @defgroup hello1 Simple message display program +*/ + +#include "system.h" +#include "simplefont.h" +#include "pacer.h" +#include "ledmat.h" + + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate in Hz. */ +#define MESSAGE_RATE 2 + + +int main (void) +{ + char message[] = "HELLO WORLD"; + uint8_t col = 0; + uint8_t index = 0; + uint8_t tick = 0; + + system_init (); + ledmat_init (); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + ledmat_display_column (font[(message[index] - ' ') * 5 + col], + col); + + col++; + if (col > 4) + col = 0; + + /* Advance message. */ + tick++; + if (tick >= LOOP_RATE / MESSAGE_RATE) + { + tick = 0; + index++; + if (!message[index]) + index = 0; + } + } + + return 0; +} diff --git a/apps/hello1/simplefont.h b/apps/hello1/simplefont.h new file mode 100644 index 0000000..386eb0d --- /dev/null +++ b/apps/hello1/simplefont.h @@ -0,0 +1,79 @@ +#ifndef SIMPLEFONT_H_ +#define SIMPLEFONT_H_ + +#include + +static uint8_t font[] = { + /* */ 0x00, 0x00, 0x00, 0x00, 0x00, + /* ! */ 0x00, 0x00, 0x5F, 0x00, 0x00, + /* " */ 0x00, 0x03, 0x00, 0x03, 0x00, + /* # */ 0x12, 0x7F, 0x12, 0x7F, 0x12, + /* $ */ 0x26, 0x49, 0x7F, 0x49, 0x32, + /* % */ 0x43, 0x33, 0x08, 0x66, 0x61, + /* & */ 0x36, 0x49, 0x55, 0x23, 0x50, + /* ' */ 0x00, 0x00, 0x03, 0x00, 0x00, + /* ( */ 0x00, 0x1C, 0x63, 0x41, 0x00, + /* ) */ 0x00, 0x41, 0x63, 0x1C, 0x00, + /* * */ 0x49, 0x2A, 0x7F, 0x2A, 0x49, + /* + */ 0x08, 0x08, 0x7F, 0x08, 0x08, + /* , */ 0x00, 0x40, 0x70, 0x00, 0x00, + /* - */ 0x08, 0x08, 0x08, 0x08, 0x08, + /* . */ 0x00, 0x00, 0x40, 0x00, 0x00, + /* / */ 0x40, 0x30, 0x08, 0x06, 0x01, + /* 0 */ 0x3E, 0x51, 0x49, 0x45, 0x3E, + /* 1 */ 0x00, 0x42, 0x7F, 0x40, 0x00, + /* 2 */ 0x42, 0x61, 0x51, 0x49, 0x46, + /* 3 */ 0x21, 0x41, 0x45, 0x4B, 0x31, + /* 4 */ 0x18, 0x14, 0x12, 0x7F, 0x10, + /* 5 */ 0x27, 0x45, 0x45, 0x45, 0x39, + /* 6 */ 0x3C, 0x4A, 0x49, 0x49, 0x30, + /* 7 */ 0x01, 0x01, 0x79, 0x05, 0x03, + /* 8 */ 0x37, 0x49, 0x49, 0x49, 0x36, + /* 9 */ 0x06, 0x49, 0x49, 0x29, 0x1E, + /* : */ 0x00, 0x00, 0x14, 0x00, 0x00, + /* ; */ 0x00, 0x40, 0x74, 0x00, 0x00, + /* < */ 0x00, 0x08, 0x14, 0x22, 0x41, + /* = */ 0x14, 0x14, 0x14, 0x14, 0x14, + /* > */ 0x41, 0x22, 0x14, 0x08, 0x00, + /* ? */ 0x00, 0x06, 0x59, 0x05, 0x02, + /* @ */ 0x3E, 0x41, 0x4D, 0x51, 0x4E, + /* A */ 0x7E, 0x11, 0x11, 0x11, 0x7E, + /* B */ 0x41, 0x7F, 0x49, 0x49, 0x36, + /* C */ 0x3E, 0x41, 0x41, 0x41, 0x22, + /* D */ 0x41, 0x7F, 0x41, 0x41, 0x3E, + /* E */ 0x7F, 0x49, 0x49, 0x49, 0x49, + /* F */ 0x7F, 0x09, 0x09, 0x09, 0x01, + /* G */ 0x3E, 0x41, 0x41, 0x49, 0x7A, + /* H */ 0x7F, 0x08, 0x08, 0x08, 0x7F, + /* I */ 0x00, 0x41, 0x7F, 0x41, 0x00, + /* J */ 0x20, 0x40, 0x41, 0x3F, 0x01, + /* K */ 0x7F, 0x08, 0x14, 0x22, 0x41, + /* L */ 0x7F, 0x40, 0x40, 0x40, 0x40, + /* M */ 0x7F, 0x02, 0x0C, 0x02, 0x7F, + /* N */ 0x7F, 0x06, 0x08, 0x30, 0x7F, + /* O */ 0x3E, 0x41, 0x41, 0x41, 0x3E, + /* P */ 0x7F, 0x09, 0x09, 0x09, 0x06, + /* Q */ 0x7E, 0x41, 0x51, 0x21, 0x5E, + /* R */ 0x7F, 0x09, 0x19, 0x29, 0x46, + /* S */ 0x26, 0x49, 0x49, 0x49, 0x32, + /* T */ 0x01, 0x01, 0x7F, 0x01, 0x01, + /* U */ 0x3F, 0x40, 0x40, 0x40, 0x3F, + /* V */ 0x1F, 0x20, 0x40, 0x20, 0x1F, + /* W */ 0x7F, 0x20, 0x18, 0x20, 0x7F, + /* X */ 0x63, 0x14, 0x08, 0x14, 0x63, + /* Y */ 0x07, 0x08, 0x70, 0x08, 0x07, + /* Z */ 0x61, 0x51, 0x49, 0x45, 0x43, + /* [ */ 0x00, 0x7F, 0x41, 0x41, 0x00, + /* \ */ 0x01, 0x06, 0x08, 0x30, 0x40, + /* ] */ 0x00, 0x41, 0x41, 0x7F, 0x00, + /* ^ */ 0x00, 0x06, 0x01, 0x06, 0x00, + /* _ */ 0x40, 0x40, 0x40, 0x40, 0x40, + /* ` */ 0x00, 0x01, 0x02, 0x00, 0x00 +}; + +#define FONT_WIDTH 5 +#define FONT_ASCII_OFFSET ' ' +#define FONT_TABLE_SIZE (sizeof(font)/FONT_WIDTH) + +#endif + diff --git a/apps/hello2/Makefile b/apps/hello2/Makefile new file mode 100644 index 0000000..ca0ca8a --- /dev/null +++ b/apps/hello2/Makefile @@ -0,0 +1,63 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for hello2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: hello2.out + + +# Compile: create object files from C source files. +hello2.o: hello2.c ../../drivers/avr/system.h ../../drivers/display.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +hello2.out: display.o hello2.o ledmat.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: hello2.out + $(OBJCOPY) -O ihex hello2.out hello2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash hello2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/hello2/Makefile.test b/apps/hello2/Makefile.test new file mode 100644 index 0000000..3e16a23 --- /dev/null +++ b/apps/hello2/Makefile.test @@ -0,0 +1,57 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for hello2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: hello2 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +hello2-test.o: hello2.c ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +hello2: timer-test.o display-test.o pio-test.o hello2-test.o pacer-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) hello2 timer-test.o display-test.o pio-test.o hello2-test.o pacer-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o + + diff --git a/apps/hello2/hello2.c b/apps/hello2/hello2.c new file mode 100644 index 0000000..c60b41e --- /dev/null +++ b/apps/hello2/hello2.c @@ -0,0 +1,44 @@ +/** @file hello2.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Simple message display program using tinygl + + @defgroup hello2 Simple message display program using tinygl +*/ + +#include "system.h" +#include "tinygl.h" +#include "pacer.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10s). */ +#define MESSAGE_RATE 10 + + +int main (void) +{ + system_init (); + tinygl_init (LOOP_RATE); + + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + + tinygl_text ("HELLO WORLD"); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + } + + return 0; +} diff --git a/apps/hello3/Makefile b/apps/hello3/Makefile new file mode 100644 index 0000000..9841ff1 --- /dev/null +++ b/apps/hello3/Makefile @@ -0,0 +1,63 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for hello3 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: hello3.out + + +# Compile: create object files from C source files. +hello3.o: hello3.c ../../drivers/avr/system.h ../../drivers/display.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +hello3.out: display.o hello3.o ledmat.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: hello3.out + $(OBJCOPY) -O ihex hello3.out hello3.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash hello3.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/hello3/Makefile.test b/apps/hello3/Makefile.test new file mode 100644 index 0000000..1d009a1 --- /dev/null +++ b/apps/hello3/Makefile.test @@ -0,0 +1,57 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for hello3 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: hello3 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +hello3-test.o: hello3.c ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +hello3: timer-test.o display-test.o pio-test.o hello3-test.o pacer-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) hello3 timer-test.o display-test.o pio-test.o hello3-test.o pacer-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o + + diff --git a/apps/hello3/hello3.c b/apps/hello3/hello3.c new file mode 100644 index 0000000..aca1cca --- /dev/null +++ b/apps/hello3/hello3.c @@ -0,0 +1,45 @@ +/** @file hello3.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Simple message display program using tinygl + + @defgroup hello3 Simple message display program using tinygl +*/ + +#include "system.h" +#include "tinygl.h" +#include "pacer.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + + +int main (void) +{ + system_init (); + tinygl_init (LOOP_RATE); + + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + tinygl_text_mode_set (TINYGL_TEXT_MODE_SCROLL_LEFT); + + tinygl_text ("HELLO WORLD"); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + } + + return 0; +} diff --git a/apps/hello4/Makefile b/apps/hello4/Makefile new file mode 100644 index 0000000..1ff1ea7 --- /dev/null +++ b/apps/hello4/Makefile @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for hello4 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: hello4.out + + +# Compile: create object files from C source files. +hello4.o: hello4.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +hello4.out: display.o hello4.o ledmat.o navswitch.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: hello4.out + $(OBJCOPY) -O ihex hello4.out hello4.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash hello4.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/hello4/Makefile.test b/apps/hello4/Makefile.test new file mode 100644 index 0000000..fd35be3 --- /dev/null +++ b/apps/hello4/Makefile.test @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for hello4 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: hello4 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +hello4-test.o: hello4.c ../../drivers/navswitch.h ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +hello4: timer-test.o display-test.o pio-test.o hello4-test.o pacer-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) hello4 timer-test.o display-test.o pio-test.o hello4-test.o pacer-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/hello4/hello4.c b/apps/hello4/hello4.c new file mode 100644 index 0000000..e981028 --- /dev/null +++ b/apps/hello4/hello4.c @@ -0,0 +1,56 @@ +/** @file hello4.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Simple message display program using display module. + + @defgroup hello4 Simple message display program using tinygl +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + + +int main (void) +{ + system_init (); + tinygl_init (LOOP_RATE); + + navswitch_init (); + + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + + tinygl_text ("HELLO WORLD"); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + tinygl_text_mode_set (TINYGL_TEXT_MODE_STEP); + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + tinygl_text_mode_set (TINYGL_TEXT_MODE_SCROLL_LEFT); + + } + + return 0; +} diff --git a/apps/ir_grab1/Makefile b/apps/ir_grab1/Makefile new file mode 100644 index 0000000..cbf1cc3 --- /dev/null +++ b/apps/ir_grab1/Makefile @@ -0,0 +1,69 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for ir_grab1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: ir_grab1.out + + +# Compile: create object files from C source files. +ir_grab1.o: ir_grab1.c ../../drivers/avr/delay.h ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ir.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir.o: ../../drivers/ir.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ir.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +ir_grab1.out: display.o ir.o ir_grab1.o ledmat.o navswitch.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: ir_grab1.out + $(OBJCOPY) -O ihex ir_grab1.out ir_grab1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash ir_grab1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/ir_grab1/Makefile.test b/apps/ir_grab1/Makefile.test new file mode 100644 index 0000000..84b8c09 --- /dev/null +++ b/apps/ir_grab1/Makefile.test @@ -0,0 +1,63 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for ir_grab1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: ir_grab1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir-test.o: ../../drivers/ir.c ../../drivers/test/pio.h ../../drivers/ir.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_grab1-test.o: ir_grab1.c ../../drivers/navswitch.h ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/ir.h ../../drivers/test/delay.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +ir_grab1: timer-test.o display-test.o pio-test.o pacer-test.o tinygl-test.o ir-test.o ledmat-test.o system-test.o ir_grab1-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) ir_grab1 timer-test.o display-test.o pio-test.o pacer-test.o tinygl-test.o ir-test.o ledmat-test.o system-test.o ir_grab1-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/ir_grab1/ir_grab1.c b/apps/ir_grab1/ir_grab1.c new file mode 100644 index 0000000..3a56cf7 --- /dev/null +++ b/apps/ir_grab1/ir_grab1.c @@ -0,0 +1,135 @@ +/** @file ir_grab1.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Test program for IR serial communnications. + + @defgroup ir_grab1 Test program for IR serial communications. +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "ir.h" +#include "delay.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + +/* At 2400 baud and 36 kHz modulation, there are 15 cycles per bit. */ +#define CODE_LEN_MAX 2000 +#define CODESEQ_LEN_MAX 200 + +#define IR_MODULATION_PERIOD_US (1e6 / IR_MODULATION_FREQ) +#define TWEAK_US 0.5 + + +typedef struct code +{ + uint16_t on; + uint16_t off; +} code_t; + + +static void transmit (code_t *codeseq, uint8_t size) +{ + uint8_t i; + + for (i = 0; i < size; i++) + { + ir_tx_set (1, codeseq[i].on); + ir_tx_set (0, codeseq[i].off); + } +} + + +static uint8_t capture (code_t *codeseq, uint8_t size) +{ + uint8_t i; + + for (i = 0; i < size; i++) + { + uint16_t count; + + for (count = 0; count < CODE_LEN_MAX && ir_rx_get (); count++) + { + DELAY_US (IR_MODULATION_PERIOD_US - TWEAK_US); + } + codeseq[i].on = count; + + /* Something funny here. */ + if (count >= CODE_LEN_MAX) + return 0; + + for (count = 0; count < CODE_LEN_MAX && !ir_rx_get (); count++) + { + DELAY_US (IR_MODULATION_PERIOD_US - TWEAK_US); + } + codeseq[i].off = count; + + if (count >= CODE_LEN_MAX) + return i + 1; + } + return i; +} + + +static void show_char (char ch) +{ + char buffer[2]; + + buffer[0] = ch; + buffer[1] = 0; + tinygl_text (buffer); +} + + +int main (void) +{ + uint8_t size = 0; + code_t codeseq[CODESEQ_LEN_MAX]; + + system_init (); + tinygl_init (LOOP_RATE); + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + + navswitch_init (); + ir_init (); + + pacer_init (LOOP_RATE); + + show_char ('W'); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_PUSH)) + { + transmit (codeseq, size); + show_char ('T'); + } + + if (ir_rx_get ()) + { + size = capture (codeseq, CODESEQ_LEN_MAX); + show_char (size == 0 ? 'E' : 'R'); +// size = capture (codeseq, 9); +// show_char ('0' + size); + } + } + + return 0; +} diff --git a/apps/ir_grab2/Makefile b/apps/ir_grab2/Makefile new file mode 100644 index 0000000..d71d6f2 --- /dev/null +++ b/apps/ir_grab2/Makefile @@ -0,0 +1,72 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for ir_grab2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: ir_grab2.out + + +# Compile: create object files from C source files. +ir_grab2.o: ir_grab2.c ../../drivers/avr/delay.h ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ir.h ../../drivers/navswitch.h ../../fonts/font3x5_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h ../../utils/uint8toa.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir.o: ../../drivers/ir.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ir.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +uint8toa.o: ../../utils/uint8toa.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +ir_grab2.out: display.o ir.o ir_grab2.o ledmat.o navswitch.o pacer.o pio.o system.o timer.o tinygl.o uint8toa.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: ir_grab2.out + $(OBJCOPY) -O ihex ir_grab2.out ir_grab2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash ir_grab2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/ir_grab2/Makefile.test b/apps/ir_grab2/Makefile.test new file mode 100644 index 0000000..b9c9d36 --- /dev/null +++ b/apps/ir_grab2/Makefile.test @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for ir_grab2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: ir_grab2 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_grab2-test.o: ir_grab2.c ../../drivers/navswitch.h ../../utils/font.h ../../drivers/test/system.h ../../fonts/font3x5_1.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/ir.h ../../utils/uint8toa.h ../../drivers/test/delay.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir-test.o: ../../drivers/ir.c ../../drivers/test/pio.h ../../drivers/ir.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +uint8toa-test.o: ../../utils/uint8toa.c ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +ir_grab2: timer-test.o ir_grab2-test.o display-test.o pio-test.o pacer-test.o tinygl-test.o ir-test.o uint8toa-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) ir_grab2 timer-test.o ir_grab2-test.o display-test.o pio-test.o pacer-test.o tinygl-test.o ir-test.o uint8toa-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/ir_grab2/ir_grab2.c b/apps/ir_grab2/ir_grab2.c new file mode 100644 index 0000000..53f1611 --- /dev/null +++ b/apps/ir_grab2/ir_grab2.c @@ -0,0 +1,153 @@ +/** @file ir_grab2.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Test program for IR serial communnications. + + @defgroup ir_grab2 Test program for IR serial communications. +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "ir.h" +#include "delay.h" +#include "uint8toa.h" +#include "../fonts/font3x5_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 2000 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + +/* At 2400 baud and 36 kHz modulation, there are 15 cycles per bit. + With a Sony start code of length 4.5 x 0.6 ms there are 86 cycles. */ +#define COUNT_MAX 250 +#define CODESEQ_LEN_MAX 40 + +#define IR_MODULATION_PERIOD_US (1e6 / IR_MODULATION_FREQ) +#define TWEAK_US 0.5 + + +typedef uint8_t count_t; + +typedef struct code +{ + count_t on; + count_t off; +} code_t; + + +static void transmit (code_t *codeseq, uint8_t size) +{ + uint8_t i; + + for (i = 0; i < size; i++) + { + ir_tx_set (1, codeseq[i].on); + ir_tx_set (0, codeseq[i].off); + } +} + + +static uint8_t capture (code_t *codeseq, uint8_t size) +{ + uint8_t i; + + for (i = 0; i < size; i++) + { + count_t count; + + for (count = 0; count < COUNT_MAX && ir_rx_get (); count++) + { + DELAY_US (IR_MODULATION_PERIOD_US - TWEAK_US); + } + codeseq[i].on = count; + + /* Something funny here. */ + if (count >= COUNT_MAX) + return 0; + + for (count = 0; count < COUNT_MAX && !ir_rx_get (); count++) + { + DELAY_US (IR_MODULATION_PERIOD_US - TWEAK_US); + } + codeseq[i].off = count; + + if (count >= COUNT_MAX) + return i + 1; + } + return i; +} + + +static void show_char (char ch) +{ + char buffer[2]; + + buffer[0] = ch; + buffer[1] = 0; + tinygl_text (buffer); +} + + +static void show_num (char ch, uint8_t num) +{ + char buffer[5]; + + buffer[0] = ch; + uint8toa (num, buffer + 1, 0); + tinygl_text (buffer); +} + + +int main (void) +{ + uint8_t size = 0; + code_t codeseq[CODESEQ_LEN_MAX]; + uint8_t count = 0; + + system_init (); + tinygl_init (LOOP_RATE); + tinygl_font_set (&font3x5_1); + tinygl_text_mode_set (TINYGL_TEXT_MODE_SCROLL_LEFT); + tinygl_text_mode_set (TINYGL_TEXT_MODE_ROTATE_SCROLL_DOWN); + tinygl_text_speed_set (MESSAGE_RATE); + + navswitch_init (); + ir_init (); + + pacer_init (LOOP_RATE); + + show_num ('W', count); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_PUSH)) + { + transmit (codeseq, size); + show_char ('T'); + } + + if (ir_rx_get ()) + { + size = capture (codeseq, CODESEQ_LEN_MAX); + show_num (size == 0 ? 'E' : 'R', size); + count++; +// size = capture (codeseq, 9); +// show_char ('0' + size); + } + } + + return 0; +} diff --git a/apps/ir_grab3/Makefile b/apps/ir_grab3/Makefile new file mode 100644 index 0000000..1284e07 --- /dev/null +++ b/apps/ir_grab3/Makefile @@ -0,0 +1,78 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for ir_grab3 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: ir_grab3.out + + +# Compile: create object files from C source files. +ir_grab3.o: ir_grab3.c ../../drivers/avr/delay.h ../../drivers/avr/eeprom.h ../../drivers/avr/system.h ../../drivers/button.h ../../drivers/display.h ../../drivers/ir.h ../../drivers/led.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +eeprom.o: ../../drivers/avr/eeprom.c ../../drivers/avr/eeprom.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +button.o: ../../drivers/button.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/button.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir.o: ../../drivers/ir.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ir.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +ir_grab3.out: button.o display.o eeprom.o ir.o ir_grab3.o led.o ledmat.o navswitch.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: ir_grab3.out + $(OBJCOPY) -O ihex ir_grab3.out ir_grab3.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash ir_grab3.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/ir_grab3/Makefile.test b/apps/ir_grab3/Makefile.test new file mode 100644 index 0000000..87f8073 --- /dev/null +++ b/apps/ir_grab3/Makefile.test @@ -0,0 +1,72 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for ir_grab3 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: ir_grab3 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +button-test.o: ../../drivers/button.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/button.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir-test.o: ../../drivers/ir.c ../../drivers/test/pio.h ../../drivers/ir.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +eeprom-test.o: ../../drivers/test/eeprom.c ../../drivers/test/eeprom.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_grab3-test.o: ir_grab3.c ../../drivers/navswitch.h ../../drivers/test/delay.h ../../drivers/test/system.h ../../drivers/test/eeprom.h ../../utils/font.h ../../drivers/led.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/ir.h ../../drivers/button.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +ir_grab3: timer-test.o pio-test.o led-test.o display-test.o button-test.o pacer-test.o tinygl-test.o ir-test.o ledmat-test.o eeprom-test.o system-test.o mgetkey-test.o ir_grab3-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) ir_grab3 timer-test.o pio-test.o led-test.o display-test.o button-test.o pacer-test.o tinygl-test.o ir-test.o ledmat-test.o eeprom-test.o system-test.o mgetkey-test.o ir_grab3-test.o navswitch-test.o + + diff --git a/apps/ir_grab3/ir_grab3.c b/apps/ir_grab3/ir_grab3.c new file mode 100644 index 0000000..54f6d1f --- /dev/null +++ b/apps/ir_grab3/ir_grab3.c @@ -0,0 +1,245 @@ +/** @file ir_grab3.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Test program for IR serial communnications. + + @defgroup ir_grab3 Test program for IR serial communications. +*/ + +#include "system.h" +#include "navswitch.h" +#include "button.h" +#include "tinygl.h" +#include "pacer.h" +#include "ir.h" +#include "led.h" +#include "delay.h" +#include "eeprom.h" +#include "../fonts/font5x7_1.h" +#include + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 2000 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + +/* At 2400 baud and 36 kHz modulation, there are 15 cycles per bit. + With a Sony start code of length 4.5 x 0.6 ms there are 86 cycles. */ +#define COUNT_MAX 250 + +#define CODESEQ_LEN_MAX 25 + +#define CODESEQ_NUM 5 + +#define IR_MODULATION_PERIOD_US (1e6 / IR_MODULATION_FREQ) + +#define LOOP_TWEAK_US 1.5 + + +typedef enum {STATE_NORMAL, STATE_LEARN, STATE_STORE} state_t; + + +typedef uint8_t count_t; + +typedef struct code +{ + count_t on; + count_t off; +} code_t; + + +static void transmit (code_t *codeseq) +{ + uint8_t i; + + for (i = 0; codeseq[i].on != 0; i++) + { + ir_tx_set (1, codeseq[i].on); + ir_tx_set (0, codeseq[i].off); + } +} + + +static uint8_t capture (code_t *codeseq, uint8_t size) +{ + uint8_t i; + + for (i = 0; i < size - 1; i++) + { + count_t count; + + for (count = 0; count < COUNT_MAX && ir_rx_get (); count++) + { + DELAY_US (IR_MODULATION_PERIOD_US - LOOP_TWEAK_US); + } + codeseq[i].on = count; + + for (count = 0; count < COUNT_MAX && !ir_rx_get (); count++) + { + DELAY_US (IR_MODULATION_PERIOD_US - LOOP_TWEAK_US - 1.0); + } + codeseq[i].off = count; + + if (count >= COUNT_MAX) + { + /* Mark end of sequence. */ + codeseq[i + 1].on = 0; + return i + 1; + } + } + /* Sequence is too long. */ + return 0; +} + + +static void codeseqs_write (code_t *codeseqs, uint8_t len, uint8_t num) +{ + eeprom_write (0, codeseqs, sizeof (codeseqs[0]) * len * num); +} + + +static void codeseqs_read (code_t *codeseqs, uint8_t len, uint8_t num) +{ + /* When the EEPROM is erased all the bytes are 0xFF so set to + sensible defaults. */ + eeprom_read (0, codeseqs, sizeof (codeseqs[0]) * len * num); + if (codeseqs[0].on == 0xff) + { + uint8_t i; + + for (i = 0; i < num; i++) + codeseqs[i * len].on = 0; + + codeseqs_write (codeseqs, len, num); + } +} + + +static int switch_get (void) +{ + if (navswitch_push_event_p (NAVSWITCH_PUSH)) + return 0; + else if (navswitch_push_event_p (NAVSWITCH_NORTH)) + return 1; + else if (navswitch_push_event_p (NAVSWITCH_EAST)) + return 2; + else if (navswitch_push_event_p (NAVSWITCH_SOUTH)) + return 3; + else if (navswitch_push_event_p (NAVSWITCH_WEST)) + return 4; + + return -1; +} + + +int main (void) +{ + state_t state = STATE_NORMAL; + code_t codeseq[CODESEQ_LEN_MAX]; + code_t codeseqs[CODESEQ_LEN_MAX * CODESEQ_NUM]; + int seq; + static const char *strings[] = {"P", "N", "E", "S", "W"}; + + codeseqs_read (codeseqs, CODESEQ_LEN_MAX, CODESEQ_NUM); + + system_init (); + tinygl_init (LOOP_RATE); + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + + navswitch_init (); + button_init (); + led_init (); + ir_init (); + + pacer_init (LOOP_RATE); + + tinygl_text ("X"); + led_set (LED1, 0); + + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + switch (state) + { + case STATE_NORMAL: + + tinygl_update (); + navswitch_update (); + button_update (); + + seq = switch_get (); + + if (seq != -1) + { + tinygl_text (strings[seq]); + transmit (&codeseqs[seq * CODESEQ_LEN_MAX]); + } + + if (button_push_event_p (BUTTON1)) + { + led_set (LED1, 1); + state = STATE_LEARN; + } + break; + + case STATE_LEARN: + + while (1) + { + /* Loop as fast as possible so that capture start of IR transmission + as soon as possible. */ + button_update (); + + if (ir_rx_get ()) + { + if (capture (codeseq, CODESEQ_LEN_MAX)) + { + tinygl_text ("?"); + led_set (LED1, 1); + state = STATE_STORE; + break; + } + } + + if (button_push_event_p (BUTTON1)) + { + led_set (LED1, 0); + state = STATE_NORMAL; + break; + } + } + break; + + case STATE_STORE: + + tinygl_update (); + navswitch_update (); + button_update (); + + seq = switch_get (); + + if (seq != -1) + { + memcpy (&codeseqs[seq * CODESEQ_LEN_MAX], codeseq, sizeof (codeseq)); + codeseqs_write (codeseqs, CODESEQ_LEN_MAX, CODESEQ_NUM); + tinygl_text (strings[seq]); + led_set (LED1, 0); + state = STATE_NORMAL; + } + + if (button_push_event_p (BUTTON1)) + { + led_set (LED1, 0); + state = STATE_NORMAL; + } + break; + } + } + + return 0; +} diff --git a/apps/ir_serial_test1/Makefile b/apps/ir_serial_test1/Makefile new file mode 100644 index 0000000..6f85bf1 --- /dev/null +++ b/apps/ir_serial_test1/Makefile @@ -0,0 +1,72 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for ir_serial_test1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: ir_serial_test1.out + + +# Compile: create object files from C source files. +ir_serial_test1.o: ir_serial_test1.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ir.h ../../drivers/ir_serial.h ../../drivers/navswitch.h ../../fonts/font3x5_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir.o: ../../drivers/ir.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ir.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial.o: ../../drivers/ir_serial.c ../../drivers/avr/delay.h ../../drivers/avr/system.h ../../drivers/ir.h ../../drivers/ir_serial.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +ir_serial_test1.out: display.o ir.o ir_serial.o ir_serial_test1.o ledmat.o navswitch.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: ir_serial_test1.out + $(OBJCOPY) -O ihex ir_serial_test1.out ir_serial_test1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash ir_serial_test1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/ir_serial_test1/Makefile.test b/apps/ir_serial_test1/Makefile.test new file mode 100644 index 0000000..645a5dc --- /dev/null +++ b/apps/ir_serial_test1/Makefile.test @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for ir_serial_test1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: ir_serial_test1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial-test.o: ../../drivers/ir_serial.c ../../drivers/ir.h ../../drivers/ir_serial.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial_test1-test.o: ir_serial_test1.c ../../drivers/navswitch.h ../../utils/font.h ../../drivers/ir_serial.h ../../drivers/test/system.h ../../fonts/font3x5_1.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/ir.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir-test.o: ../../drivers/ir.c ../../drivers/test/pio.h ../../drivers/ir.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +ir_serial_test1: timer-test.o display-test.o pio-test.o ir_serial-test.o pacer-test.o ir_serial_test1-test.o tinygl-test.o ir-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) ir_serial_test1 timer-test.o display-test.o pio-test.o ir_serial-test.o pacer-test.o ir_serial_test1-test.o tinygl-test.o ir-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/ir_serial_test1/ir_serial_test1.c b/apps/ir_serial_test1/ir_serial_test1.c new file mode 100644 index 0000000..aa27c62 --- /dev/null +++ b/apps/ir_serial_test1/ir_serial_test1.c @@ -0,0 +1,101 @@ +/** @file ir_serial_test1.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Test program for IR serial communnications. + + @defgroup ir_serial_test1 Test program for IR serial communications. +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "ir_serial.h" +#include "../fonts/font3x5_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + + +static void show_count (uint8_t count) +{ + char buffer[3]; + + /* FIXME! */ + buffer[0] = 'S'; + buffer[1] = count + '0'; + buffer[2] = 0; + tinygl_text (buffer); +} + + +static void show_err (uint8_t err) +{ + char buffer[3]; + + buffer[0] = 'E'; + buffer[1] = err + '0'; + buffer[2] = 0; + tinygl_text (buffer); +} + + +int main (void) +{ + int count = 5; + + system_init (); + tinygl_init (LOOP_RATE); + tinygl_font_set (&font3x5_1); + tinygl_text_speed_set (MESSAGE_RATE); + tinygl_text_mode_set (TINYGL_TEXT_MODE_ROTATE_SCROLL_DOWN); + + navswitch_init (); + ir_serial_init (); + + show_count (count); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + int ret; + uint8_t data; + + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + ir_serial_transmit (1); + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + ir_serial_transmit (2); + + ret = ir_serial_receive (&data); + if (ret == IR_SERIAL_OK) + { + if (data == 1) + count--; + else if (data == 2) + count++; + else + count = 0; + show_count (count); + } + else if (ret < 0) + { + show_err (-ret); + } + } + + return 0; +} diff --git a/apps/ir_serial_test2/Makefile b/apps/ir_serial_test2/Makefile new file mode 100644 index 0000000..5969d77 --- /dev/null +++ b/apps/ir_serial_test2/Makefile @@ -0,0 +1,72 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for ir_serial_test2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: ir_serial_test2.out + + +# Compile: create object files from C source files. +ir_serial_test2.o: ir_serial_test2.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ir.h ../../drivers/ir_serial.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir.o: ../../drivers/ir.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ir.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial.o: ../../drivers/ir_serial.c ../../drivers/avr/delay.h ../../drivers/avr/system.h ../../drivers/ir.h ../../drivers/ir_serial.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +ir_serial_test2.out: display.o ir.o ir_serial.o ir_serial_test2.o ledmat.o navswitch.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: ir_serial_test2.out + $(OBJCOPY) -O ihex ir_serial_test2.out ir_serial_test2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash ir_serial_test2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/ir_serial_test2/Makefile.test b/apps/ir_serial_test2/Makefile.test new file mode 100644 index 0000000..fce243e --- /dev/null +++ b/apps/ir_serial_test2/Makefile.test @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for ir_serial_test2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: ir_serial_test2 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial-test.o: ../../drivers/ir_serial.c ../../drivers/ir.h ../../drivers/ir_serial.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial_test2-test.o: ir_serial_test2.c ../../drivers/navswitch.h ../../drivers/ir_serial.h ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/ir.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir-test.o: ../../drivers/ir.c ../../drivers/test/pio.h ../../drivers/ir.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +ir_serial_test2: timer-test.o display-test.o pio-test.o ir_serial-test.o pacer-test.o ir_serial_test2-test.o tinygl-test.o ir-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) ir_serial_test2 timer-test.o display-test.o pio-test.o ir_serial-test.o pacer-test.o ir_serial_test2-test.o tinygl-test.o ir-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/ir_serial_test2/ir_serial_test2.c b/apps/ir_serial_test2/ir_serial_test2.c new file mode 100644 index 0000000..d73063d --- /dev/null +++ b/apps/ir_serial_test2/ir_serial_test2.c @@ -0,0 +1,83 @@ +/** @file ir_serial_test2.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Test program for IR serial communnications. + + @defgroup ir_serial_test2 Test program for IR serial communications. +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "ir_serial.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + + +static void show_count (uint8_t count) +{ + char buffer[2]; + + /* FIXME! */ + buffer[0] = count + '0'; + buffer[1] = 0; + tinygl_text (buffer); +} + + +int main (void) +{ + int count = 5; + + system_init (); + tinygl_init (LOOP_RATE); + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + tinygl_text_mode_set (TINYGL_TEXT_MODE_STEP); + + navswitch_init (); + ir_serial_init (); + + show_count (count); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + int ret; + uint8_t data; + + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + ir_serial_transmit (1); + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + ir_serial_transmit (2); + + ret = ir_serial_receive (&data); + if (ret == IR_SERIAL_OK) + { + if (data == 1) + count--; + else if (data == 2) + count++; + show_count (count); + } + } + + return 0; +} diff --git a/apps/ir_serial_test3/Makefile b/apps/ir_serial_test3/Makefile new file mode 100644 index 0000000..be21b31 --- /dev/null +++ b/apps/ir_serial_test3/Makefile @@ -0,0 +1,72 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for ir_serial_test3 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: ir_serial_test3.out + + +# Compile: create object files from C source files. +ir_serial_test3.o: ir_serial_test3.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ir.h ../../drivers/ir_serial.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir.o: ../../drivers/ir.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ir.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial.o: ../../drivers/ir_serial.c ../../drivers/avr/delay.h ../../drivers/avr/system.h ../../drivers/ir.h ../../drivers/ir_serial.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +ir_serial_test3.out: display.o ir.o ir_serial.o ir_serial_test3.o ledmat.o navswitch.o pacer.o pio.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: ir_serial_test3.out + $(OBJCOPY) -O ihex ir_serial_test3.out ir_serial_test3.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash ir_serial_test3.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/ir_serial_test3/Makefile.test b/apps/ir_serial_test3/Makefile.test new file mode 100644 index 0000000..e08f94c --- /dev/null +++ b/apps/ir_serial_test3/Makefile.test @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for ir_serial_test3 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: ir_serial_test3 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial-test.o: ../../drivers/ir_serial.c ../../drivers/ir.h ../../drivers/ir_serial.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir-test.o: ../../drivers/ir.c ../../drivers/test/pio.h ../../drivers/ir.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial_test3-test.o: ir_serial_test3.c ../../drivers/navswitch.h ../../drivers/ir_serial.h ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/ir.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +ir_serial_test3: timer-test.o display-test.o pio-test.o ir_serial-test.o pacer-test.o tinygl-test.o ir-test.o ledmat-test.o ir_serial_test3-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) ir_serial_test3 timer-test.o display-test.o pio-test.o ir_serial-test.o pacer-test.o tinygl-test.o ir-test.o ledmat-test.o ir_serial_test3-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/ir_serial_test3/ir_serial_test3.c b/apps/ir_serial_test3/ir_serial_test3.c new file mode 100644 index 0000000..4c06ca6 --- /dev/null +++ b/apps/ir_serial_test3/ir_serial_test3.c @@ -0,0 +1,77 @@ +/** @file ir_serial_test3.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Test program for IR serial communnications. + + @defgroup ir_serial_test3 Test program for IR serial communications. +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "ir_serial.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + + +static void show_count (uint8_t count) +{ + char buffer[2]; + + /* FIXME! */ + buffer[0] = count + '0'; + buffer[1] = 0; + tinygl_text (buffer); +} + + +int main (void) +{ + int count = 5; + + system_init (); + tinygl_init (LOOP_RATE); + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + tinygl_text_mode_set (TINYGL_TEXT_MODE_STEP); + + navswitch_init (); + ir_serial_init (); + + show_count (count); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + int ret; + uint8_t data; + + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + ir_serial_transmit (--count); + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + ir_serial_transmit (++count); + + ret = ir_serial_receive (&data); + if (ret == IR_SERIAL_OK) + show_count (data); + } + + return 0; +} diff --git a/apps/ir_uart_test1/Makefile b/apps/ir_uart_test1/Makefile new file mode 100644 index 0000000..d139042 --- /dev/null +++ b/apps/ir_uart_test1/Makefile @@ -0,0 +1,78 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for ir_uart_test1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: ir_uart_test1.out + + +# Compile: create object files from C source files. +ir_uart_test1.o: ir_uart_test1.c ../../drivers/avr/ir_uart.h ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_uart.o: ../../drivers/avr/ir_uart.c ../../drivers/avr/ir_uart.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/avr/timer0.h ../../drivers/avr/usart1.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +prescale.o: ../../drivers/avr/prescale.c ../../drivers/avr/prescale.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer0.o: ../../drivers/avr/timer0.c ../../drivers/avr/bits.h ../../drivers/avr/prescale.h ../../drivers/avr/system.h ../../drivers/avr/timer0.h + $(CC) -c $(CFLAGS) $< -o $@ + +usart1.o: ../../drivers/avr/usart1.c ../../drivers/avr/system.h ../../drivers/avr/usart1.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +ir_uart_test1.out: display.o ir_uart.o ir_uart_test1.o ledmat.o navswitch.o pacer.o pio.o prescale.o system.o timer.o timer0.o tinygl.o usart1.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: ir_uart_test1.out + $(OBJCOPY) -O ihex ir_uart_test1.out ir_uart_test1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash ir_uart_test1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/ir_uart_test1/Makefile.test b/apps/ir_uart_test1/Makefile.test new file mode 100644 index 0000000..31dbe0e --- /dev/null +++ b/apps/ir_uart_test1/Makefile.test @@ -0,0 +1,63 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for ir_uart_test1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: ir_uart_test1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_uart-test.o: ../../drivers/test/ir_uart.c ../../drivers/test/ir_uart.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_uart_test1-test.o: ir_uart_test1.c ../../drivers/navswitch.h ../../drivers/test/system.h ../../utils/font.h ../../drivers/test/ir_uart.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +ir_uart_test1: timer-test.o display-test.o pio-test.o pacer-test.o ir_uart-test.o tinygl-test.o ledmat-test.o system-test.o ir_uart_test1-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) ir_uart_test1 timer-test.o display-test.o pio-test.o pacer-test.o ir_uart-test.o tinygl-test.o ledmat-test.o system-test.o ir_uart_test1-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/ir_uart_test1/ir_uart_test1.c b/apps/ir_uart_test1/ir_uart_test1.c new file mode 100644 index 0000000..11b9091 --- /dev/null +++ b/apps/ir_uart_test1/ir_uart_test1.c @@ -0,0 +1,83 @@ +/** @file ir_uart_test1.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Test program for IR serial communnications. + + @defgroup ir_uart_test1 Test program for IR serial communications. +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "ir_uart.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + + +static void show_count (uint8_t count) +{ + char buffer[2]; + + /* FIXME! */ + buffer[0] = count + '0'; + buffer[1] = 0; + tinygl_text (buffer); +} + + +int main (void) +{ + int count = 5; + + system_init (); + tinygl_init (LOOP_RATE); + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + tinygl_text_mode_set (TINYGL_TEXT_MODE_STEP); + + navswitch_init (); + ir_uart_init (); + + show_count (count); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + ir_uart_putc ('1'); + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + ir_uart_putc ('2'); + + if (ir_uart_read_ready_p ()) + { + uint8_t data; + + data = ir_uart_getc (); + + if (data == '1') + count--; + else if (data == '2') + count++; + show_count (count); + } + } + + return 0; +} diff --git a/apps/ir_uart_test2/Makefile b/apps/ir_uart_test2/Makefile new file mode 100644 index 0000000..80eebf2 --- /dev/null +++ b/apps/ir_uart_test2/Makefile @@ -0,0 +1,78 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for ir_uart_test2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: ir_uart_test2.out + + +# Compile: create object files from C source files. +ir_uart_test2.o: ir_uart_test2.c ../../drivers/avr/ir_uart.h ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_uart.o: ../../drivers/avr/ir_uart.c ../../drivers/avr/ir_uart.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/avr/timer0.h ../../drivers/avr/usart1.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +prescale.o: ../../drivers/avr/prescale.c ../../drivers/avr/prescale.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer0.o: ../../drivers/avr/timer0.c ../../drivers/avr/bits.h ../../drivers/avr/prescale.h ../../drivers/avr/system.h ../../drivers/avr/timer0.h + $(CC) -c $(CFLAGS) $< -o $@ + +usart1.o: ../../drivers/avr/usart1.c ../../drivers/avr/system.h ../../drivers/avr/usart1.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +ir_uart_test2.out: display.o ir_uart.o ir_uart_test2.o ledmat.o navswitch.o pacer.o pio.o prescale.o system.o timer.o timer0.o tinygl.o usart1.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: ir_uart_test2.out + $(OBJCOPY) -O ihex ir_uart_test2.out ir_uart_test2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash ir_uart_test2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/ir_uart_test2/Makefile.test b/apps/ir_uart_test2/Makefile.test new file mode 100644 index 0000000..c3482ad --- /dev/null +++ b/apps/ir_uart_test2/Makefile.test @@ -0,0 +1,63 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for ir_uart_test2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: ir_uart_test2 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +ir_uart_test2-test.o: ir_uart_test2.c ../../drivers/navswitch.h ../../drivers/test/system.h ../../utils/font.h ../../drivers/test/ir_uart.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_uart-test.o: ../../drivers/test/ir_uart.c ../../drivers/test/ir_uart.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +ir_uart_test2: timer-test.o display-test.o pio-test.o ir_uart_test2-test.o pacer-test.o ir_uart-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) ir_uart_test2 timer-test.o display-test.o pio-test.o ir_uart_test2-test.o pacer-test.o ir_uart-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/ir_uart_test2/ir_uart_test2.c b/apps/ir_uart_test2/ir_uart_test2.c new file mode 100644 index 0000000..e552167 --- /dev/null +++ b/apps/ir_uart_test2/ir_uart_test2.c @@ -0,0 +1,77 @@ +/** @file ir_uart_test2.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Test program for IR serial communnications. + + @defgroup ir_uart_test2 Test program for IR serial communications. +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "ir_uart.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + + +static void show_char (char ch) +{ + char buffer[2]; + + buffer[0] = ch; + buffer[1] = 0; + tinygl_text (buffer); +} + + +int main (void) +{ + system_init (); + tinygl_init (LOOP_RATE); + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + tinygl_text_mode_set (TINYGL_TEXT_MODE_STEP); + + navswitch_init (); + ir_uart_init (); + + pacer_init (LOOP_RATE); + + show_char ('M'); + + /* Paced loop. */ + while (1) + { + uint8_t data = 'M'; + + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + ir_uart_putc (--data); + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + ir_uart_putc (++data); + + if (ir_uart_read_ready_p ()) + { + uint8_t data; + + data = ir_uart_getc (); + show_char (data); + } + } + + return 0; +} diff --git a/apps/led1/Makefile b/apps/led1/Makefile new file mode 100644 index 0000000..d8a8531 --- /dev/null +++ b/apps/led1/Makefile @@ -0,0 +1,57 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for led1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: led1.out + + +# Compile: create object files from C source files. +led1.o: led1.c ../../drivers/avr/system.h ../../drivers/led.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +led1.out: led.o led1.o pacer.o pio.o system.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: led1.out + $(OBJCOPY) -O ihex led1.out led1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash led1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/led1/Makefile.test b/apps/led1/Makefile.test new file mode 100644 index 0000000..d7e11d1 --- /dev/null +++ b/apps/led1/Makefile.test @@ -0,0 +1,51 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for led1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: led1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +led1-test.o: led1.c ../../drivers/led.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +led1: timer-test.o led-test.o pio-test.o pacer-test.o system-test.o mgetkey-test.o led1-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) led1 timer-test.o led-test.o pio-test.o pacer-test.o system-test.o mgetkey-test.o led1-test.o + + diff --git a/apps/led1/led1.c b/apps/led1/led1.c new file mode 100644 index 0000000..7e32e1b --- /dev/null +++ b/apps/led1/led1.c @@ -0,0 +1,40 @@ +/** @file led1.c + @author M.P. Hayes + @date 5 Oct 2010 +*/ + +#include "system.h" +#include "led.h" +#include "pacer.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 1000 + +#define LED_RATE 2 + +int main (void) +{ + unsigned int count = 0; + unsigned int period = LOOP_RATE / LED_RATE; + + system_init (); + + pacer_init (LOOP_RATE); + led_init (); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + led_set (LED1, count < period / 2); + count++; + if (count >= period) + count = 0; + + } + + return 0; +} diff --git a/apps/led2/Makefile b/apps/led2/Makefile new file mode 100644 index 0000000..0a9af1f --- /dev/null +++ b/apps/led2/Makefile @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for led2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: led2.out + + +# Compile: create object files from C source files. +led2.o: led2.c ../../drivers/avr/system.h ../../drivers/led.h ../../drivers/navswitch.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +led2.out: led.o led2.o navswitch.o pacer.o pio.o system.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: led2.out + $(OBJCOPY) -O ihex led2.out led2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash led2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/led2/Makefile.test b/apps/led2/Makefile.test new file mode 100644 index 0000000..4688fd4 --- /dev/null +++ b/apps/led2/Makefile.test @@ -0,0 +1,54 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for led2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: led2 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led2-test.o: led2.c ../../drivers/led.h ../../utils/pacer.h ../../drivers/navswitch.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +led2: timer-test.o led-test.o pio-test.o pacer-test.o system-test.o led2-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) led2 timer-test.o led-test.o pio-test.o pacer-test.o system-test.o led2-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/led2/led2.c b/apps/led2/led2.c new file mode 100644 index 0000000..1ec982b --- /dev/null +++ b/apps/led2/led2.c @@ -0,0 +1,69 @@ +/** @file led2.c + @author M.P. Hayes + @date 5 Oct 2010 +*/ + +#include "system.h" +#include "navswitch.h" +#include "led.h" +#include "pacer.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 1000 + +#define LED_RATE_INC 2 +#define LED_RATE_MIN 0 +#define LED_RATE_MAX 100 + +typedef struct pen pen_t; + + +int main (void) +{ + int freq = 1; + unsigned int count = 0; + unsigned int period = LOOP_RATE / freq; + + system_init (); + + pacer_init (LOOP_RATE); + led_init (); + navswitch_init (); + + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_SOUTH)) + { + freq -= LED_RATE_INC; + if (freq < LED_RATE_MIN) + freq = LED_RATE_MIN; + + period = LOOP_RATE / freq; + } + + if (navswitch_push_event_p (NAVSWITCH_NORTH)) + { + freq += LED_RATE_INC; + if (freq > LED_RATE_MAX) + freq = LED_RATE_MAX; + + period = LOOP_RATE / freq; + } + + led_set (LED1, count < period / 2); + count++; + if (count >= period) + count = 0; + + } + + return 0; +} diff --git a/apps/led3/Makefile b/apps/led3/Makefile new file mode 100644 index 0000000..2af9acf --- /dev/null +++ b/apps/led3/Makefile @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for led3 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: led3.out + + +# Compile: create object files from C source files. +led3.o: led3.c ../../drivers/avr/system.h ../../drivers/led.h ../../drivers/navswitch.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +led3.out: led.o led3.o navswitch.o pacer.o pio.o system.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: led3.out + $(OBJCOPY) -O ihex led3.out led3.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash led3.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/led3/Makefile.test b/apps/led3/Makefile.test new file mode 100644 index 0000000..f6502c9 --- /dev/null +++ b/apps/led3/Makefile.test @@ -0,0 +1,54 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for led3 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: led3 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led3-test.o: led3.c ../../drivers/led.h ../../utils/pacer.h ../../drivers/navswitch.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +led3: timer-test.o led-test.o pio-test.o system-test.o pacer-test.o mgetkey-test.o navswitch-test.o led3-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) led3 timer-test.o led-test.o pio-test.o system-test.o pacer-test.o mgetkey-test.o navswitch-test.o led3-test.o + + diff --git a/apps/led3/led3.c b/apps/led3/led3.c new file mode 100644 index 0000000..f9f8061 --- /dev/null +++ b/apps/led3/led3.c @@ -0,0 +1,68 @@ +/** @file led3.c + @author M.P. Hayes + @date 5 Oct 2010 +*/ + +#include "system.h" +#include "navswitch.h" +#include "led.h" +#include "pacer.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 1000 + +#define LED_RATE 50 +#define LED_DUTY_INC 5 +#define LED_DUTY_MIN 0 +#define LED_DUTY_MAX 100 + + +int main (void) +{ + int duty = 50; + unsigned int count = 0; + unsigned int period = LOOP_RATE / LED_RATE; + unsigned int offcount = period * duty / 100; + + system_init (); + + pacer_init (LOOP_RATE); + led_init (); + navswitch_init (); + + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_SOUTH)) + { + duty -= LED_DUTY_INC; + if (duty < LED_DUTY_MIN) + duty = LED_DUTY_MIN; + + offcount = period * duty / 100; + } + + if (navswitch_push_event_p (NAVSWITCH_NORTH)) + { + duty += LED_DUTY_INC; + if (duty > LED_DUTY_MAX) + duty = LED_DUTY_MAX; + offcount = period * duty / 100; + } + + led_set (LED1, count < offcount); + count++; + if (count >= period) + count = 0; + + } + + return 0; +} diff --git a/apps/led4/Makefile b/apps/led4/Makefile new file mode 100644 index 0000000..f06bdb2 --- /dev/null +++ b/apps/led4/Makefile @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for led4 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: led4.out + + +# Compile: create object files from C source files. +led4.o: led4.c ../../drivers/avr/system.h ../../drivers/led.h ../../drivers/navswitch.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +led4.out: led.o led4.o navswitch.o pacer.o pio.o system.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: led4.out + $(OBJCOPY) -O ihex led4.out led4.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash led4.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/led4/Makefile.test b/apps/led4/Makefile.test new file mode 100644 index 0000000..3cc9689 --- /dev/null +++ b/apps/led4/Makefile.test @@ -0,0 +1,54 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for led4 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: led4 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +led4-test.o: led4.c ../../drivers/led.h ../../utils/pacer.h ../../drivers/navswitch.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +led4: timer-test.o led-test.o pio-test.o led4-test.o pacer-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) led4 timer-test.o led-test.o pio-test.o led4-test.o pacer-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/led4/led4.c b/apps/led4/led4.c new file mode 100644 index 0000000..182e4c5 --- /dev/null +++ b/apps/led4/led4.c @@ -0,0 +1,68 @@ +/** @file led4.c + @author M.P. Hayes + @date 5 Oct 2010 +*/ + +#include "system.h" +#include "navswitch.h" +#include "led.h" +#include "pacer.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 4000 + +#define LED_RATE 40 + +typedef struct pen pen_t; + + +int main (void) +{ + unsigned int duty[] = {0, 1, 2, 5, 10, 20, 50, 100}; + unsigned int duty_index = 3; + unsigned int count = 0; + unsigned int period = LOOP_RATE / LED_RATE; + unsigned int offcount = period * duty[duty_index] / 100; + + system_init (); + + pacer_init (LOOP_RATE); + led_init (); + navswitch_init (); + + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + /* There is no need to poll the switches so frequently but it + keeps the code simpler. */ + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_SOUTH)) + { + if (duty_index > 0) + duty_index--; + + offcount = (period * duty[duty_index] + 50) / 100; + } + + if (navswitch_push_event_p (NAVSWITCH_NORTH)) + { + duty_index++; + if (duty_index >= ARRAY_SIZE (duty)) + duty_index = ARRAY_SIZE (duty) - 1; + offcount = (period * duty[duty_index] + 50) / 100; + } + + led_set (LED1, count < offcount); + count++; + if (count >= period) + count = 0; + } + + return 0; +} diff --git a/apps/led5/Makefile b/apps/led5/Makefile new file mode 100644 index 0000000..ed60927 --- /dev/null +++ b/apps/led5/Makefile @@ -0,0 +1,54 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for led5 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: led5.out + + +# Compile: create object files from C source files. +led5.o: led5.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +led5.out: led.o led5.o pio.o system.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: led5.out + $(OBJCOPY) -O ihex led5.out led5.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash led5.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/led5/Makefile.test b/apps/led5/Makefile.test new file mode 100644 index 0000000..22f79f7 --- /dev/null +++ b/apps/led5/Makefile.test @@ -0,0 +1,48 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for led5 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: led5 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +led5-test.o: led5.c ../../drivers/test/timer.h ../../drivers/led.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +led5: timer-test.o mgetkey-test.o led-test.o pio-test.o led5-test.o system-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) led5 timer-test.o mgetkey-test.o led-test.o pio-test.o led5-test.o system-test.o + + diff --git a/apps/led5/led5.c b/apps/led5/led5.c new file mode 100644 index 0000000..a3da777 --- /dev/null +++ b/apps/led5/led5.c @@ -0,0 +1,31 @@ +/** @file led5.c + @author M.P. Hayes + @date 18 Aug 2011 +*/ + +#include "system.h" +#include "timer.h" +#include "led.h" + +int main (void) +{ + timer_tick_t now; + + system_init (); + + timer_init (); + led_init (); + + now = timer_get (); + while (1) + { + led_set (LED1, 1); + + now = timer_wait_until (now + (timer_tick_t)(TIMER_RATE * 0.5)); + + led_set (LED1, 0); + + now = timer_wait_until (now + (timer_tick_t)(TIMER_RATE * 0.75)); + } + return 0; +} diff --git a/apps/pio1/Makefile b/apps/pio1/Makefile new file mode 100644 index 0000000..b1e5326 --- /dev/null +++ b/apps/pio1/Makefile @@ -0,0 +1,48 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for pio1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: pio1.out + + +# Compile: create object files from C source files. +pio1.o: pio1.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +pio1.out: pio.o pio1.o system.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: pio1.out + $(OBJCOPY) -O ihex pio1.out pio1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash pio1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/pio1/Makefile.test b/apps/pio1/Makefile.test new file mode 100644 index 0000000..9ce7261 --- /dev/null +++ b/apps/pio1/Makefile.test @@ -0,0 +1,42 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for pio1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: pio1 + + +# Compile: create object files from C source files. +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio1-test.o: pio1.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +pio1: mgetkey-test.o pio1-test.o pio-test.o system-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) pio1 mgetkey-test.o pio1-test.o pio-test.o system-test.o + + diff --git a/apps/pio1/pio1.c b/apps/pio1/pio1.c new file mode 100644 index 0000000..a1b6e53 --- /dev/null +++ b/apps/pio1/pio1.c @@ -0,0 +1,21 @@ +/** @file pio1.c + @author M.P. Hayes + @date 25 Aug 2011 +*/ + +#include "system.h" +#include "pio.h" + +int main (void) +{ + system_init (); + + pio_config_set (LED1_PIO, PIO_OUTPUT_HIGH); + + while (1) + { + pio_output_toggle (LED1_PIO); + } + + return 0; +} diff --git a/apps/pio2/Makefile b/apps/pio2/Makefile new file mode 100644 index 0000000..cdeaa43 --- /dev/null +++ b/apps/pio2/Makefile @@ -0,0 +1,48 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for pio2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: pio2.out + + +# Compile: create object files from C source files. +pio2.o: pio2.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +pio2.out: pio.o pio2.o system.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: pio2.out + $(OBJCOPY) -O ihex pio2.out pio2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash pio2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/pio2/Makefile.test b/apps/pio2/Makefile.test new file mode 100644 index 0000000..5f19707 --- /dev/null +++ b/apps/pio2/Makefile.test @@ -0,0 +1,42 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for pio2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: pio2 + + +# Compile: create object files from C source files. +pio2-test.o: pio2.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +pio2: pio2-test.o mgetkey-test.o pio-test.o system-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) pio2 pio2-test.o mgetkey-test.o pio-test.o system-test.o + + diff --git a/apps/pio2/pio2.c b/apps/pio2/pio2.c new file mode 100644 index 0000000..324efb0 --- /dev/null +++ b/apps/pio2/pio2.c @@ -0,0 +1,27 @@ +/** @file pio2.c + @author M.P. Hayes + @date 25 Aug 2011 +*/ + +#include "system.h" +#include "pio.h" + +int main (void) +{ + uint16_t count = 0; + + system_init (); + + pio_config_set (LED1_PIO, PIO_OUTPUT_HIGH); + + while (1) + { + if (count > 10000) + { + pio_output_toggle (LED1_PIO); + count = 0; + } + } + + return 0; +} diff --git a/apps/pio3/Makefile b/apps/pio3/Makefile new file mode 100644 index 0000000..6d5f643 --- /dev/null +++ b/apps/pio3/Makefile @@ -0,0 +1,51 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for pio3 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: pio3.out + + +# Compile: create object files from C source files. +pio3.o: pio3.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +pio3.out: pio.o pio3.o system.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: pio3.out + $(OBJCOPY) -O ihex pio3.out pio3.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash pio3.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/pio3/Makefile.test b/apps/pio3/Makefile.test new file mode 100644 index 0000000..0b2c617 --- /dev/null +++ b/apps/pio3/Makefile.test @@ -0,0 +1,45 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for pio3 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: pio3 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pio3-test.o: pio3.c ../../drivers/test/timer.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +pio3: timer-test.o mgetkey-test.o pio-test.o pio3-test.o system-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) pio3 timer-test.o mgetkey-test.o pio-test.o pio3-test.o system-test.o + + diff --git a/apps/pio3/pio3.c b/apps/pio3/pio3.c new file mode 100644 index 0000000..55eb797 --- /dev/null +++ b/apps/pio3/pio3.c @@ -0,0 +1,40 @@ +/** @file pio3.c + @author M.P. Hayes + @date 25 Aug 2011 +*/ + +#include "system.h" +#include "pio.h" +#include "timer.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 1000 + +#define LED_RATE 2 + +int main (void) +{ + unsigned int count = 0; + unsigned int period = LOOP_RATE / LED_RATE; + + system_init (); + + timer_init (); + + pio_config_set (LED1_PIO, PIO_OUTPUT_HIGH); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + timer_wait (TIMER_RATE / LOOP_RATE); + + pio_output_set (LED1_PIO, count < period / 2); + count++; + if (count >= period) + count = 0; + } + + return 0; +} diff --git a/apps/scribble1/Makefile b/apps/scribble1/Makefile new file mode 100644 index 0000000..706ba2f --- /dev/null +++ b/apps/scribble1/Makefile @@ -0,0 +1,72 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for scribble1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: scribble1.out + + +# Compile: create object files from C source files. +scribble1.o: scribble1.c ../../drivers/avr/system.h ../../drivers/button.h ../../drivers/display.h ../../drivers/led.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +button.o: ../../drivers/button.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/button.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +scribble1.out: button.o display.o led.o ledmat.o navswitch.o pacer.o pio.o scribble1.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: scribble1.out + $(OBJCOPY) -O ihex scribble1.out scribble1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash scribble1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/scribble1/Makefile.test b/apps/scribble1/Makefile.test new file mode 100644 index 0000000..a53607f --- /dev/null +++ b/apps/scribble1/Makefile.test @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for scribble1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: scribble1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +button-test.o: ../../drivers/button.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/button.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +scribble1-test.o: scribble1.c ../../drivers/navswitch.h ../../drivers/test/system.h ../../utils/font.h ../../drivers/led.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/button.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +scribble1: timer-test.o led-test.o display-test.o pio-test.o button-test.o pacer-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o scribble1-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) scribble1 timer-test.o led-test.o display-test.o pio-test.o button-test.o pacer-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o scribble1-test.o + + diff --git a/apps/scribble1/scribble1.c b/apps/scribble1/scribble1.c new file mode 100644 index 0000000..5dadaa6 --- /dev/null +++ b/apps/scribble1/scribble1.c @@ -0,0 +1,104 @@ +/** @file scribble1.c + @author M.P. Hayes + @date 5 Oct 2010 +*/ + +#include "system.h" +#include "button.h" +#include "navswitch.h" +#include "led.h" +#include "tinygl.h" +#include "pacer.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +#define SCRIBBLE_SPEED 2 + + +typedef enum dir dir_t; + +struct pen +{ + /* Current pos of pen. */ + tinygl_point_t pos; + /* Current state of pen. */ + bool state; +}; + +typedef struct pen pen_t; + + +int main (void) +{ + pen_t pen; + + system_init (); + + pen.pos.x = TINYGL_WIDTH / 2; + pen.pos.y = TINYGL_HEIGHT / 2; + pen.state = 1; + + pacer_init (LOOP_RATE); + led_init (); + button_init (); + navswitch_init (); + + tinygl_init (LOOP_RATE); + tinygl_draw_point (pen.pos, pen.state); + + led_set (LED1, pen.state); + + /* Paced loop. */ + while (1) + { + bool draw = 0; + + /* Wait for next tick. */ + pacer_wait (); + + navswitch_update (); + button_update (); + + if (navswitch_push_event_p (NAVSWITCH_WEST) && pen.pos.x > 0) + { + pen.pos.x--; + draw = 1; + } + + if (navswitch_push_event_p (NAVSWITCH_EAST) && pen.pos.x < TINYGL_WIDTH - 1) + { + pen.pos.x++; + draw = 1; + } + + if (navswitch_push_event_p (NAVSWITCH_SOUTH) && pen.pos.y < TINYGL_HEIGHT - 1) + { + pen.pos.y++; + draw = 1; + } + + if (navswitch_push_event_p (NAVSWITCH_NORTH) && pen.pos.y > 0) + { + pen.pos.y--; + draw = 1; + } + + if (navswitch_push_event_p (NAVSWITCH_PUSH) + || button_push_event_p (BUTTON1)) + { + pen.state = !pen.state; + led_set (LED1, pen.state); + } + + if (draw) + tinygl_draw_point (pen.pos, pen.state); + + + tinygl_update (); + } + + return 0; +} diff --git a/apps/snake1/Makefile b/apps/snake1/Makefile new file mode 100644 index 0000000..2d244c9 --- /dev/null +++ b/apps/snake1/Makefile @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for snake1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: snake1.out + + +# Compile: create object files from C source files. +snake1.o: snake1.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +snake1.out: display.o ledmat.o navswitch.o pacer.o pio.o snake1.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: snake1.out + $(OBJCOPY) -O ihex snake1.out snake1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash snake1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/snake1/Makefile.test b/apps/snake1/Makefile.test new file mode 100644 index 0000000..82d16fd --- /dev/null +++ b/apps/snake1/Makefile.test @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for snake1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: snake1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +snake1-test.o: snake1.c ../../drivers/navswitch.h ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +snake1: timer-test.o display-test.o pio-test.o system-test.o pacer-test.o tinygl-test.o ledmat-test.o snake1-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) snake1 timer-test.o display-test.o pio-test.o system-test.o pacer-test.o tinygl-test.o ledmat-test.o snake1-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/snake1/snake1.c b/apps/snake1/snake1.c new file mode 100644 index 0000000..79d0928 --- /dev/null +++ b/apps/snake1/snake1.c @@ -0,0 +1,127 @@ +/** @file snake1.c + @author M.P. Hayes + @date 5 Oct 2010 + @note The sneake head deliberately moves off screen unless turned. +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +#define SNAKE_SPEED 2 + +enum dir {DIR_N, DIR_E, DIR_S, DIR_W}; + +typedef enum dir dir_t; + +struct snake +{ + /* Current head of snake. */ + tinygl_point_t pos; + /* Current direction. */ + enum dir dir; +}; + +typedef struct snake snake_t; + + +static snake_t snake_move (snake_t snake) +{ + switch (snake.dir) + { + case DIR_N: + snake.pos.y = snake.pos.y - 1; + break; + + case DIR_E: + snake.pos.x = snake.pos.x + 1; + break; + + case DIR_S: + snake.pos.y = snake.pos.y + 1; + break; + + case DIR_W: + snake.pos.x = snake.pos.x - 1; + break; + } + tinygl_draw_point (snake.pos, 1); + return snake; +} + + +static snake_t snake_turn_left (snake_t snake) +{ + dir_t newdir[] = {DIR_W, DIR_N, DIR_E, DIR_S}; + + snake.dir = newdir[snake.dir]; + return snake; +} + + +static snake_t snake_turn_right (snake_t snake) +{ + dir_t newdir[] = {DIR_E, DIR_S, DIR_W, DIR_N}; + + snake.dir = newdir[snake.dir]; + return snake; +} + + +int main (void) +{ + snake_t snake; + int tick = 0; + + system_init (); + + snake.dir = DIR_N; + snake.pos.x = TINYGL_WIDTH / 2; + snake.pos.y = TINYGL_HEIGHT - 1; + + tinygl_init (LOOP_RATE); + tinygl_font_set (&font5x7_1); + + navswitch_init (); + + pacer_init (LOOP_RATE); + + tinygl_draw_point (snake.pos, 1); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + navswitch_update (); + + tick = tick + 1; + if (tick > LOOP_RATE / SNAKE_SPEED) + { + tick = 0; + snake = snake_move (snake); + } + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + { + snake = snake_turn_left (snake); + } + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + { + snake = snake_turn_right (snake); + } + + + tinygl_update (); + } + + return 0; +} diff --git a/apps/space10/Makefile b/apps/space10/Makefile new file mode 100644 index 0000000..77e9056 --- /dev/null +++ b/apps/space10/Makefile @@ -0,0 +1,81 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for space10 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: space10.out + + +# Compile: create object files from C source files. +space10.o: space10.c ../../drivers/avr/eeprom.h ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ir.h ../../drivers/ir_serial.h ../../fonts/font3x5_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h ../../utils/uint8toa.h flasher.h spacey.h + $(CC) -c $(CFLAGS) $< -o $@ + +flasher.o: flasher.c ../../drivers/avr/system.h flasher.h + $(CC) -c $(CFLAGS) $< -o $@ + +spacey.o: spacey.c ../../drivers/avr/system.h spacey.h + $(CC) -c $(CFLAGS) $< -o $@ + +eeprom.o: ../../drivers/avr/eeprom.c ../../drivers/avr/eeprom.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir.o: ../../drivers/ir.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ir.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial.o: ../../drivers/ir_serial.c ../../drivers/avr/delay.h ../../drivers/avr/system.h ../../drivers/ir.h ../../drivers/ir_serial.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +uint8toa.o: ../../utils/uint8toa.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +space10.out: display.o eeprom.o flasher.o ir.o ir_serial.o ledmat.o pacer.o pio.o space10.o spacey.o system.o timer.o tinygl.o uint8toa.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: space10.out + $(OBJCOPY) -O ihex space10.out space10.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash space10.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/space10/Makefile.test b/apps/space10/Makefile.test new file mode 100644 index 0000000..e2fdf5d --- /dev/null +++ b/apps/space10/Makefile.test @@ -0,0 +1,75 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for space10 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: space10 + + +# Compile: create object files from C source files. +space10-test.o: space10.c ../../utils/font.h ../../drivers/ir_serial.h ../../drivers/test/system.h ../../drivers/test/eeprom.h ../../fonts/font3x5_1.h flasher.h ../../utils/tinygl.h spacey.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/ir.h ../../utils/uint8toa.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +spacey-test.o: spacey.c spacey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial-test.o: ../../drivers/ir_serial.c ../../drivers/ir.h ../../drivers/ir_serial.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +flasher-test.o: flasher.c flasher.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +uint8toa-test.o: ../../utils/uint8toa.c ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +eeprom-test.o: ../../drivers/test/eeprom.c ../../drivers/test/eeprom.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir-test.o: ../../drivers/ir.c ../../drivers/test/pio.h ../../drivers/ir.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +space10: space10-test.o timer-test.o display-test.o spacey-test.o pio-test.o ir_serial-test.o pacer-test.o flasher-test.o tinygl-test.o uint8toa-test.o ledmat-test.o eeprom-test.o system-test.o ir-test.o mgetkey-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) space10 space10-test.o timer-test.o display-test.o spacey-test.o pio-test.o ir_serial-test.o pacer-test.o flasher-test.o tinygl-test.o uint8toa-test.o ledmat-test.o eeprom-test.o system-test.o ir-test.o mgetkey-test.o + + diff --git a/apps/space10/flasher.c b/apps/space10/flasher.c new file mode 100644 index 0000000..2cdebd4 --- /dev/null +++ b/apps/space10/flasher.c @@ -0,0 +1,100 @@ +/** @file flasher.c + @author M. P. Hayes, UCECE + @date 13 March 2005 + @brief +*/ + +#include "flasher.h" + + +/* These routines are for flashing a LED or beeping a piezo tweeter. + Perhaps they should be separated into software PWM and flash + pattern sequencing. */ + +int8_t +flasher_pattern_set (flasher_t flasher, flasher_pattern_t *pattern) +{ + flasher->pattern = pattern; + flasher->mod_count = 0; + flasher->flasher_count = 0; + flasher->flashes_count = 0; + flasher->flasher_prescale = 0; + return 1; +} + + +flasher_pattern_t * +flasher_pattern_get (flasher_t flasher) +{ + return flasher->pattern; +} + + +/* FIXME. */ +int8_t +flasher_phase_set (flasher_t flasher, uint8_t phase) +{ + flasher->mod_count = 0; + flasher->flasher_count = 0; + flasher->flashes_count = phase; + return 1; +} + + +/* Return the next state for the associated device. For example, + to control the flashing of a LED use: + + led_set (led, flasher_update (flasher)); */ +bool +flasher_update (flasher_t flasher) +{ + if (!flasher->pattern) + return 0; + + flasher->mod_count++; + if (flasher->mod_count >= flasher->pattern->mod_period) + { + flasher->mod_count = 0; + flasher->flasher_prescale++; + + if (flasher->flasher_prescale >= FLASHER_PRESCALE) + { + flasher->flasher_prescale = 0; + flasher->flasher_count++; + + if (flasher->flasher_count >= flasher->pattern->flasher_period) + { + flasher->flasher_count = 0; + flasher->flashes_count++; + + if (!flasher->pattern->period) + { + /* One shot mode. */ + if (flasher->flashes_count >= flasher->pattern->flashes) + { + /* Disable pattern. */ + flasher->pattern = 0; + return 1; + } + } + else if (flasher->flashes_count >= flasher->pattern->period) + { + flasher->flashes_count = 0; + } + } + } + } + + return flasher->mod_count < flasher->pattern->mod_duty + && flasher->flasher_count < flasher->pattern->flasher_duty + && flasher->flashes_count < flasher->pattern->flashes; +} + + +/* Create a new flasher device. */ +flasher_t +flasher_init (flasher_obj_t *flasher) +{ + flasher_pattern_set (flasher, 0); + return flasher; +} diff --git a/apps/space10/flasher.h b/apps/space10/flasher.h new file mode 100644 index 0000000..4cfd6f8 --- /dev/null +++ b/apps/space10/flasher.h @@ -0,0 +1,97 @@ +/** @file flasher.h + @author M. P. Hayes, UCECE + @date 13 March 2005 + @brief Combined software PWM and flashing module. Use at your peril! +*/ +#ifndef FLASHER_H +#define FLASHER_H + +#include "system.h" + +/* This parameter is for internal use only. It's purpose is to + reduce the chances of flasher_period and flasher_duty overflow. + It could possibly be made an additional parameter for flasher_pattern_t + and calculated by FLASHER_PATTERN. */ +#define FLASHER_PRESCALE 8 + +/* POLL_RATE (Hz) + MOD_FREQ (Hz) + MOD_DUTY (percent) + FLASHER_PERIOD (s) - period between flashes + FLASHER_DUTY (percent) - proportion of flash period that is lit + FLASHES (integer) - how many flashes per flash pattern + PERIOD (s) - how often the flash pattern repeats +*/ + +#define FLASHER_PATTERN(POLL_RATE, MOD_FREQ, MOD_DUTY, FLASHER_PERIOD, \ + FLASHER_DUTY, FLASHES, PERIOD) \ + (POLL_RATE) / (double)(MOD_FREQ) + 0.5, \ + (POLL_RATE) * (double)(MOD_DUTY) / (MOD_FREQ) / 100.0 + 0.5, \ + (MOD_FREQ) * (FLASHER_PERIOD) / (double)FLASHER_PRESCALE + 0.5, \ + (MOD_FREQ) * (FLASHER_PERIOD) * (FLASHER_DUTY) / 100.0 / FLASHER_PRESCALE + 0.5, \ + (FLASHES), \ + (PERIOD) / (double)(FLASHER_PERIOD) + 0.5 + + +typedef struct +{ + /* This is the modulation period. It determines the frequency + of a tone or flicker rate of a LED. */ + uint8_t mod_period; + /* This is the modulation duty. It determines the luminance of a LED. */ + uint8_t mod_duty; + /* This is the period between the start of two flashes in a sequence. */ + uint8_t flasher_period; + /* This is the flash period. */ + uint8_t flasher_duty; + /* This is the number of flashes in the sequence. */ + uint8_t flashes; + /* This is the number of flasher periods before the sequence repeats. */ + uint8_t period; +} flasher_pattern_t; + + +#define FLASHER_ACTIVE_P(FLASHER) \ + (((flasher_obj_t *)(FLASHER))->pattern != 0) + + +#define FLASHER_PATTERN_FLASHES_SET(PATTERN, FLASHES) \ + (PATTERN)->flashes = (FLASHES) + +/* This structure is defined here so the compiler can allocate enough + memory for it. However, its fields should be treated as + private. */ +typedef struct +{ + flasher_pattern_t *pattern; + uint8_t mod_count; + uint8_t flasher_count; + uint8_t flashes_count; + uint8_t flasher_prescale; +} flasher_private_t; + + +typedef flasher_private_t flasher_obj_t; +typedef flasher_obj_t *flasher_t; + + +extern int8_t +flasher_pattern_set (flasher_t flasher, flasher_pattern_t *pattern); + +extern flasher_pattern_t * +flasher_pattern_get (flasher_t flasher); + +extern int8_t +flasher_phase_set (flasher_t flasher, uint8_t phase); + +extern bool +flasher_update (flasher_t); + + +/* INFO is a pointer into RAM that stores the state of the FLASH. + CFG is a pointer into ROM to define the port the FLASH is connected to. + The returned handle is passed to the other flasher_xxx routines to denote + the FLASH to operate on. */ +extern flasher_t +flasher_init (flasher_obj_t *info); +#endif diff --git a/apps/space10/space10.c b/apps/space10/space10.c new file mode 100644 index 0000000..60b78e3 --- /dev/null +++ b/apps/space10/space10.c @@ -0,0 +1,363 @@ +/** @file space10.c + @author M. P. Hayes, UCECE + @date 20 April 2007 + @brief A simple space invaders game with different difficulty levels + and IR remote control. + + @defgroup space10 A simple space invaders game controlled by the @ref spacey_remote1 "spacey_remote1" application. +*/ + +#include +#include "system.h" +#include "display.h" +#include "tinygl.h" +#include "pacer.h" +#include "ir_serial.h" +#include "flasher.h" +#include "spacey.h" +#include "eeprom.h" +#include "uint8toa.h" +#include "../fonts/font3x5_1.h" + + +#define VERSION "1.6" + +/** Define polling rates in Hz. */ +enum {LOOP_RATE = 500}; +enum {FLASHER_UPDATE_RATE = LOOP_RATE}; +enum {GAME_UPDATE_RATE = 100}; +enum {GAME_OVER_PERIOD = 2}; + + +/** Define flasher modes. */ +static flasher_pattern_t flasher_patterns[] = +{ + /** POLL_RATE, MOD_FREQ, MOD_DUTY, FLASHER_PERIOD, + FLASHER_DUTY, FLASHES, PERIOD. */ + {FLASHER_PATTERN (FLASHER_UPDATE_RATE, 100, 100, 0.4, 100, 1, 0.4)}, + {FLASHER_PATTERN (FLASHER_UPDATE_RATE, 100, 100, 0.4, 100, 1, 0.4)}, + {FLASHER_PATTERN (FLASHER_UPDATE_RATE, 200, 100, 0.1, 50, 1, 0.1)}, +}; + +typedef enum {FLASH_MODE_GUN, FLASH_MODE_SHELL, + FLASH_MODE_ALIEN, FLASH_MODE_NUM} flash_mode_t; + + +typedef enum {STATE_DUMMY, STATE_INIT, STATE_START, + STATE_PLAYING, STATE_OVER, + STATE_READY, STATE_MENU_LEVEL} state_t; + +enum {GAME_LEVEL_MAX = 5}; + + +typedef struct +{ + uint8_t level; + uint8_t games; +} game_data_t; + + + +/** Draw pixel on display. */ +static void +display_handler (void *data, uint8_t col, uint8_t row, spacey_pix_t type) +{ + uint8_t *display = data; + uint8_t pixel; + + pixel = row * TINYGL_WIDTH + col; + display[pixel] = type; +} + + +/** Display the game over message. */ +static void +game_over_display (char *buffer) +{ + char *str = buffer; + + strcpy (str, "GAME OVER "); + while (*str) + str++; + *str++ = 'K'; + uint8toa (spacey_aliens_killed_get (), str, 0); + while (*str) + str++; + *str++ = 'F'; + uint8toa (spacey_shells_fired_get (), str, 0); + tinygl_clear (); + tinygl_text (buffer); +} + + +static void +game_text_display (uint8_t num, char *buffer, char *msg) +{ + char *str = buffer; + + while (*msg) + *str++ = *msg++; + uint8toa (num, str, 0); + tinygl_clear (); + tinygl_text (buffer); +} + + +/** Display the game level of difficulty. */ +static void +game_level_display (uint8_t level, char *buffer) +{ + game_text_display (level, buffer, "LEVEL"); +} + + +/** Set the game level of difficulty. */ +static void +game_level_set (uint8_t level) +{ + spacey_options_t options; + uint8_t alien_num = 3; + uint8_t alien_steps = 4; + + switch (level) + { + default: + case 0: + options.aliens_wrap = 0; + options.aliens_evasive = 0; + break; + + case 1: + options.aliens_wrap = 0; + options.aliens_evasive = 1; + alien_steps = 3; + break; + + case 2: + options.aliens_wrap = 1; + options.aliens_evasive = 0; + alien_steps = 3; + break; + + case 3: + options.aliens_wrap = 1; + options.aliens_evasive = 1; + alien_steps = 3; + break; + + case 4: + options.aliens_wrap = 1; + options.aliens_evasive = 1; + alien_steps = 3; + alien_num = 4; + break; + + case 5: + options.aliens_wrap = 1; + options.aliens_evasive = 1; + alien_steps = 2; + alien_num = 4; + break; + } + + spacey_alien_num_set (alien_num); + spacey_alien_create_steps_set (alien_steps); + spacey_options_set (options); +} + + +static void +game_start (game_data_t *data) +{ + tinygl_clear (); + data->games++; + game_level_set (data->level); + spacey_start (); + eeprom_write (0, data, sizeof (*data)); +} + + +void game_event (__unused__ void *data, spacey_event_t event) +{ + ir_serial_transmit (event); +} + + +int +main (void) +{ + uint8_t game_ticks; + uint8_t game_over_ticks; + state_t state = STATE_INIT; + flasher_t flashers[SPACEY_PIX_TYPE_NUM]; + uint8_t flasher_state[SPACEY_PIX_TYPE_NUM]; + flasher_obj_t flashers_info[SPACEY_PIX_TYPE_NUM]; + uint8_t display[TINYGL_WIDTH * TINYGL_HEIGHT]; + uint8_t i; + uint8_t j; + game_data_t data; + char message[44]; + ir_serial_ret_t ret; + uint8_t ir_data; + + system_init (); + + /* The first time EEPROM is read all the bytes are 0xFF so set to + sensible defaults. */ + eeprom_read (0, &data, sizeof (data)); + if (data.level == 0xff) + { + data.level = 0; + data.games = 0; + } + + for (i = 0; i < ARRAY_SIZE (flashers); i++) + { + flashers[i] = flasher_init (&flashers_info[i]); + flasher_state[i] = 0; + } + + for (i = 0; i < ARRAY_SIZE (display); i++) + display[i] = 0; + + /* Set up flash patterns for different pixel types. */ + flasher_pattern_set (flashers[SPACEY_PIX_GUN], + &flasher_patterns[FLASH_MODE_GUN]); + flasher_pattern_set (flashers[SPACEY_PIX_SHELL], + &flasher_patterns[FLASH_MODE_SHELL]); + flasher_pattern_set (flashers[SPACEY_PIX_ALIEN], + &flasher_patterns[FLASH_MODE_ALIEN]); + + tinygl_init (LOOP_RATE); + tinygl_font_set (&font3x5_1); + tinygl_text_mode_set (TINYGL_TEXT_MODE_SCROLL_LEFT); + tinygl_text_mode_set (TINYGL_TEXT_MODE_ROTATE_SCROLL_DOWN); + tinygl_text_speed_set (10); + + spacey_init (GAME_UPDATE_RATE, TINYGL_WIDTH, TINYGL_HEIGHT, + display_handler, display); + + spacey_event_handler_set (game_event, 0); + + game_ticks = 0; + game_over_ticks = 0; + + pacer_init (LOOP_RATE); + + while (1) + { + pacer_wait (); + + if (state == STATE_PLAYING) + { + uint8_t *src; + + /* Update flasher states. NB, the first flasher is always off. */ + for (i = 1; i < ARRAY_SIZE (flashers); i++) + flasher_state[i] = flasher_update (flashers[i]); + + /* Update display. */ + src = display; + for (j = 0; j < TINYGL_HEIGHT; j++) + for (i = 0; i < TINYGL_WIDTH; i++) + { + tinygl_point_t point = {i, j}; + + tinygl_draw_point (point, flasher_state[*src++]); + } + } + + /* Advance messages and refresh display. */ + tinygl_update (); + + game_ticks++; + if (game_ticks >= LOOP_RATE / GAME_UPDATE_RATE) + { + game_ticks = 0; + + switch (state) + { + case STATE_PLAYING: + if (! spacey_update ()) + { + game_over_display (message); + game_over_ticks = 0; + state = STATE_OVER; + } + break; + + case STATE_INIT: + tinygl_text ("SPACEY READY V" VERSION " BY MPH "); + state = STATE_READY; + break; + + case STATE_OVER: + game_over_ticks ++; + if (game_over_ticks >= GAME_UPDATE_RATE * GAME_OVER_PERIOD) + state = STATE_READY; + /* Fall through. */ + + case STATE_READY: + case STATE_MENU_LEVEL: + default: + break; + + case STATE_START: + /* Turn that bloody blimey space invader off... */ + game_start (&data); + state = STATE_PLAYING; + break; + } + } + + ret = ir_serial_receive (&ir_data); + if (ret == IR_SERIAL_OK) + { + if (ir_data == 1) + { + switch (state) + { + case STATE_READY: + state = STATE_MENU_LEVEL; + game_level_display (data.level, message); + break; + + case STATE_MENU_LEVEL: + state = STATE_INIT; + break; + + case STATE_PLAYING: + spacey_gun_move_right (); + break; + + default: + break; + } + } + + if (ir_data == 2) + { + switch (state) + { + case STATE_READY: + state = STATE_START; + break; + + case STATE_PLAYING: + spacey_gun_fire (); + break; + + case STATE_MENU_LEVEL: + data.level++; + if (data.level > GAME_LEVEL_MAX) + data.level = 0; + game_level_display (data.level, message); + break; + + default: + break; + } + } + } + } +} diff --git a/apps/space10/spacey.c b/apps/space10/spacey.c new file mode 100644 index 0000000..cb96d91 --- /dev/null +++ b/apps/space10/spacey.c @@ -0,0 +1,578 @@ +/** @file spacey.c + @author M. P. Hayes, UCECE + @date 13 March 2007 + @brief This implements a simple space invaders game. +*/ + +#include +#include "spacey.h" + +/* This function implements a simple space invades program. Aliens + randomly appear on the top row of the display and work their way + randomly down the display. The game ends when 5 aliens have + reached the bottom of the display. To kill the aliens, shells can + be fired at them from a gun on the bottom of the display. The gun + can only move across the bottom and when it gets to the side it + wraps around to the other side. + + The aliens could move at different speeds but currently they are + set at the same speed to keep things simple. +*/ + + +#ifndef DISPLAY_INTERLACE +#define DISPLAY_INTERLACE 0 +#endif + + +#if DISPLAY_INTERLACE +/* With an interlaced display we do not have odd numbered rows and + columns. */ +enum {SPACEY_SHELL_INC = 2, SPACEY_GUN_INC = 2}; +#else +enum {SPACEY_SHELL_INC = 1, SPACEY_GUN_INC = 1}; +#endif + + +enum {SPACEY_LIVES_DEFAULT = 5}; +enum {SPACEY_SHELL_SPEED_DEFAULT = 16}; +enum {SPACEY_ALIEN_SPEED_DEFAULT = 2}; +enum {SPACEY_ALIEN_CREATE_STEPS_DEFAULT = 4}; +enum {SPACEY_ALIEN_NUM_DEFAULT = 3}; +enum {SPACEY_SHELL_NUM_DEFAULT = 2}; + + +#define min(x, y) ((x) < (y) ? (x) : (y)) + +static spacey_t spacey_data; +static spacey_t * const spacey = &spacey_data; + + +void +spacey_lives_set (uint8_t lives) +{ + spacey->lives = lives; +} + + +void +spacey_alien_speed_set (uint8_t alien_speed) +{ + spacey->aliens.move_period = spacey->poll_rate / alien_speed; +} + + +void +spacey_shell_speed_set (uint8_t shell_speed) +{ + spacey->shells.move_period = spacey->poll_rate / shell_speed; +} + + +void +spacey_alien_create_steps_set (uint8_t alien_create_steps) +{ + spacey->alien_create_steps = alien_create_steps; +} + + +void +spacey_alien_num_set (uint8_t aliens_num) +{ + spacey->aliens.num = min (aliens_num, SPACEY_ALIENS_MAX); +} + + +void +spacey_shell_num_set (uint8_t shells_num) +{ + spacey->shells.num = min (shells_num, SPACEY_SHELLS_MAX); +} + + +void +spacey_options_set (spacey_options_t options) +{ + spacey->options = options; +} + + +void +spacey_init (uint16_t poll_rate, + uint8_t x_size, uint8_t y_size, + void (*display_handler) (void *data, uint8_t row, uint8_t col, + spacey_pix_t type), + void *display_data) +{ + spacey->size.x = x_size; + spacey->size.y = y_size; + spacey->display_hook = display_handler; + spacey->display_data = display_data; + spacey->active = 0; + spacey->poll_rate = poll_rate; + spacey->event_hook = NULL; + + spacey->options.aliens_wrap = 0; + spacey->options.aliens_evasive = 0; + + spacey_alien_num_set (SPACEY_ALIEN_NUM_DEFAULT); + spacey_shell_num_set (SPACEY_SHELL_NUM_DEFAULT); + spacey_shell_speed_set (SPACEY_SHELL_SPEED_DEFAULT); + spacey_alien_speed_set (SPACEY_ALIEN_SPEED_DEFAULT); + spacey_alien_create_steps_set (SPACEY_ALIEN_CREATE_STEPS_DEFAULT); + spacey->lives = SPACEY_LIVES_DEFAULT; +} + + +void +spacey_event_handler_set (void (*event_handler) (void *, spacey_event_t), + void *event_data) +{ + spacey->event_hook = event_handler; + spacey->event_data = event_data; +} + + +static void +spacey_pixel_set (spacey_pos_t *pos, + spacey_pix_t type) +{ + spacey->display_hook (spacey->display_data, pos->x, pos->y, type); +} + + +/* Return if obj is at the same point as another alien. */ +static spacey_obj_t * +spacey_alien_conflict_p (spacey_obj_t *obj) +{ + int8_t i; + + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (!alien->active || alien == obj) + continue; + + if (alien->pos.x == obj->pos.x + && alien->pos.y == obj->pos.y) + return alien; + } + return 0; +} + + +/* Return if obj is at the same point as another shell. */ +static spacey_obj_t * +spacey_shell_conflict_p (spacey_obj_t *obj) +{ + int8_t i; + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + if (!shell->active || shell == obj) + continue; + + if (shell->pos.x == obj->pos.x + && shell->pos.y == obj->pos.y) + return shell; + } + return 0; +} + + +static void +spacey_gun_move (int8_t inc) +{ + int8_t gun_x; + + spacey_pixel_set (&spacey->gun, SPACEY_PIX_OFF); + + gun_x = spacey->gun.x + inc; + if (gun_x >= spacey->size.x) + gun_x = 0; + else if (gun_x < 0) + gun_x = spacey->size.x - 1; + spacey->gun.x = gun_x; + + spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN); +} + + +/* Return true if move off display. */ +static bool +spacey_shell_move (spacey_obj_t *shell) +{ + /* Erase the previous position of the shell except if still + at gun. */ + if (shell->pos.x != spacey->gun.x + || shell->pos.y != spacey->gun.y) + spacey_pixel_set (&shell->pos, SPACEY_PIX_OFF); + + /* Shells only go straight up. */ + shell->pos.y -= SPACEY_SHELL_INC; + + if (shell->pos.y < 0) + return 1; + + spacey_pixel_set (&shell->pos, SPACEY_PIX_SHELL); + return 0; +} + + +static void +spacey_alien_erase (spacey_obj_t *alien) +{ + spacey_pixel_set (&alien->pos, SPACEY_PIX_OFF); +} + + +/* Return true if move off display or get to the bottom. */ +static bool +spacey_alien_move (spacey_obj_t *alien) +{ + int8_t x_jump; + + /* Aliens move down randomly zig zagging. */ + alien->pos.y++; + + /* Randomly move left or right. */ + x_jump = 2 * (rand () & 1) - 1; + + /* Don't move into line of gun if being evasive. */ + if (spacey->options.aliens_evasive && + spacey->gun.x == alien->pos.x + x_jump) + x_jump = -x_jump; + + alien->pos.x += x_jump; + + /* Keep the alien within bounds. */ + if (alien->pos.x < 0 || alien->pos.x >= spacey->size.x) + { + if (spacey->options.aliens_wrap) + alien->pos.x = spacey->size.x - abs (alien->pos.x); + else + alien->pos.x -= 2 * x_jump; + } + + if (alien->pos.y < spacey->size.y) + spacey_pixel_set (&alien->pos, SPACEY_PIX_ALIEN); + + if (alien->pos.y >= (spacey->size.y - 1)) + return 1; + + return 0; +} + + +static void +spacey_alien_create (void) +{ + int8_t i; + + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (alien->active) + continue; + + /* Aliens start at the top. */ + alien->pos.y = 0; + + while (1) + { + /* Choose random x position. */ + alien->pos.x = rand () % spacey->size.x; +#if DISPLAY_INTERLACE + /* Ensure x position is even. */ + alien->pos.x &= ~1; +#endif + alien->active = 1; + + /* Ensure aliens start at different spots to be fair! */ + if (!spacey_alien_conflict_p (alien)) + break; + } + spacey->stats.aliens_live++; + spacey_pixel_set (&alien->pos, SPACEY_PIX_ALIEN); + return; + } + /* If we get to here then we already have too many aliens. */ +} + + +static void +spacey_shell_create (void) +{ + int8_t i; + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + if (shell->active) + continue; + + shell->pos = spacey->gun; + shell->active = 1; + spacey->stats.shells_fired++; + /* Don't turn shell on initially since it will erase the gun. */ + return; + } + /* If we get to here then we already have too many shells. */ +} + + +static void +spacey_alien_kill (spacey_obj_t *alien) +{ + alien->active = 0; + spacey->stats.aliens_live--; +} + + +static void +spacey_shell_kill (spacey_obj_t *shell) +{ + shell->active = 0; +} + + +static void +spacey_event (spacey_event_t event) +{ + if (spacey->event_hook) + spacey->event_hook (spacey->event_data, event); +} + + +static void +spacey_alien_hit (spacey_obj_t *alien, spacey_obj_t *shell) +{ + spacey_alien_kill (alien); + spacey->stats.aliens_killed++; + + spacey_shell_kill (shell); + spacey_pixel_set (&shell->pos, SPACEY_PIX_OFF); + + spacey_event (SPACEY_EVENT_ALIEN_HIT); +} + + +static void +spacey_alien_landed (void) +{ + spacey->stats.aliens_landed++; + if (spacey->stats.aliens_landed >= spacey->lives) + spacey->active = 0; + + spacey_event (SPACEY_EVENT_ALIEN_LANDED); +} + + +static void +spacey_shells_move (void) +{ + int8_t i; + + spacey->shells.move_clock++; + if (spacey->shells.move_clock < spacey->shells.move_period) + return; + spacey->shells.move_clock = 0; + + /* Shells move until they hit an alien or move off the display. */ + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + if (!shell->active) + continue; + + if (spacey_shell_move (shell)) + spacey_shell_kill (shell); + else + { + spacey_obj_t *alien; + + /* Check if we've hit an alien. */ + while ((alien = spacey_alien_conflict_p (shell))) + { + /* We've hit an alien so kill it and the shell. */ + spacey_alien_hit (alien, shell); + } + } + } +} + + +static void +spacey_aliens_move (void) +{ + int8_t i; + uint8_t steps; + + spacey->aliens.move_clock++; + if (spacey->aliens.move_clock < spacey->aliens.move_period) + return; + spacey->aliens.move_clock = 0; + + /* Erase all aliens before moving them. */ + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (!alien->active) + continue; + + spacey_alien_erase (alien); + } + + /* Now move and redraw aliens. */ + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (!alien->active) + continue; + + if (spacey_alien_move (alien)) + { + if (alien->pos.y == spacey->gun.y) + { + /* The alien has reached Earth. */ + spacey_alien_landed (); + } + else + { + /* The alien has moved off the display so disable it. */ + spacey_alien_kill (alien); + + /* Redraw the gun in case it got hit by the alien. */ + spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN); + } + } + else + { + spacey_obj_t *shell; + + while ((shell = spacey_shell_conflict_p (alien))) + { + /* The alien hit a shell so kill it and the shell. */ + spacey_alien_hit (alien, shell); + } + } + } + + /* Randomly create an alien. If there are no aliens create a new + one more rapidly. */ + if (!spacey->stats.aliens_live) + steps = 2; + else + steps = spacey->alien_create_steps; + + if ((rand () % steps) == 0) + spacey_alien_create (); +} + + +/* Update the state of the game. */ +bool +spacey_update (void) +{ + /* Allow playing with the gun even if game inactive. */ + spacey_shells_move (); + + if (!spacey->active) + return 0; + + spacey_aliens_move (); + return 1; +} + + +/* Move the gun position to the right wrapping back around on left. */ +void +spacey_gun_move_right (void) +{ + spacey_gun_move (SPACEY_GUN_INC); +} + + +/* Move the gun position to the left wrapping back around on right. */ +void +spacey_gun_move_left (void) +{ + spacey_gun_move (-SPACEY_GUN_INC); +} + + +/* Fire the gun. */ +void +spacey_gun_fire (void) +{ + spacey_shell_create (); +} + + +/* Start a new game. */ +void +spacey_start (void) +{ + int8_t i; + int8_t j; + + spacey->shells.move_clock = 0; + spacey->aliens.move_clock = 0; + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + shell->active = 0; + } + + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + alien->active = 0; + } + + /* Turn all pixels off. */ + for (i = 0; i < spacey->size.x; i++) + for (j = 0; j < spacey->size.y; j++) + { + spacey_pos_t pos = {i, j}; + + spacey_pixel_set (&pos, SPACEY_PIX_OFF); + } + + /* Display gun in centre of display. */ + spacey->gun.x = (spacey->size.x / 2) & ~1; + spacey->gun.y = spacey->size.y - 1; + spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN); + + spacey->stats.aliens_live = 0; + spacey->stats.aliens_killed = 0; + spacey->stats.aliens_landed = 0; + spacey->stats.shells_fired = 0; + spacey->active = 1; +} + + +uint8_t +spacey_aliens_killed_get (void) +{ + return spacey->stats.aliens_killed; +} + + +uint8_t +spacey_shells_fired_get (void) +{ + return spacey->stats.shells_fired; +} diff --git a/apps/space10/spacey.h b/apps/space10/spacey.h new file mode 100644 index 0000000..5b8b868 --- /dev/null +++ b/apps/space10/spacey.h @@ -0,0 +1,152 @@ +/** @file spacey.h + @author M. P. Hayes, UCECE + @date 13 March 2007 + @brief This is the interface for a simple space invaders game. +*/ + +#ifndef SPACEY_H +#define SPACEY_H + +#include "system.h" + + +enum {SPACEY_ALIENS_MAX = 4, SPACEY_SHELLS_MAX = 4}; + + +typedef enum {SPACEY_PIX_OFF, SPACEY_PIX_GUN, SPACEY_PIX_ALIEN, + SPACEY_PIX_SHELL, SPACEY_PIX_TYPE_NUM} spacey_pix_t; + +typedef enum {SPACEY_EVENT_ALIEN_HIT, SPACEY_EVENT_ALIEN_LANDED} spacey_event_t; + + +typedef enum {SPACEY_ALIEN, SPACEY_SHELL} spacey_type_t; + +/* Positions must be signed. */ +typedef struct +{ + int8_t x; + int8_t y; +} spacey_pos_t; + + +typedef struct +{ + spacey_pos_t pos; + bool active; +} spacey_obj_t; + + +typedef struct +{ + spacey_obj_t array[SPACEY_ALIENS_MAX]; + uint8_t num; + uint16_t move_period; + uint16_t move_clock; +} spacey_objs_t; + + +typedef struct +{ + uint8_t aliens_killed; + uint8_t aliens_landed; + uint8_t shells_fired; + uint8_t aliens_live; +} spacey_stats_t; + + +typedef struct +{ + int aliens_wrap:1; + int aliens_evasive:1; +} spacey_options_t; + + +typedef struct +{ + /* If we have lots of objects then a linked list would be more + efficient for the aliens and shells. */ + spacey_objs_t aliens; + spacey_objs_t shells; + spacey_pos_t gun; + spacey_pos_t size; + spacey_stats_t stats; + uint16_t poll_rate; /* Hz */ + uint8_t alien_create_steps; + uint8_t lives; + void (*display_hook) (void *data, uint8_t row, uint8_t col, spacey_pix_t type); + void *display_data; + void (*event_hook) (void *data, spacey_event_t event); + void *event_data; + spacey_options_t options; + bool active; +} spacey_t; + + +void +spacey_event_handler_set (void (*event_handler) (void *, spacey_event_t), + void *event_data); + +void +spacey_init (uint16_t poll_rate, + uint8_t x_size, uint8_t y_size, + void (*display_handler) (void *data, uint8_t row, uint8_t col, + spacey_pix_t type), + void *display_data); + +/** Start a new game. */ +void +spacey_start (void); + +/** Update state of game. */ +bool +spacey_update (void); + +/** Specify number of lives we have before we die. */ +void +spacey_lives_set (uint8_t lives); + +/** Set alien speed in Hz. */ +void +spacey_alien_speed_set (uint8_t alien_speed); + +/** Set shell speed in Hz. */ +void +spacey_shell_speed_set (uint8_t shell_speed); + +/** Set number of steps that a new alien is created on average. */ +void +spacey_alien_create_steps_set (uint8_t alien_create_steps); + +/** Set maximum number of aliens on display at once. */ +void +spacey_alien_num_set (uint8_t aliens_num); + +/** Set maximum number of shells on display at once. */ +void +spacey_shell_num_set (uint8_t shells_num); + +/** Set game options. */ +void +spacey_options_set (spacey_options_t options); + +/** Move gun to right. */ +void +spacey_gun_move_right (void); + +/** Move gun to left. */ +void +spacey_gun_move_left (void); + +/** Fire gun. */ +void +spacey_gun_fire (void); + +/** Return number of aliens killed in current game. */ +uint8_t +spacey_aliens_killed_get (void); + +/** Return number of shells fired in current game. */ +uint8_t +spacey_shells_fired_get (void); + +#endif diff --git a/apps/space10/ticker.c b/apps/space10/ticker.c new file mode 100644 index 0000000..25c741e --- /dev/null +++ b/apps/space10/ticker.c @@ -0,0 +1,6 @@ +/** @file ticker.c + @author M. P. Hayes, UCECE + @date 2 April 2007 + @brief +*/ + diff --git a/apps/space10/ticker.h b/apps/space10/ticker.h new file mode 100644 index 0000000..ac86df5 --- /dev/null +++ b/apps/space10/ticker.h @@ -0,0 +1,45 @@ +/** @file ticker.h + @author M. P. Hayes, UCECE + @date 2 April 2007 + @brief +*/ +#ifndef TICKER_H +#define TICKER_H + +#include + +typedef struct +{ + uint16_t period; + uint16_t clock; +} ticker_t; + + +typedef struct +{ + uint16_t period; + uint16_t clock; +} ticker16_t; + + +typedef struct +{ + uint8_t period; + uint8_t clock; +} ticker8_t; + + +#define TICKER_INIT(DEV, PERIOD) \ + (DEV)->period = (PERIOD); \ + (DEV)->clock = (DEV)->period; + + +/* Return non-zero when the ticker rolls over otherwise return 0. */ +#define TICKER_UPDATE(DEV) \ + (--(DEV)->clock ? 0 : ((DEV)->clock = (DEV)->period)) + + +#define TICKER_START(DEV) \ + (DEV)->clock = (DEV)->period; + +#endif diff --git a/apps/space11/Makefile b/apps/space11/Makefile new file mode 100644 index 0000000..956d66a --- /dev/null +++ b/apps/space11/Makefile @@ -0,0 +1,81 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for space11 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: space11.out + + +# Compile: create object files from C source files. +space11.o: space11.c ../../drivers/avr/eeprom.h ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../drivers/display.h ../../drivers/led.h ../../drivers/navswitch.h ../../fonts/font3x5_1.h ../../utils/font.h ../../utils/task.h ../../utils/tinygl.h ../../utils/uint8toa.h flasher.h spacey.h + $(CC) -c $(CFLAGS) $< -o $@ + +flasher.o: flasher.c ../../drivers/avr/system.h flasher.h + $(CC) -c $(CFLAGS) $< -o $@ + +spacey.o: spacey.c ../../drivers/avr/system.h spacey.h + $(CC) -c $(CFLAGS) $< -o $@ + +eeprom.o: ../../drivers/avr/eeprom.c ../../drivers/avr/eeprom.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +task.o: ../../utils/task.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/task.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +uint8toa.o: ../../utils/uint8toa.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +space11.out: display.o eeprom.o flasher.o led.o ledmat.o navswitch.o pio.o space11.o spacey.o system.o task.o timer.o tinygl.o uint8toa.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: space11.out + $(OBJCOPY) -O ihex space11.out space11.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash space11.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/space11/Makefile.test b/apps/space11/Makefile.test new file mode 100644 index 0000000..a447dcf --- /dev/null +++ b/apps/space11/Makefile.test @@ -0,0 +1,75 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for space11 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: space11 + + +# Compile: create object files from C source files. +space11-test.o: space11.c ../../drivers/navswitch.h ../../utils/task.h ../../drivers/test/system.h ../../drivers/test/eeprom.h ../../fonts/font3x5_1.h ../../drivers/led.h ../../drivers/test/timer.h ../../utils/font.h ../../utils/tinygl.h spacey.h flasher.h ../../drivers/display.h ../../utils/uint8toa.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +task-test.o: ../../utils/task.c ../../drivers/test/timer.h ../../utils/task.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +uint8toa-test.o: ../../utils/uint8toa.c ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +spacey-test.o: spacey.c spacey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +eeprom-test.o: ../../drivers/test/eeprom.c ../../drivers/test/eeprom.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +flasher-test.o: flasher.c flasher.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +space11: space11-test.o timer-test.o led-test.o display-test.o pio-test.o task-test.o tinygl-test.o uint8toa-test.o ledmat-test.o spacey-test.o eeprom-test.o system-test.o mgetkey-test.o navswitch-test.o flasher-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) space11 space11-test.o timer-test.o led-test.o display-test.o pio-test.o task-test.o tinygl-test.o uint8toa-test.o ledmat-test.o spacey-test.o eeprom-test.o system-test.o mgetkey-test.o navswitch-test.o flasher-test.o + + diff --git a/apps/space11/flasher.c b/apps/space11/flasher.c new file mode 100644 index 0000000..2cdebd4 --- /dev/null +++ b/apps/space11/flasher.c @@ -0,0 +1,100 @@ +/** @file flasher.c + @author M. P. Hayes, UCECE + @date 13 March 2005 + @brief +*/ + +#include "flasher.h" + + +/* These routines are for flashing a LED or beeping a piezo tweeter. + Perhaps they should be separated into software PWM and flash + pattern sequencing. */ + +int8_t +flasher_pattern_set (flasher_t flasher, flasher_pattern_t *pattern) +{ + flasher->pattern = pattern; + flasher->mod_count = 0; + flasher->flasher_count = 0; + flasher->flashes_count = 0; + flasher->flasher_prescale = 0; + return 1; +} + + +flasher_pattern_t * +flasher_pattern_get (flasher_t flasher) +{ + return flasher->pattern; +} + + +/* FIXME. */ +int8_t +flasher_phase_set (flasher_t flasher, uint8_t phase) +{ + flasher->mod_count = 0; + flasher->flasher_count = 0; + flasher->flashes_count = phase; + return 1; +} + + +/* Return the next state for the associated device. For example, + to control the flashing of a LED use: + + led_set (led, flasher_update (flasher)); */ +bool +flasher_update (flasher_t flasher) +{ + if (!flasher->pattern) + return 0; + + flasher->mod_count++; + if (flasher->mod_count >= flasher->pattern->mod_period) + { + flasher->mod_count = 0; + flasher->flasher_prescale++; + + if (flasher->flasher_prescale >= FLASHER_PRESCALE) + { + flasher->flasher_prescale = 0; + flasher->flasher_count++; + + if (flasher->flasher_count >= flasher->pattern->flasher_period) + { + flasher->flasher_count = 0; + flasher->flashes_count++; + + if (!flasher->pattern->period) + { + /* One shot mode. */ + if (flasher->flashes_count >= flasher->pattern->flashes) + { + /* Disable pattern. */ + flasher->pattern = 0; + return 1; + } + } + else if (flasher->flashes_count >= flasher->pattern->period) + { + flasher->flashes_count = 0; + } + } + } + } + + return flasher->mod_count < flasher->pattern->mod_duty + && flasher->flasher_count < flasher->pattern->flasher_duty + && flasher->flashes_count < flasher->pattern->flashes; +} + + +/* Create a new flasher device. */ +flasher_t +flasher_init (flasher_obj_t *flasher) +{ + flasher_pattern_set (flasher, 0); + return flasher; +} diff --git a/apps/space11/flasher.h b/apps/space11/flasher.h new file mode 100644 index 0000000..4cfd6f8 --- /dev/null +++ b/apps/space11/flasher.h @@ -0,0 +1,97 @@ +/** @file flasher.h + @author M. P. Hayes, UCECE + @date 13 March 2005 + @brief Combined software PWM and flashing module. Use at your peril! +*/ +#ifndef FLASHER_H +#define FLASHER_H + +#include "system.h" + +/* This parameter is for internal use only. It's purpose is to + reduce the chances of flasher_period and flasher_duty overflow. + It could possibly be made an additional parameter for flasher_pattern_t + and calculated by FLASHER_PATTERN. */ +#define FLASHER_PRESCALE 8 + +/* POLL_RATE (Hz) + MOD_FREQ (Hz) + MOD_DUTY (percent) + FLASHER_PERIOD (s) - period between flashes + FLASHER_DUTY (percent) - proportion of flash period that is lit + FLASHES (integer) - how many flashes per flash pattern + PERIOD (s) - how often the flash pattern repeats +*/ + +#define FLASHER_PATTERN(POLL_RATE, MOD_FREQ, MOD_DUTY, FLASHER_PERIOD, \ + FLASHER_DUTY, FLASHES, PERIOD) \ + (POLL_RATE) / (double)(MOD_FREQ) + 0.5, \ + (POLL_RATE) * (double)(MOD_DUTY) / (MOD_FREQ) / 100.0 + 0.5, \ + (MOD_FREQ) * (FLASHER_PERIOD) / (double)FLASHER_PRESCALE + 0.5, \ + (MOD_FREQ) * (FLASHER_PERIOD) * (FLASHER_DUTY) / 100.0 / FLASHER_PRESCALE + 0.5, \ + (FLASHES), \ + (PERIOD) / (double)(FLASHER_PERIOD) + 0.5 + + +typedef struct +{ + /* This is the modulation period. It determines the frequency + of a tone or flicker rate of a LED. */ + uint8_t mod_period; + /* This is the modulation duty. It determines the luminance of a LED. */ + uint8_t mod_duty; + /* This is the period between the start of two flashes in a sequence. */ + uint8_t flasher_period; + /* This is the flash period. */ + uint8_t flasher_duty; + /* This is the number of flashes in the sequence. */ + uint8_t flashes; + /* This is the number of flasher periods before the sequence repeats. */ + uint8_t period; +} flasher_pattern_t; + + +#define FLASHER_ACTIVE_P(FLASHER) \ + (((flasher_obj_t *)(FLASHER))->pattern != 0) + + +#define FLASHER_PATTERN_FLASHES_SET(PATTERN, FLASHES) \ + (PATTERN)->flashes = (FLASHES) + +/* This structure is defined here so the compiler can allocate enough + memory for it. However, its fields should be treated as + private. */ +typedef struct +{ + flasher_pattern_t *pattern; + uint8_t mod_count; + uint8_t flasher_count; + uint8_t flashes_count; + uint8_t flasher_prescale; +} flasher_private_t; + + +typedef flasher_private_t flasher_obj_t; +typedef flasher_obj_t *flasher_t; + + +extern int8_t +flasher_pattern_set (flasher_t flasher, flasher_pattern_t *pattern); + +extern flasher_pattern_t * +flasher_pattern_get (flasher_t flasher); + +extern int8_t +flasher_phase_set (flasher_t flasher, uint8_t phase); + +extern bool +flasher_update (flasher_t); + + +/* INFO is a pointer into RAM that stores the state of the FLASH. + CFG is a pointer into ROM to define the port the FLASH is connected to. + The returned handle is passed to the other flasher_xxx routines to denote + the FLASH to operate on. */ +extern flasher_t +flasher_init (flasher_obj_t *info); +#endif diff --git a/apps/space11/space11.c b/apps/space11/space11.c new file mode 100644 index 0000000..85eb6d0 --- /dev/null +++ b/apps/space11/space11.c @@ -0,0 +1,459 @@ +/** @file space11.c + @author M. P. Hayes, UCECE + @date 20 April 2007 + @brief A simple space invaders game with different difficulty levels. + + @defgroup space11 A simple space invaders game. +*/ + +#include +#include "system.h" +#include "display.h" +#include "tinygl.h" +#include "task.h" +#include "navswitch.h" +#include "led.h" +#include "flasher.h" +#include "spacey.h" +#include "eeprom.h" +#include "uint8toa.h" +#include "../fonts/font3x5_1.h" + + +#define VERSION "1.6" + +/** Define polling rates in Hz. */ +enum {DISPLAY_UPDATE_RATE = 500}; +enum {FLASHER_UPDATE_RATE = 500}; +enum {BUTTON_POLL_RATE = 100}; +enum {GAME_UPDATE_RATE = 100}; +enum {GAME_OVER_PERIOD = 2}; +enum {BUTTON_HOLD_PERIOD = 1}; + + +/** Define flasher modes. */ +static flasher_pattern_t flasher_patterns[] = +{ + /** POLL_RATE, MOD_FREQ, MOD_DUTY, FLASHER_PERIOD, + FLASHER_DUTY, FLASHES, PERIOD. */ + {FLASHER_PATTERN (FLASHER_UPDATE_RATE, 100, 100, 0.4, 100, 1, 0.4)}, + {FLASHER_PATTERN (FLASHER_UPDATE_RATE, 100, 100, 0.4, 100, 1, 0.4)}, + {FLASHER_PATTERN (FLASHER_UPDATE_RATE, 200, 100, 0.1, 50, 1, 0.1)}, +}; + +typedef enum {FLASH_MODE_GUN, FLASH_MODE_SHELL, + FLASH_MODE_ALIEN, FLASH_MODE_NUM} flash_mode_t; + + +typedef enum {STATE_DUMMY, STATE_INIT, STATE_START, + STATE_PLAYING, STATE_OVER, + STATE_READY, STATE_MENU_LEVEL} state_t; + +enum {GAME_LEVEL_MAX = 5}; + + +typedef struct +{ + uint8_t level; + uint8_t games; +} game_data_t; + + +static state_t state = STATE_INIT; +static game_data_t game_data; +static uint8_t display[TINYGL_WIDTH * TINYGL_HEIGHT]; + + +/** Draw pixel on display. */ +static void +display_handler (void *data, uint8_t col, uint8_t row, spacey_pix_t type) +{ + uint8_t *display = data; + uint8_t pixel; + + pixel = row * TINYGL_WIDTH + col; + display[pixel] = type; +} + + +/** Display the game over message. */ +static void +game_over_display (char *buffer) +{ + char *str = buffer; + + strcpy (str, "GAME OVER "); + while (*str) + str++; + uint8toa (spacey_aliens_killed_get (), str, 0); + while (*str) + str++; + *str++ = '/'; + uint8toa (spacey_shells_fired_get (), str, 0); + tinygl_clear (); + tinygl_text (buffer); +} + + +static void +game_text_display (uint8_t num, char *buffer, char *msg) +{ + char *str = buffer; + + while (*msg) + *str++ = *msg++; + uint8toa (num, str, 0); + tinygl_clear (); + tinygl_text (buffer); +} + + +/** Display the game level of difficulty. */ +static void +game_level_display (uint8_t level, char *buffer) +{ + game_text_display (level, buffer, "L"); +} + + +/** Set the game level of difficulty. */ +static void +game_level_set (uint8_t level) +{ + spacey_options_t options; + uint8_t alien_num = 3; + uint8_t alien_steps = 4; + + switch (level) + { + default: + case 0: + options.aliens_wrap = 0; + options.aliens_evasive = 0; + break; + + case 1: + options.aliens_wrap = 0; + options.aliens_evasive = 1; + alien_steps = 3; + break; + + case 2: + options.aliens_wrap = 1; + options.aliens_evasive = 0; + alien_steps = 3; + break; + + case 3: + options.aliens_wrap = 1; + options.aliens_evasive = 1; + alien_steps = 3; + break; + + case 4: + options.aliens_wrap = 1; + options.aliens_evasive = 1; + alien_steps = 3; + alien_num = 4; + break; + + case 5: + options.aliens_wrap = 1; + options.aliens_evasive = 1; + alien_steps = 2; + alien_num = 4; + break; + } + + spacey_alien_num_set (alien_num); + spacey_alien_create_steps_set (alien_steps); + spacey_options_set (options); +} + + +static void +game_start (game_data_t *data) +{ + tinygl_clear (); + data->games++; + game_level_set (data->level); + spacey_start (); + eeprom_write (0, data, sizeof (*data)); +} + + +static void game_event (__unused__ void *data, spacey_event_t event) +{ + switch (event) + { + case SPACEY_EVENT_ALIEN_HIT: + break; + + case SPACEY_EVENT_ALIEN_LANDED: + break; + } +} + + + +static void game_task (__unused__ void *data) +{ + static bool init = 0; + static uint8_t game_over_ticks; + char message[44]; + + if (!init) + { + led_init (); + + led_set (LED1, 1); + + /* When the EEPROM is erased all the bytes are 0xFF so set to + sensible defaults. */ + eeprom_read (0, &game_data, sizeof (game_data)); + if (game_data.level == 0xff) + { + game_data.level = 0; + game_data.games = 0; + } + + spacey_init (GAME_UPDATE_RATE, TINYGL_WIDTH, TINYGL_HEIGHT, + display_handler, display); + + spacey_event_handler_set (game_event, 0); + + init = 1; + } + + switch (state) + { + case STATE_PLAYING: + if (! spacey_update ()) + { + game_over_display (message); + game_over_ticks = 0; + led_set (LED1, 1); + state = STATE_OVER; + } + break; + + case STATE_INIT: + tinygl_text ("SPACEY READY V" VERSION " BY MPH "); + state = STATE_READY; + break; + + case STATE_OVER: + game_over_ticks ++; + if (game_over_ticks >= GAME_UPDATE_RATE * GAME_OVER_PERIOD) + state = STATE_READY; + /* Fall through. */ + + case STATE_READY: + case STATE_MENU_LEVEL: + default: + break; + + case STATE_START: + /* Turn that bloody blimey space invader off... */ + game_start (&game_data); + led_set (LED1, 0); + state = STATE_PLAYING; + break; + } +} + + + +static void display_task (__unused__ void *data) +{ + static bool init = 0; + static flasher_t flashers[SPACEY_PIX_TYPE_NUM]; + static uint8_t flasher_state[SPACEY_PIX_TYPE_NUM]; + static flasher_obj_t flashers_info[SPACEY_PIX_TYPE_NUM]; + + if (!init) + { + uint8_t i; + + tinygl_init (DISPLAY_UPDATE_RATE); + tinygl_font_set (&font3x5_1); + tinygl_text_mode_set (TINYGL_TEXT_MODE_SCROLL_LEFT); + tinygl_text_mode_set (TINYGL_TEXT_MODE_ROTATE_SCROLL_DOWN); + tinygl_text_speed_set (10); + + for (i = 0; i < ARRAY_SIZE (flashers); i++) + { + flashers[i] = flasher_init (&flashers_info[i]); + flasher_state[i] = 0; + } + + for (i = 0; i < ARRAY_SIZE (display); i++) + display[i] = 0; + + /* Set up flash patterns for different pixel types. */ + flasher_pattern_set (flashers[SPACEY_PIX_GUN], + &flasher_patterns[FLASH_MODE_GUN]); + flasher_pattern_set (flashers[SPACEY_PIX_SHELL], + &flasher_patterns[FLASH_MODE_SHELL]); + flasher_pattern_set (flashers[SPACEY_PIX_ALIEN], + &flasher_patterns[FLASH_MODE_ALIEN]); + + init = 1; + } + + if (state == STATE_PLAYING) + { + uint8_t *src; + uint8_t i; + uint8_t j; + + /* Update flasher states. NB, the first flasher is always off. */ + for (i = 1; i < ARRAY_SIZE (flashers); i++) + flasher_state[i] = flasher_update (flashers[i]); + + /* Update display. */ + src = display; + for (j = 0; j < TINYGL_HEIGHT; j++) + for (i = 0; i < TINYGL_WIDTH; i++) + { + tinygl_point_t point = {i, j}; + + tinygl_draw_point (point, flasher_state[*src++]); + } + } + + + /* Advance messages and refresh display. */ + tinygl_update (); +} + + + +static void navswitch_task (__unused__ void *data) +{ + static bool init = 0; + char message[44]; + + if (!init) + { + navswitch_init (); + init = 1; + } + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + { + switch (state) + { + case STATE_READY: + break; + + case STATE_MENU_LEVEL: + if (game_data.level < GAME_LEVEL_MAX - 1) + game_data.level++; + game_level_display (game_data.level, message); + break; + + case STATE_PLAYING: + spacey_gun_move_right (); + break; + + default: + break; + } + } + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + { + switch (state) + { + case STATE_READY: + break; + + case STATE_MENU_LEVEL: + if (game_data.level > 1) + game_data.level--; + game_level_display (game_data.level, message); + break; + + case STATE_PLAYING: + spacey_gun_move_left (); + break; + + default: + break; + } + } + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + { + switch (state) + { + case STATE_READY: + state = STATE_MENU_LEVEL; + game_level_display (game_data.level, message); + break; + + case STATE_MENU_LEVEL: + break; + + case STATE_PLAYING: + break; + + default: + break; + } + } + + if (navswitch_push_event_p (NAVSWITCH_PUSH)) + { + switch (state) + { + case STATE_READY: + state = STATE_START; + break; + + case STATE_PLAYING: + spacey_gun_fire (); + break; + + case STATE_MENU_LEVEL: + state = STATE_READY; + break; + + default: + break; + } + } +} + + +int +main (void) +{ + task_t tasks[] = + { + { + .func = display_task, + .period = TASK_RATE / DISPLAY_UPDATE_RATE, + .data = 0 + }, + { + .func = navswitch_task, + .period = TASK_RATE / BUTTON_POLL_RATE, + .data = 0 + }, + { + .func = game_task, + .period = TASK_RATE / GAME_UPDATE_RATE, + .data = 0 + }, + }; + + + system_init (); + + task_schedule (tasks, ARRAY_SIZE (tasks)); + return 0; +} diff --git a/apps/space11/spacey.c b/apps/space11/spacey.c new file mode 100644 index 0000000..cb96d91 --- /dev/null +++ b/apps/space11/spacey.c @@ -0,0 +1,578 @@ +/** @file spacey.c + @author M. P. Hayes, UCECE + @date 13 March 2007 + @brief This implements a simple space invaders game. +*/ + +#include +#include "spacey.h" + +/* This function implements a simple space invades program. Aliens + randomly appear on the top row of the display and work their way + randomly down the display. The game ends when 5 aliens have + reached the bottom of the display. To kill the aliens, shells can + be fired at them from a gun on the bottom of the display. The gun + can only move across the bottom and when it gets to the side it + wraps around to the other side. + + The aliens could move at different speeds but currently they are + set at the same speed to keep things simple. +*/ + + +#ifndef DISPLAY_INTERLACE +#define DISPLAY_INTERLACE 0 +#endif + + +#if DISPLAY_INTERLACE +/* With an interlaced display we do not have odd numbered rows and + columns. */ +enum {SPACEY_SHELL_INC = 2, SPACEY_GUN_INC = 2}; +#else +enum {SPACEY_SHELL_INC = 1, SPACEY_GUN_INC = 1}; +#endif + + +enum {SPACEY_LIVES_DEFAULT = 5}; +enum {SPACEY_SHELL_SPEED_DEFAULT = 16}; +enum {SPACEY_ALIEN_SPEED_DEFAULT = 2}; +enum {SPACEY_ALIEN_CREATE_STEPS_DEFAULT = 4}; +enum {SPACEY_ALIEN_NUM_DEFAULT = 3}; +enum {SPACEY_SHELL_NUM_DEFAULT = 2}; + + +#define min(x, y) ((x) < (y) ? (x) : (y)) + +static spacey_t spacey_data; +static spacey_t * const spacey = &spacey_data; + + +void +spacey_lives_set (uint8_t lives) +{ + spacey->lives = lives; +} + + +void +spacey_alien_speed_set (uint8_t alien_speed) +{ + spacey->aliens.move_period = spacey->poll_rate / alien_speed; +} + + +void +spacey_shell_speed_set (uint8_t shell_speed) +{ + spacey->shells.move_period = spacey->poll_rate / shell_speed; +} + + +void +spacey_alien_create_steps_set (uint8_t alien_create_steps) +{ + spacey->alien_create_steps = alien_create_steps; +} + + +void +spacey_alien_num_set (uint8_t aliens_num) +{ + spacey->aliens.num = min (aliens_num, SPACEY_ALIENS_MAX); +} + + +void +spacey_shell_num_set (uint8_t shells_num) +{ + spacey->shells.num = min (shells_num, SPACEY_SHELLS_MAX); +} + + +void +spacey_options_set (spacey_options_t options) +{ + spacey->options = options; +} + + +void +spacey_init (uint16_t poll_rate, + uint8_t x_size, uint8_t y_size, + void (*display_handler) (void *data, uint8_t row, uint8_t col, + spacey_pix_t type), + void *display_data) +{ + spacey->size.x = x_size; + spacey->size.y = y_size; + spacey->display_hook = display_handler; + spacey->display_data = display_data; + spacey->active = 0; + spacey->poll_rate = poll_rate; + spacey->event_hook = NULL; + + spacey->options.aliens_wrap = 0; + spacey->options.aliens_evasive = 0; + + spacey_alien_num_set (SPACEY_ALIEN_NUM_DEFAULT); + spacey_shell_num_set (SPACEY_SHELL_NUM_DEFAULT); + spacey_shell_speed_set (SPACEY_SHELL_SPEED_DEFAULT); + spacey_alien_speed_set (SPACEY_ALIEN_SPEED_DEFAULT); + spacey_alien_create_steps_set (SPACEY_ALIEN_CREATE_STEPS_DEFAULT); + spacey->lives = SPACEY_LIVES_DEFAULT; +} + + +void +spacey_event_handler_set (void (*event_handler) (void *, spacey_event_t), + void *event_data) +{ + spacey->event_hook = event_handler; + spacey->event_data = event_data; +} + + +static void +spacey_pixel_set (spacey_pos_t *pos, + spacey_pix_t type) +{ + spacey->display_hook (spacey->display_data, pos->x, pos->y, type); +} + + +/* Return if obj is at the same point as another alien. */ +static spacey_obj_t * +spacey_alien_conflict_p (spacey_obj_t *obj) +{ + int8_t i; + + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (!alien->active || alien == obj) + continue; + + if (alien->pos.x == obj->pos.x + && alien->pos.y == obj->pos.y) + return alien; + } + return 0; +} + + +/* Return if obj is at the same point as another shell. */ +static spacey_obj_t * +spacey_shell_conflict_p (spacey_obj_t *obj) +{ + int8_t i; + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + if (!shell->active || shell == obj) + continue; + + if (shell->pos.x == obj->pos.x + && shell->pos.y == obj->pos.y) + return shell; + } + return 0; +} + + +static void +spacey_gun_move (int8_t inc) +{ + int8_t gun_x; + + spacey_pixel_set (&spacey->gun, SPACEY_PIX_OFF); + + gun_x = spacey->gun.x + inc; + if (gun_x >= spacey->size.x) + gun_x = 0; + else if (gun_x < 0) + gun_x = spacey->size.x - 1; + spacey->gun.x = gun_x; + + spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN); +} + + +/* Return true if move off display. */ +static bool +spacey_shell_move (spacey_obj_t *shell) +{ + /* Erase the previous position of the shell except if still + at gun. */ + if (shell->pos.x != spacey->gun.x + || shell->pos.y != spacey->gun.y) + spacey_pixel_set (&shell->pos, SPACEY_PIX_OFF); + + /* Shells only go straight up. */ + shell->pos.y -= SPACEY_SHELL_INC; + + if (shell->pos.y < 0) + return 1; + + spacey_pixel_set (&shell->pos, SPACEY_PIX_SHELL); + return 0; +} + + +static void +spacey_alien_erase (spacey_obj_t *alien) +{ + spacey_pixel_set (&alien->pos, SPACEY_PIX_OFF); +} + + +/* Return true if move off display or get to the bottom. */ +static bool +spacey_alien_move (spacey_obj_t *alien) +{ + int8_t x_jump; + + /* Aliens move down randomly zig zagging. */ + alien->pos.y++; + + /* Randomly move left or right. */ + x_jump = 2 * (rand () & 1) - 1; + + /* Don't move into line of gun if being evasive. */ + if (spacey->options.aliens_evasive && + spacey->gun.x == alien->pos.x + x_jump) + x_jump = -x_jump; + + alien->pos.x += x_jump; + + /* Keep the alien within bounds. */ + if (alien->pos.x < 0 || alien->pos.x >= spacey->size.x) + { + if (spacey->options.aliens_wrap) + alien->pos.x = spacey->size.x - abs (alien->pos.x); + else + alien->pos.x -= 2 * x_jump; + } + + if (alien->pos.y < spacey->size.y) + spacey_pixel_set (&alien->pos, SPACEY_PIX_ALIEN); + + if (alien->pos.y >= (spacey->size.y - 1)) + return 1; + + return 0; +} + + +static void +spacey_alien_create (void) +{ + int8_t i; + + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (alien->active) + continue; + + /* Aliens start at the top. */ + alien->pos.y = 0; + + while (1) + { + /* Choose random x position. */ + alien->pos.x = rand () % spacey->size.x; +#if DISPLAY_INTERLACE + /* Ensure x position is even. */ + alien->pos.x &= ~1; +#endif + alien->active = 1; + + /* Ensure aliens start at different spots to be fair! */ + if (!spacey_alien_conflict_p (alien)) + break; + } + spacey->stats.aliens_live++; + spacey_pixel_set (&alien->pos, SPACEY_PIX_ALIEN); + return; + } + /* If we get to here then we already have too many aliens. */ +} + + +static void +spacey_shell_create (void) +{ + int8_t i; + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + if (shell->active) + continue; + + shell->pos = spacey->gun; + shell->active = 1; + spacey->stats.shells_fired++; + /* Don't turn shell on initially since it will erase the gun. */ + return; + } + /* If we get to here then we already have too many shells. */ +} + + +static void +spacey_alien_kill (spacey_obj_t *alien) +{ + alien->active = 0; + spacey->stats.aliens_live--; +} + + +static void +spacey_shell_kill (spacey_obj_t *shell) +{ + shell->active = 0; +} + + +static void +spacey_event (spacey_event_t event) +{ + if (spacey->event_hook) + spacey->event_hook (spacey->event_data, event); +} + + +static void +spacey_alien_hit (spacey_obj_t *alien, spacey_obj_t *shell) +{ + spacey_alien_kill (alien); + spacey->stats.aliens_killed++; + + spacey_shell_kill (shell); + spacey_pixel_set (&shell->pos, SPACEY_PIX_OFF); + + spacey_event (SPACEY_EVENT_ALIEN_HIT); +} + + +static void +spacey_alien_landed (void) +{ + spacey->stats.aliens_landed++; + if (spacey->stats.aliens_landed >= spacey->lives) + spacey->active = 0; + + spacey_event (SPACEY_EVENT_ALIEN_LANDED); +} + + +static void +spacey_shells_move (void) +{ + int8_t i; + + spacey->shells.move_clock++; + if (spacey->shells.move_clock < spacey->shells.move_period) + return; + spacey->shells.move_clock = 0; + + /* Shells move until they hit an alien or move off the display. */ + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + if (!shell->active) + continue; + + if (spacey_shell_move (shell)) + spacey_shell_kill (shell); + else + { + spacey_obj_t *alien; + + /* Check if we've hit an alien. */ + while ((alien = spacey_alien_conflict_p (shell))) + { + /* We've hit an alien so kill it and the shell. */ + spacey_alien_hit (alien, shell); + } + } + } +} + + +static void +spacey_aliens_move (void) +{ + int8_t i; + uint8_t steps; + + spacey->aliens.move_clock++; + if (spacey->aliens.move_clock < spacey->aliens.move_period) + return; + spacey->aliens.move_clock = 0; + + /* Erase all aliens before moving them. */ + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (!alien->active) + continue; + + spacey_alien_erase (alien); + } + + /* Now move and redraw aliens. */ + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (!alien->active) + continue; + + if (spacey_alien_move (alien)) + { + if (alien->pos.y == spacey->gun.y) + { + /* The alien has reached Earth. */ + spacey_alien_landed (); + } + else + { + /* The alien has moved off the display so disable it. */ + spacey_alien_kill (alien); + + /* Redraw the gun in case it got hit by the alien. */ + spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN); + } + } + else + { + spacey_obj_t *shell; + + while ((shell = spacey_shell_conflict_p (alien))) + { + /* The alien hit a shell so kill it and the shell. */ + spacey_alien_hit (alien, shell); + } + } + } + + /* Randomly create an alien. If there are no aliens create a new + one more rapidly. */ + if (!spacey->stats.aliens_live) + steps = 2; + else + steps = spacey->alien_create_steps; + + if ((rand () % steps) == 0) + spacey_alien_create (); +} + + +/* Update the state of the game. */ +bool +spacey_update (void) +{ + /* Allow playing with the gun even if game inactive. */ + spacey_shells_move (); + + if (!spacey->active) + return 0; + + spacey_aliens_move (); + return 1; +} + + +/* Move the gun position to the right wrapping back around on left. */ +void +spacey_gun_move_right (void) +{ + spacey_gun_move (SPACEY_GUN_INC); +} + + +/* Move the gun position to the left wrapping back around on right. */ +void +spacey_gun_move_left (void) +{ + spacey_gun_move (-SPACEY_GUN_INC); +} + + +/* Fire the gun. */ +void +spacey_gun_fire (void) +{ + spacey_shell_create (); +} + + +/* Start a new game. */ +void +spacey_start (void) +{ + int8_t i; + int8_t j; + + spacey->shells.move_clock = 0; + spacey->aliens.move_clock = 0; + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + shell->active = 0; + } + + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + alien->active = 0; + } + + /* Turn all pixels off. */ + for (i = 0; i < spacey->size.x; i++) + for (j = 0; j < spacey->size.y; j++) + { + spacey_pos_t pos = {i, j}; + + spacey_pixel_set (&pos, SPACEY_PIX_OFF); + } + + /* Display gun in centre of display. */ + spacey->gun.x = (spacey->size.x / 2) & ~1; + spacey->gun.y = spacey->size.y - 1; + spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN); + + spacey->stats.aliens_live = 0; + spacey->stats.aliens_killed = 0; + spacey->stats.aliens_landed = 0; + spacey->stats.shells_fired = 0; + spacey->active = 1; +} + + +uint8_t +spacey_aliens_killed_get (void) +{ + return spacey->stats.aliens_killed; +} + + +uint8_t +spacey_shells_fired_get (void) +{ + return spacey->stats.shells_fired; +} diff --git a/apps/space11/spacey.h b/apps/space11/spacey.h new file mode 100644 index 0000000..5b8b868 --- /dev/null +++ b/apps/space11/spacey.h @@ -0,0 +1,152 @@ +/** @file spacey.h + @author M. P. Hayes, UCECE + @date 13 March 2007 + @brief This is the interface for a simple space invaders game. +*/ + +#ifndef SPACEY_H +#define SPACEY_H + +#include "system.h" + + +enum {SPACEY_ALIENS_MAX = 4, SPACEY_SHELLS_MAX = 4}; + + +typedef enum {SPACEY_PIX_OFF, SPACEY_PIX_GUN, SPACEY_PIX_ALIEN, + SPACEY_PIX_SHELL, SPACEY_PIX_TYPE_NUM} spacey_pix_t; + +typedef enum {SPACEY_EVENT_ALIEN_HIT, SPACEY_EVENT_ALIEN_LANDED} spacey_event_t; + + +typedef enum {SPACEY_ALIEN, SPACEY_SHELL} spacey_type_t; + +/* Positions must be signed. */ +typedef struct +{ + int8_t x; + int8_t y; +} spacey_pos_t; + + +typedef struct +{ + spacey_pos_t pos; + bool active; +} spacey_obj_t; + + +typedef struct +{ + spacey_obj_t array[SPACEY_ALIENS_MAX]; + uint8_t num; + uint16_t move_period; + uint16_t move_clock; +} spacey_objs_t; + + +typedef struct +{ + uint8_t aliens_killed; + uint8_t aliens_landed; + uint8_t shells_fired; + uint8_t aliens_live; +} spacey_stats_t; + + +typedef struct +{ + int aliens_wrap:1; + int aliens_evasive:1; +} spacey_options_t; + + +typedef struct +{ + /* If we have lots of objects then a linked list would be more + efficient for the aliens and shells. */ + spacey_objs_t aliens; + spacey_objs_t shells; + spacey_pos_t gun; + spacey_pos_t size; + spacey_stats_t stats; + uint16_t poll_rate; /* Hz */ + uint8_t alien_create_steps; + uint8_t lives; + void (*display_hook) (void *data, uint8_t row, uint8_t col, spacey_pix_t type); + void *display_data; + void (*event_hook) (void *data, spacey_event_t event); + void *event_data; + spacey_options_t options; + bool active; +} spacey_t; + + +void +spacey_event_handler_set (void (*event_handler) (void *, spacey_event_t), + void *event_data); + +void +spacey_init (uint16_t poll_rate, + uint8_t x_size, uint8_t y_size, + void (*display_handler) (void *data, uint8_t row, uint8_t col, + spacey_pix_t type), + void *display_data); + +/** Start a new game. */ +void +spacey_start (void); + +/** Update state of game. */ +bool +spacey_update (void); + +/** Specify number of lives we have before we die. */ +void +spacey_lives_set (uint8_t lives); + +/** Set alien speed in Hz. */ +void +spacey_alien_speed_set (uint8_t alien_speed); + +/** Set shell speed in Hz. */ +void +spacey_shell_speed_set (uint8_t shell_speed); + +/** Set number of steps that a new alien is created on average. */ +void +spacey_alien_create_steps_set (uint8_t alien_create_steps); + +/** Set maximum number of aliens on display at once. */ +void +spacey_alien_num_set (uint8_t aliens_num); + +/** Set maximum number of shells on display at once. */ +void +spacey_shell_num_set (uint8_t shells_num); + +/** Set game options. */ +void +spacey_options_set (spacey_options_t options); + +/** Move gun to right. */ +void +spacey_gun_move_right (void); + +/** Move gun to left. */ +void +spacey_gun_move_left (void); + +/** Fire gun. */ +void +spacey_gun_fire (void); + +/** Return number of aliens killed in current game. */ +uint8_t +spacey_aliens_killed_get (void); + +/** Return number of shells fired in current game. */ +uint8_t +spacey_shells_fired_get (void); + +#endif diff --git a/apps/space11/ticker.c b/apps/space11/ticker.c new file mode 100644 index 0000000..25c741e --- /dev/null +++ b/apps/space11/ticker.c @@ -0,0 +1,6 @@ +/** @file ticker.c + @author M. P. Hayes, UCECE + @date 2 April 2007 + @brief +*/ + diff --git a/apps/space11/ticker.h b/apps/space11/ticker.h new file mode 100644 index 0000000..ac86df5 --- /dev/null +++ b/apps/space11/ticker.h @@ -0,0 +1,45 @@ +/** @file ticker.h + @author M. P. Hayes, UCECE + @date 2 April 2007 + @brief +*/ +#ifndef TICKER_H +#define TICKER_H + +#include + +typedef struct +{ + uint16_t period; + uint16_t clock; +} ticker_t; + + +typedef struct +{ + uint16_t period; + uint16_t clock; +} ticker16_t; + + +typedef struct +{ + uint8_t period; + uint8_t clock; +} ticker8_t; + + +#define TICKER_INIT(DEV, PERIOD) \ + (DEV)->period = (PERIOD); \ + (DEV)->clock = (DEV)->period; + + +/* Return non-zero when the ticker rolls over otherwise return 0. */ +#define TICKER_UPDATE(DEV) \ + (--(DEV)->clock ? 0 : ((DEV)->clock = (DEV)->period)) + + +#define TICKER_START(DEV) \ + (DEV)->clock = (DEV)->period; + +#endif diff --git a/apps/space9/Makefile b/apps/space9/Makefile new file mode 100644 index 0000000..7e9c3d6 --- /dev/null +++ b/apps/space9/Makefile @@ -0,0 +1,81 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for space9 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: space9.out + + +# Compile: create object files from C source files. +space9.o: space9.c ../../drivers/avr/eeprom.h ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/led.h ../../drivers/navswitch.h ../../fonts/font3x5_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h ../../utils/uint8toa.h flasher.h spacey.h + $(CC) -c $(CFLAGS) $< -o $@ + +flasher.o: flasher.c ../../drivers/avr/system.h flasher.h + $(CC) -c $(CFLAGS) $< -o $@ + +spacey.o: spacey.c ../../drivers/avr/system.h spacey.h + $(CC) -c $(CFLAGS) $< -o $@ + +eeprom.o: ../../drivers/avr/eeprom.c ../../drivers/avr/eeprom.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +uint8toa.o: ../../utils/uint8toa.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +space9.out: display.o eeprom.o flasher.o led.o ledmat.o navswitch.o pacer.o pio.o space9.o spacey.o system.o timer.o tinygl.o uint8toa.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: space9.out + $(OBJCOPY) -O ihex space9.out space9.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash space9.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/space9/Makefile.test b/apps/space9/Makefile.test new file mode 100644 index 0000000..5e81b90 --- /dev/null +++ b/apps/space9/Makefile.test @@ -0,0 +1,75 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for space9 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: space9 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +flasher-test.o: flasher.c flasher.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +uint8toa-test.o: ../../utils/uint8toa.c ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +spacey-test.o: spacey.c spacey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +space9-test.o: space9.c ../../drivers/navswitch.h ../../utils/font.h ../../drivers/test/system.h ../../drivers/test/eeprom.h ../../fonts/font3x5_1.h ../../drivers/led.h flasher.h ../../utils/tinygl.h spacey.h ../../utils/pacer.h ../../drivers/display.h ../../utils/uint8toa.h + $(CC) -c $(CFLAGS) $< -o $@ + +eeprom-test.o: ../../drivers/test/eeprom.c ../../drivers/test/eeprom.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +space9: timer-test.o led-test.o display-test.o pio-test.o flasher-test.o pacer-test.o tinygl-test.o uint8toa-test.o ledmat-test.o spacey-test.o space9-test.o eeprom-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) space9 timer-test.o led-test.o display-test.o pio-test.o flasher-test.o pacer-test.o tinygl-test.o uint8toa-test.o ledmat-test.o spacey-test.o space9-test.o eeprom-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/space9/flasher.c b/apps/space9/flasher.c new file mode 100644 index 0000000..2cdebd4 --- /dev/null +++ b/apps/space9/flasher.c @@ -0,0 +1,100 @@ +/** @file flasher.c + @author M. P. Hayes, UCECE + @date 13 March 2005 + @brief +*/ + +#include "flasher.h" + + +/* These routines are for flashing a LED or beeping a piezo tweeter. + Perhaps they should be separated into software PWM and flash + pattern sequencing. */ + +int8_t +flasher_pattern_set (flasher_t flasher, flasher_pattern_t *pattern) +{ + flasher->pattern = pattern; + flasher->mod_count = 0; + flasher->flasher_count = 0; + flasher->flashes_count = 0; + flasher->flasher_prescale = 0; + return 1; +} + + +flasher_pattern_t * +flasher_pattern_get (flasher_t flasher) +{ + return flasher->pattern; +} + + +/* FIXME. */ +int8_t +flasher_phase_set (flasher_t flasher, uint8_t phase) +{ + flasher->mod_count = 0; + flasher->flasher_count = 0; + flasher->flashes_count = phase; + return 1; +} + + +/* Return the next state for the associated device. For example, + to control the flashing of a LED use: + + led_set (led, flasher_update (flasher)); */ +bool +flasher_update (flasher_t flasher) +{ + if (!flasher->pattern) + return 0; + + flasher->mod_count++; + if (flasher->mod_count >= flasher->pattern->mod_period) + { + flasher->mod_count = 0; + flasher->flasher_prescale++; + + if (flasher->flasher_prescale >= FLASHER_PRESCALE) + { + flasher->flasher_prescale = 0; + flasher->flasher_count++; + + if (flasher->flasher_count >= flasher->pattern->flasher_period) + { + flasher->flasher_count = 0; + flasher->flashes_count++; + + if (!flasher->pattern->period) + { + /* One shot mode. */ + if (flasher->flashes_count >= flasher->pattern->flashes) + { + /* Disable pattern. */ + flasher->pattern = 0; + return 1; + } + } + else if (flasher->flashes_count >= flasher->pattern->period) + { + flasher->flashes_count = 0; + } + } + } + } + + return flasher->mod_count < flasher->pattern->mod_duty + && flasher->flasher_count < flasher->pattern->flasher_duty + && flasher->flashes_count < flasher->pattern->flashes; +} + + +/* Create a new flasher device. */ +flasher_t +flasher_init (flasher_obj_t *flasher) +{ + flasher_pattern_set (flasher, 0); + return flasher; +} diff --git a/apps/space9/flasher.h b/apps/space9/flasher.h new file mode 100644 index 0000000..4cfd6f8 --- /dev/null +++ b/apps/space9/flasher.h @@ -0,0 +1,97 @@ +/** @file flasher.h + @author M. P. Hayes, UCECE + @date 13 March 2005 + @brief Combined software PWM and flashing module. Use at your peril! +*/ +#ifndef FLASHER_H +#define FLASHER_H + +#include "system.h" + +/* This parameter is for internal use only. It's purpose is to + reduce the chances of flasher_period and flasher_duty overflow. + It could possibly be made an additional parameter for flasher_pattern_t + and calculated by FLASHER_PATTERN. */ +#define FLASHER_PRESCALE 8 + +/* POLL_RATE (Hz) + MOD_FREQ (Hz) + MOD_DUTY (percent) + FLASHER_PERIOD (s) - period between flashes + FLASHER_DUTY (percent) - proportion of flash period that is lit + FLASHES (integer) - how many flashes per flash pattern + PERIOD (s) - how often the flash pattern repeats +*/ + +#define FLASHER_PATTERN(POLL_RATE, MOD_FREQ, MOD_DUTY, FLASHER_PERIOD, \ + FLASHER_DUTY, FLASHES, PERIOD) \ + (POLL_RATE) / (double)(MOD_FREQ) + 0.5, \ + (POLL_RATE) * (double)(MOD_DUTY) / (MOD_FREQ) / 100.0 + 0.5, \ + (MOD_FREQ) * (FLASHER_PERIOD) / (double)FLASHER_PRESCALE + 0.5, \ + (MOD_FREQ) * (FLASHER_PERIOD) * (FLASHER_DUTY) / 100.0 / FLASHER_PRESCALE + 0.5, \ + (FLASHES), \ + (PERIOD) / (double)(FLASHER_PERIOD) + 0.5 + + +typedef struct +{ + /* This is the modulation period. It determines the frequency + of a tone or flicker rate of a LED. */ + uint8_t mod_period; + /* This is the modulation duty. It determines the luminance of a LED. */ + uint8_t mod_duty; + /* This is the period between the start of two flashes in a sequence. */ + uint8_t flasher_period; + /* This is the flash period. */ + uint8_t flasher_duty; + /* This is the number of flashes in the sequence. */ + uint8_t flashes; + /* This is the number of flasher periods before the sequence repeats. */ + uint8_t period; +} flasher_pattern_t; + + +#define FLASHER_ACTIVE_P(FLASHER) \ + (((flasher_obj_t *)(FLASHER))->pattern != 0) + + +#define FLASHER_PATTERN_FLASHES_SET(PATTERN, FLASHES) \ + (PATTERN)->flashes = (FLASHES) + +/* This structure is defined here so the compiler can allocate enough + memory for it. However, its fields should be treated as + private. */ +typedef struct +{ + flasher_pattern_t *pattern; + uint8_t mod_count; + uint8_t flasher_count; + uint8_t flashes_count; + uint8_t flasher_prescale; +} flasher_private_t; + + +typedef flasher_private_t flasher_obj_t; +typedef flasher_obj_t *flasher_t; + + +extern int8_t +flasher_pattern_set (flasher_t flasher, flasher_pattern_t *pattern); + +extern flasher_pattern_t * +flasher_pattern_get (flasher_t flasher); + +extern int8_t +flasher_phase_set (flasher_t flasher, uint8_t phase); + +extern bool +flasher_update (flasher_t); + + +/* INFO is a pointer into RAM that stores the state of the FLASH. + CFG is a pointer into ROM to define the port the FLASH is connected to. + The returned handle is passed to the other flasher_xxx routines to denote + the FLASH to operate on. */ +extern flasher_t +flasher_init (flasher_obj_t *info); +#endif diff --git a/apps/space9/space9.c b/apps/space9/space9.c new file mode 100644 index 0000000..a2f0f6b --- /dev/null +++ b/apps/space9/space9.c @@ -0,0 +1,432 @@ +/** @file space9.c + @author M. P. Hayes, UCECE + @date 20 April 2007 + @brief A simple space invaders game with different difficulty levels. + + @defgroup space9 A simple space invaders game. +*/ + +#include +#include "system.h" +#include "display.h" +#include "tinygl.h" +#include "pacer.h" +#include "navswitch.h" +#include "led.h" +#include "flasher.h" +#include "spacey.h" +#include "eeprom.h" +#include "uint8toa.h" +#include "../fonts/font3x5_1.h" + + +#define VERSION "1.6" + +/** Define polling rates in Hz. */ +enum {LOOP_RATE = 500}; +enum {FLASHER_UPDATE_RATE = LOOP_RATE}; +enum {BUTTON_POLL_RATE = 100}; +enum {GAME_UPDATE_RATE = 100}; +enum {GAME_OVER_PERIOD = 2}; +enum {BUTTON_HOLD_PERIOD = 1}; + + +/** Define flasher modes. */ +static flasher_pattern_t flasher_patterns[] = +{ + /** POLL_RATE, MOD_FREQ, MOD_DUTY, FLASHER_PERIOD, + FLASHER_DUTY, FLASHES, PERIOD. */ + {FLASHER_PATTERN (FLASHER_UPDATE_RATE, 100, 100, 0.4, 100, 1, 0.4)}, + {FLASHER_PATTERN (FLASHER_UPDATE_RATE, 100, 100, 0.4, 100, 1, 0.4)}, + {FLASHER_PATTERN (FLASHER_UPDATE_RATE, 200, 100, 0.1, 50, 1, 0.1)}, +}; + +typedef enum {FLASH_MODE_GUN, FLASH_MODE_SHELL, + FLASH_MODE_ALIEN, FLASH_MODE_NUM} flash_mode_t; + + +typedef enum {STATE_DUMMY, STATE_INIT, STATE_START, + STATE_PLAYING, STATE_OVER, + STATE_READY, STATE_MENU_LEVEL} state_t; + +enum {GAME_LEVEL_MAX = 5}; + + +typedef struct +{ + uint8_t level; + uint8_t games; +} game_data_t; + + + +/** Draw pixel on display. */ +static void +display_handler (void *data, uint8_t col, uint8_t row, spacey_pix_t type) +{ + uint8_t *display = data; + uint8_t pixel; + + pixel = row * TINYGL_WIDTH + col; + display[pixel] = type; +} + + +/** Display the game over message. */ +static void +game_over_display (char *buffer) +{ + char *str = buffer; + + strcpy (str, "GAME OVER "); + while (*str) + str++; + uint8toa (spacey_aliens_killed_get (), str, 0); + while (*str) + str++; + *str++ = '/'; + uint8toa (spacey_shells_fired_get (), str, 0); + tinygl_clear (); + tinygl_text (buffer); +} + + +static void +game_text_display (uint8_t num, char *buffer, char *msg) +{ + char *str = buffer; + + while (*msg) + *str++ = *msg++; + uint8toa (num, str, 0); + tinygl_clear (); + tinygl_text (buffer); +} + + +/** Display the game level of difficulty. */ +static void +game_level_display (uint8_t level, char *buffer) +{ + game_text_display (level, buffer, "L"); +} + + +/** Set the game level of difficulty. */ +static void +game_level_set (uint8_t level) +{ + spacey_options_t options; + uint8_t alien_num = 3; + uint8_t alien_steps = 4; + + switch (level) + { + default: + case 0: + options.aliens_wrap = 0; + options.aliens_evasive = 0; + break; + + case 1: + options.aliens_wrap = 0; + options.aliens_evasive = 1; + alien_steps = 3; + break; + + case 2: + options.aliens_wrap = 1; + options.aliens_evasive = 0; + alien_steps = 3; + break; + + case 3: + options.aliens_wrap = 1; + options.aliens_evasive = 1; + alien_steps = 3; + break; + + case 4: + options.aliens_wrap = 1; + options.aliens_evasive = 1; + alien_steps = 3; + alien_num = 4; + break; + + case 5: + options.aliens_wrap = 1; + options.aliens_evasive = 1; + alien_steps = 2; + alien_num = 4; + break; + } + + spacey_alien_num_set (alien_num); + spacey_alien_create_steps_set (alien_steps); + spacey_options_set (options); +} + + +static void +game_start (game_data_t *data) +{ + tinygl_clear (); + data->games++; + game_level_set (data->level); + spacey_start (); + eeprom_write (0, data, sizeof (*data)); +} + + +void game_event (__unused__ void *data, spacey_event_t event) +{ + switch (event) + { + case SPACEY_EVENT_ALIEN_HIT: + break; + + case SPACEY_EVENT_ALIEN_LANDED: + break; + } +} + + +int +main (void) +{ + uint8_t navswitch_ticks; + uint8_t game_ticks; + uint8_t game_over_ticks; + uint8_t navswitch_down_count; + state_t state = STATE_INIT; + flasher_t flashers[SPACEY_PIX_TYPE_NUM]; + uint8_t flasher_state[SPACEY_PIX_TYPE_NUM]; + flasher_obj_t flashers_info[SPACEY_PIX_TYPE_NUM]; + uint8_t display[TINYGL_WIDTH * TINYGL_HEIGHT]; + uint8_t i; + uint8_t j; + game_data_t data; + char message[44]; + + system_init (); + led_init (); + + led_set (LED1, 1); + + /* When the EEPROM is erased all the bytes are 0xFF so set to + sensible defaults. */ + eeprom_read (0, &data, sizeof (data)); + if (data.level == 0xff) + { + data.level = 0; + data.games = 0; + } + + for (i = 0; i < ARRAY_SIZE (flashers); i++) + { + flashers[i] = flasher_init (&flashers_info[i]); + flasher_state[i] = 0; + } + + for (i = 0; i < ARRAY_SIZE (display); i++) + display[i] = 0; + + /* Set up flash patterns for different pixel types. */ + flasher_pattern_set (flashers[SPACEY_PIX_GUN], + &flasher_patterns[FLASH_MODE_GUN]); + flasher_pattern_set (flashers[SPACEY_PIX_SHELL], + &flasher_patterns[FLASH_MODE_SHELL]); + flasher_pattern_set (flashers[SPACEY_PIX_ALIEN], + &flasher_patterns[FLASH_MODE_ALIEN]); + + tinygl_init (LOOP_RATE); + tinygl_font_set (&font3x5_1); + tinygl_text_mode_set (TINYGL_TEXT_MODE_SCROLL_LEFT); + tinygl_text_mode_set (TINYGL_TEXT_MODE_ROTATE_SCROLL_DOWN); + tinygl_text_speed_set (10); + + navswitch_init (); + + spacey_init (GAME_UPDATE_RATE, TINYGL_WIDTH, TINYGL_HEIGHT, + display_handler, display); + + spacey_event_handler_set (game_event, 0); + + navswitch_ticks = 0; + game_ticks = 0; + game_over_ticks = 0; + navswitch_down_count = 0; + + pacer_init (LOOP_RATE); + + while (1) + { + pacer_wait (); + + if (state == STATE_PLAYING) + { + uint8_t *src; + + /* Update flasher states. NB, the first flasher is always off. */ + for (i = 1; i < ARRAY_SIZE (flashers); i++) + flasher_state[i] = flasher_update (flashers[i]); + + /* Update display. */ + src = display; + for (j = 0; j < TINYGL_HEIGHT; j++) + for (i = 0; i < TINYGL_WIDTH; i++) + { + tinygl_point_t point = {i, j}; + + tinygl_draw_point (point, flasher_state[*src++]); + } + } + + /* Advance messages and refresh display. */ + tinygl_update (); + + game_ticks++; + if (game_ticks >= LOOP_RATE / GAME_UPDATE_RATE) + { + game_ticks = 0; + + switch (state) + { + case STATE_PLAYING: + if (! spacey_update ()) + { + game_over_display (message); + game_over_ticks = 0; + led_set (LED1, 1); + state = STATE_OVER; + } + break; + + case STATE_INIT: + tinygl_text ("SPACEY READY V" VERSION " BY MPH "); + state = STATE_READY; + break; + + case STATE_OVER: + game_over_ticks ++; + if (game_over_ticks >= GAME_UPDATE_RATE * GAME_OVER_PERIOD) + state = STATE_READY; + /* Fall through. */ + + case STATE_READY: + case STATE_MENU_LEVEL: + default: + break; + + case STATE_START: + /* Turn that bloody blimey space invader off... */ + game_start (&data); + led_set (LED1, 0); + state = STATE_PLAYING; + break; + } + } + + /* Poll navswitch. */ + navswitch_ticks++; + if (navswitch_ticks >= LOOP_RATE / BUTTON_POLL_RATE) + { + navswitch_ticks = 0; + + navswitch_update (); + + if (navswitch_down_p (NAVSWITCH_EAST)) + navswitch_down_count++; + else + navswitch_down_count = 0; + + if (navswitch_down_count >= BUTTON_POLL_RATE * BUTTON_HOLD_PERIOD) + state = STATE_INIT; + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + { + switch (state) + { + case STATE_READY: + break; + + case STATE_MENU_LEVEL: + if (data.level < GAME_LEVEL_MAX - 1) + data.level++; + game_level_display (data.level, message); + break; + + case STATE_PLAYING: + spacey_gun_move_right (); + break; + + default: + break; + } + } + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + { + switch (state) + { + case STATE_READY: + break; + + case STATE_MENU_LEVEL: + if (data.level > 1) + data.level--; + game_level_display (data.level, message); + break; + + case STATE_PLAYING: + spacey_gun_move_left (); + break; + + default: + break; + } + } + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + { + switch (state) + { + case STATE_READY: + state = STATE_MENU_LEVEL; + game_level_display (data.level, message); + break; + + case STATE_MENU_LEVEL: + break; + + case STATE_PLAYING: + break; + + default: + break; + } + } + + if (navswitch_push_event_p (NAVSWITCH_PUSH)) + { + switch (state) + { + case STATE_READY: + state = STATE_START; + break; + + case STATE_PLAYING: + spacey_gun_fire (); + break; + + case STATE_MENU_LEVEL: + state = STATE_READY; + break; + + default: + break; + } + } + } + } +} diff --git a/apps/space9/spacey.c b/apps/space9/spacey.c new file mode 100644 index 0000000..cb96d91 --- /dev/null +++ b/apps/space9/spacey.c @@ -0,0 +1,578 @@ +/** @file spacey.c + @author M. P. Hayes, UCECE + @date 13 March 2007 + @brief This implements a simple space invaders game. +*/ + +#include +#include "spacey.h" + +/* This function implements a simple space invades program. Aliens + randomly appear on the top row of the display and work their way + randomly down the display. The game ends when 5 aliens have + reached the bottom of the display. To kill the aliens, shells can + be fired at them from a gun on the bottom of the display. The gun + can only move across the bottom and when it gets to the side it + wraps around to the other side. + + The aliens could move at different speeds but currently they are + set at the same speed to keep things simple. +*/ + + +#ifndef DISPLAY_INTERLACE +#define DISPLAY_INTERLACE 0 +#endif + + +#if DISPLAY_INTERLACE +/* With an interlaced display we do not have odd numbered rows and + columns. */ +enum {SPACEY_SHELL_INC = 2, SPACEY_GUN_INC = 2}; +#else +enum {SPACEY_SHELL_INC = 1, SPACEY_GUN_INC = 1}; +#endif + + +enum {SPACEY_LIVES_DEFAULT = 5}; +enum {SPACEY_SHELL_SPEED_DEFAULT = 16}; +enum {SPACEY_ALIEN_SPEED_DEFAULT = 2}; +enum {SPACEY_ALIEN_CREATE_STEPS_DEFAULT = 4}; +enum {SPACEY_ALIEN_NUM_DEFAULT = 3}; +enum {SPACEY_SHELL_NUM_DEFAULT = 2}; + + +#define min(x, y) ((x) < (y) ? (x) : (y)) + +static spacey_t spacey_data; +static spacey_t * const spacey = &spacey_data; + + +void +spacey_lives_set (uint8_t lives) +{ + spacey->lives = lives; +} + + +void +spacey_alien_speed_set (uint8_t alien_speed) +{ + spacey->aliens.move_period = spacey->poll_rate / alien_speed; +} + + +void +spacey_shell_speed_set (uint8_t shell_speed) +{ + spacey->shells.move_period = spacey->poll_rate / shell_speed; +} + + +void +spacey_alien_create_steps_set (uint8_t alien_create_steps) +{ + spacey->alien_create_steps = alien_create_steps; +} + + +void +spacey_alien_num_set (uint8_t aliens_num) +{ + spacey->aliens.num = min (aliens_num, SPACEY_ALIENS_MAX); +} + + +void +spacey_shell_num_set (uint8_t shells_num) +{ + spacey->shells.num = min (shells_num, SPACEY_SHELLS_MAX); +} + + +void +spacey_options_set (spacey_options_t options) +{ + spacey->options = options; +} + + +void +spacey_init (uint16_t poll_rate, + uint8_t x_size, uint8_t y_size, + void (*display_handler) (void *data, uint8_t row, uint8_t col, + spacey_pix_t type), + void *display_data) +{ + spacey->size.x = x_size; + spacey->size.y = y_size; + spacey->display_hook = display_handler; + spacey->display_data = display_data; + spacey->active = 0; + spacey->poll_rate = poll_rate; + spacey->event_hook = NULL; + + spacey->options.aliens_wrap = 0; + spacey->options.aliens_evasive = 0; + + spacey_alien_num_set (SPACEY_ALIEN_NUM_DEFAULT); + spacey_shell_num_set (SPACEY_SHELL_NUM_DEFAULT); + spacey_shell_speed_set (SPACEY_SHELL_SPEED_DEFAULT); + spacey_alien_speed_set (SPACEY_ALIEN_SPEED_DEFAULT); + spacey_alien_create_steps_set (SPACEY_ALIEN_CREATE_STEPS_DEFAULT); + spacey->lives = SPACEY_LIVES_DEFAULT; +} + + +void +spacey_event_handler_set (void (*event_handler) (void *, spacey_event_t), + void *event_data) +{ + spacey->event_hook = event_handler; + spacey->event_data = event_data; +} + + +static void +spacey_pixel_set (spacey_pos_t *pos, + spacey_pix_t type) +{ + spacey->display_hook (spacey->display_data, pos->x, pos->y, type); +} + + +/* Return if obj is at the same point as another alien. */ +static spacey_obj_t * +spacey_alien_conflict_p (spacey_obj_t *obj) +{ + int8_t i; + + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (!alien->active || alien == obj) + continue; + + if (alien->pos.x == obj->pos.x + && alien->pos.y == obj->pos.y) + return alien; + } + return 0; +} + + +/* Return if obj is at the same point as another shell. */ +static spacey_obj_t * +spacey_shell_conflict_p (spacey_obj_t *obj) +{ + int8_t i; + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + if (!shell->active || shell == obj) + continue; + + if (shell->pos.x == obj->pos.x + && shell->pos.y == obj->pos.y) + return shell; + } + return 0; +} + + +static void +spacey_gun_move (int8_t inc) +{ + int8_t gun_x; + + spacey_pixel_set (&spacey->gun, SPACEY_PIX_OFF); + + gun_x = spacey->gun.x + inc; + if (gun_x >= spacey->size.x) + gun_x = 0; + else if (gun_x < 0) + gun_x = spacey->size.x - 1; + spacey->gun.x = gun_x; + + spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN); +} + + +/* Return true if move off display. */ +static bool +spacey_shell_move (spacey_obj_t *shell) +{ + /* Erase the previous position of the shell except if still + at gun. */ + if (shell->pos.x != spacey->gun.x + || shell->pos.y != spacey->gun.y) + spacey_pixel_set (&shell->pos, SPACEY_PIX_OFF); + + /* Shells only go straight up. */ + shell->pos.y -= SPACEY_SHELL_INC; + + if (shell->pos.y < 0) + return 1; + + spacey_pixel_set (&shell->pos, SPACEY_PIX_SHELL); + return 0; +} + + +static void +spacey_alien_erase (spacey_obj_t *alien) +{ + spacey_pixel_set (&alien->pos, SPACEY_PIX_OFF); +} + + +/* Return true if move off display or get to the bottom. */ +static bool +spacey_alien_move (spacey_obj_t *alien) +{ + int8_t x_jump; + + /* Aliens move down randomly zig zagging. */ + alien->pos.y++; + + /* Randomly move left or right. */ + x_jump = 2 * (rand () & 1) - 1; + + /* Don't move into line of gun if being evasive. */ + if (spacey->options.aliens_evasive && + spacey->gun.x == alien->pos.x + x_jump) + x_jump = -x_jump; + + alien->pos.x += x_jump; + + /* Keep the alien within bounds. */ + if (alien->pos.x < 0 || alien->pos.x >= spacey->size.x) + { + if (spacey->options.aliens_wrap) + alien->pos.x = spacey->size.x - abs (alien->pos.x); + else + alien->pos.x -= 2 * x_jump; + } + + if (alien->pos.y < spacey->size.y) + spacey_pixel_set (&alien->pos, SPACEY_PIX_ALIEN); + + if (alien->pos.y >= (spacey->size.y - 1)) + return 1; + + return 0; +} + + +static void +spacey_alien_create (void) +{ + int8_t i; + + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (alien->active) + continue; + + /* Aliens start at the top. */ + alien->pos.y = 0; + + while (1) + { + /* Choose random x position. */ + alien->pos.x = rand () % spacey->size.x; +#if DISPLAY_INTERLACE + /* Ensure x position is even. */ + alien->pos.x &= ~1; +#endif + alien->active = 1; + + /* Ensure aliens start at different spots to be fair! */ + if (!spacey_alien_conflict_p (alien)) + break; + } + spacey->stats.aliens_live++; + spacey_pixel_set (&alien->pos, SPACEY_PIX_ALIEN); + return; + } + /* If we get to here then we already have too many aliens. */ +} + + +static void +spacey_shell_create (void) +{ + int8_t i; + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + if (shell->active) + continue; + + shell->pos = spacey->gun; + shell->active = 1; + spacey->stats.shells_fired++; + /* Don't turn shell on initially since it will erase the gun. */ + return; + } + /* If we get to here then we already have too many shells. */ +} + + +static void +spacey_alien_kill (spacey_obj_t *alien) +{ + alien->active = 0; + spacey->stats.aliens_live--; +} + + +static void +spacey_shell_kill (spacey_obj_t *shell) +{ + shell->active = 0; +} + + +static void +spacey_event (spacey_event_t event) +{ + if (spacey->event_hook) + spacey->event_hook (spacey->event_data, event); +} + + +static void +spacey_alien_hit (spacey_obj_t *alien, spacey_obj_t *shell) +{ + spacey_alien_kill (alien); + spacey->stats.aliens_killed++; + + spacey_shell_kill (shell); + spacey_pixel_set (&shell->pos, SPACEY_PIX_OFF); + + spacey_event (SPACEY_EVENT_ALIEN_HIT); +} + + +static void +spacey_alien_landed (void) +{ + spacey->stats.aliens_landed++; + if (spacey->stats.aliens_landed >= spacey->lives) + spacey->active = 0; + + spacey_event (SPACEY_EVENT_ALIEN_LANDED); +} + + +static void +spacey_shells_move (void) +{ + int8_t i; + + spacey->shells.move_clock++; + if (spacey->shells.move_clock < spacey->shells.move_period) + return; + spacey->shells.move_clock = 0; + + /* Shells move until they hit an alien or move off the display. */ + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + if (!shell->active) + continue; + + if (spacey_shell_move (shell)) + spacey_shell_kill (shell); + else + { + spacey_obj_t *alien; + + /* Check if we've hit an alien. */ + while ((alien = spacey_alien_conflict_p (shell))) + { + /* We've hit an alien so kill it and the shell. */ + spacey_alien_hit (alien, shell); + } + } + } +} + + +static void +spacey_aliens_move (void) +{ + int8_t i; + uint8_t steps; + + spacey->aliens.move_clock++; + if (spacey->aliens.move_clock < spacey->aliens.move_period) + return; + spacey->aliens.move_clock = 0; + + /* Erase all aliens before moving them. */ + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (!alien->active) + continue; + + spacey_alien_erase (alien); + } + + /* Now move and redraw aliens. */ + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + if (!alien->active) + continue; + + if (spacey_alien_move (alien)) + { + if (alien->pos.y == spacey->gun.y) + { + /* The alien has reached Earth. */ + spacey_alien_landed (); + } + else + { + /* The alien has moved off the display so disable it. */ + spacey_alien_kill (alien); + + /* Redraw the gun in case it got hit by the alien. */ + spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN); + } + } + else + { + spacey_obj_t *shell; + + while ((shell = spacey_shell_conflict_p (alien))) + { + /* The alien hit a shell so kill it and the shell. */ + spacey_alien_hit (alien, shell); + } + } + } + + /* Randomly create an alien. If there are no aliens create a new + one more rapidly. */ + if (!spacey->stats.aliens_live) + steps = 2; + else + steps = spacey->alien_create_steps; + + if ((rand () % steps) == 0) + spacey_alien_create (); +} + + +/* Update the state of the game. */ +bool +spacey_update (void) +{ + /* Allow playing with the gun even if game inactive. */ + spacey_shells_move (); + + if (!spacey->active) + return 0; + + spacey_aliens_move (); + return 1; +} + + +/* Move the gun position to the right wrapping back around on left. */ +void +spacey_gun_move_right (void) +{ + spacey_gun_move (SPACEY_GUN_INC); +} + + +/* Move the gun position to the left wrapping back around on right. */ +void +spacey_gun_move_left (void) +{ + spacey_gun_move (-SPACEY_GUN_INC); +} + + +/* Fire the gun. */ +void +spacey_gun_fire (void) +{ + spacey_shell_create (); +} + + +/* Start a new game. */ +void +spacey_start (void) +{ + int8_t i; + int8_t j; + + spacey->shells.move_clock = 0; + spacey->aliens.move_clock = 0; + + for (i = 0; i < spacey->shells.num; i++) + { + spacey_obj_t *shell = &spacey->shells.array[i]; + + shell->active = 0; + } + + for (i = 0; i < spacey->aliens.num; i++) + { + spacey_obj_t *alien = &spacey->aliens.array[i]; + + alien->active = 0; + } + + /* Turn all pixels off. */ + for (i = 0; i < spacey->size.x; i++) + for (j = 0; j < spacey->size.y; j++) + { + spacey_pos_t pos = {i, j}; + + spacey_pixel_set (&pos, SPACEY_PIX_OFF); + } + + /* Display gun in centre of display. */ + spacey->gun.x = (spacey->size.x / 2) & ~1; + spacey->gun.y = spacey->size.y - 1; + spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN); + + spacey->stats.aliens_live = 0; + spacey->stats.aliens_killed = 0; + spacey->stats.aliens_landed = 0; + spacey->stats.shells_fired = 0; + spacey->active = 1; +} + + +uint8_t +spacey_aliens_killed_get (void) +{ + return spacey->stats.aliens_killed; +} + + +uint8_t +spacey_shells_fired_get (void) +{ + return spacey->stats.shells_fired; +} diff --git a/apps/space9/spacey.h b/apps/space9/spacey.h new file mode 100644 index 0000000..5b8b868 --- /dev/null +++ b/apps/space9/spacey.h @@ -0,0 +1,152 @@ +/** @file spacey.h + @author M. P. Hayes, UCECE + @date 13 March 2007 + @brief This is the interface for a simple space invaders game. +*/ + +#ifndef SPACEY_H +#define SPACEY_H + +#include "system.h" + + +enum {SPACEY_ALIENS_MAX = 4, SPACEY_SHELLS_MAX = 4}; + + +typedef enum {SPACEY_PIX_OFF, SPACEY_PIX_GUN, SPACEY_PIX_ALIEN, + SPACEY_PIX_SHELL, SPACEY_PIX_TYPE_NUM} spacey_pix_t; + +typedef enum {SPACEY_EVENT_ALIEN_HIT, SPACEY_EVENT_ALIEN_LANDED} spacey_event_t; + + +typedef enum {SPACEY_ALIEN, SPACEY_SHELL} spacey_type_t; + +/* Positions must be signed. */ +typedef struct +{ + int8_t x; + int8_t y; +} spacey_pos_t; + + +typedef struct +{ + spacey_pos_t pos; + bool active; +} spacey_obj_t; + + +typedef struct +{ + spacey_obj_t array[SPACEY_ALIENS_MAX]; + uint8_t num; + uint16_t move_period; + uint16_t move_clock; +} spacey_objs_t; + + +typedef struct +{ + uint8_t aliens_killed; + uint8_t aliens_landed; + uint8_t shells_fired; + uint8_t aliens_live; +} spacey_stats_t; + + +typedef struct +{ + int aliens_wrap:1; + int aliens_evasive:1; +} spacey_options_t; + + +typedef struct +{ + /* If we have lots of objects then a linked list would be more + efficient for the aliens and shells. */ + spacey_objs_t aliens; + spacey_objs_t shells; + spacey_pos_t gun; + spacey_pos_t size; + spacey_stats_t stats; + uint16_t poll_rate; /* Hz */ + uint8_t alien_create_steps; + uint8_t lives; + void (*display_hook) (void *data, uint8_t row, uint8_t col, spacey_pix_t type); + void *display_data; + void (*event_hook) (void *data, spacey_event_t event); + void *event_data; + spacey_options_t options; + bool active; +} spacey_t; + + +void +spacey_event_handler_set (void (*event_handler) (void *, spacey_event_t), + void *event_data); + +void +spacey_init (uint16_t poll_rate, + uint8_t x_size, uint8_t y_size, + void (*display_handler) (void *data, uint8_t row, uint8_t col, + spacey_pix_t type), + void *display_data); + +/** Start a new game. */ +void +spacey_start (void); + +/** Update state of game. */ +bool +spacey_update (void); + +/** Specify number of lives we have before we die. */ +void +spacey_lives_set (uint8_t lives); + +/** Set alien speed in Hz. */ +void +spacey_alien_speed_set (uint8_t alien_speed); + +/** Set shell speed in Hz. */ +void +spacey_shell_speed_set (uint8_t shell_speed); + +/** Set number of steps that a new alien is created on average. */ +void +spacey_alien_create_steps_set (uint8_t alien_create_steps); + +/** Set maximum number of aliens on display at once. */ +void +spacey_alien_num_set (uint8_t aliens_num); + +/** Set maximum number of shells on display at once. */ +void +spacey_shell_num_set (uint8_t shells_num); + +/** Set game options. */ +void +spacey_options_set (spacey_options_t options); + +/** Move gun to right. */ +void +spacey_gun_move_right (void); + +/** Move gun to left. */ +void +spacey_gun_move_left (void); + +/** Fire gun. */ +void +spacey_gun_fire (void); + +/** Return number of aliens killed in current game. */ +uint8_t +spacey_aliens_killed_get (void); + +/** Return number of shells fired in current game. */ +uint8_t +spacey_shells_fired_get (void); + +#endif diff --git a/apps/space9/ticker.c b/apps/space9/ticker.c new file mode 100644 index 0000000..25c741e --- /dev/null +++ b/apps/space9/ticker.c @@ -0,0 +1,6 @@ +/** @file ticker.c + @author M. P. Hayes, UCECE + @date 2 April 2007 + @brief +*/ + diff --git a/apps/space9/ticker.h b/apps/space9/ticker.h new file mode 100644 index 0000000..ac86df5 --- /dev/null +++ b/apps/space9/ticker.h @@ -0,0 +1,45 @@ +/** @file ticker.h + @author M. P. Hayes, UCECE + @date 2 April 2007 + @brief +*/ +#ifndef TICKER_H +#define TICKER_H + +#include + +typedef struct +{ + uint16_t period; + uint16_t clock; +} ticker_t; + + +typedef struct +{ + uint16_t period; + uint16_t clock; +} ticker16_t; + + +typedef struct +{ + uint8_t period; + uint8_t clock; +} ticker8_t; + + +#define TICKER_INIT(DEV, PERIOD) \ + (DEV)->period = (PERIOD); \ + (DEV)->clock = (DEV)->period; + + +/* Return non-zero when the ticker rolls over otherwise return 0. */ +#define TICKER_UPDATE(DEV) \ + (--(DEV)->clock ? 0 : ((DEV)->clock = (DEV)->period)) + + +#define TICKER_START(DEV) \ + (DEV)->clock = (DEV)->period; + +#endif diff --git a/apps/spacey_remote1/Makefile b/apps/spacey_remote1/Makefile new file mode 100644 index 0000000..7ef951f --- /dev/null +++ b/apps/spacey_remote1/Makefile @@ -0,0 +1,72 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for spacey_remote1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: spacey_remote1.out + + +# Compile: create object files from C source files. +spacey_remote1.o: spacey_remote1.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ir.h ../../drivers/ir_serial.h ../../drivers/navswitch.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir.o: ../../drivers/ir.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ir.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial.o: ../../drivers/ir_serial.c ../../drivers/avr/delay.h ../../drivers/avr/system.h ../../drivers/ir.h ../../drivers/ir_serial.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +spacey_remote1.out: display.o ir.o ir_serial.o ledmat.o navswitch.o pacer.o pio.o spacey_remote1.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: spacey_remote1.out + $(OBJCOPY) -O ihex spacey_remote1.out spacey_remote1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash spacey_remote1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/spacey_remote1/Makefile.test b/apps/spacey_remote1/Makefile.test new file mode 100644 index 0000000..c781bbf --- /dev/null +++ b/apps/spacey_remote1/Makefile.test @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for spacey_remote1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: spacey_remote1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +ir_serial-test.o: ../../drivers/ir_serial.c ../../drivers/ir.h ../../drivers/ir_serial.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +spacey_remote1-test.o: spacey_remote1.c ../../drivers/navswitch.h ../../drivers/ir_serial.h ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/ir.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +ir-test.o: ../../drivers/ir.c ../../drivers/test/pio.h ../../drivers/ir.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +spacey_remote1: timer-test.o display-test.o pio-test.o ir_serial-test.o pacer-test.o spacey_remote1-test.o ir-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) spacey_remote1 timer-test.o display-test.o pio-test.o ir_serial-test.o pacer-test.o spacey_remote1-test.o ir-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/spacey_remote1/spacey_remote1.c b/apps/spacey_remote1/spacey_remote1.c new file mode 100644 index 0000000..8e4f83a --- /dev/null +++ b/apps/spacey_remote1/spacey_remote1.c @@ -0,0 +1,92 @@ +/** @file spacey_remote1.c + @author M. P. Hayes, UCECE + @date 24 August 2009 + @brief Test program for IR serial communications. + + @defgroup spacey_remote1 Remote control for the @ref spacey10 "spacey10" application. +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "ir_serial.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + +typedef enum {SPACEY_EVENT_ALIEN_HIT, SPACEY_EVENT_ALIEN_LANDED} spacey_event_t; + + +int main (void) +{ + int tick = 0; + + system_init (); + tinygl_init (LOOP_RATE); + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + tinygl_text_mode_set (TINYGL_TEXT_MODE_STEP); + + navswitch_init (); + ir_serial_init (); + + pacer_init (LOOP_RATE); + + tinygl_text ("#"); + + /* Paced loop. */ + while (1) + { + int ret; + uint8_t data; + + /* Wait for next tick. */ + pacer_wait (); + + tinygl_update (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + ir_serial_transmit (1); + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + ir_serial_transmit (2); + + ret = ir_serial_receive (&data); + if (ret == 1) + { + spacey_event_t event = data; + + tick = 1; + + switch (event) + { + case SPACEY_EVENT_ALIEN_HIT: + tinygl_text ("*"); + break; + + case SPACEY_EVENT_ALIEN_LANDED: + tinygl_text ("@"); + break; + } + + } + + if (tick) + tick++; + if (tick > LOOP_RATE / 2) + { + tinygl_clear (); + tick = 0; + } + } + + return 0; +} diff --git a/apps/steer1/Makefile b/apps/steer1/Makefile new file mode 100644 index 0000000..b823e31 --- /dev/null +++ b/apps/steer1/Makefile @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for steer1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: steer1.out + + +# Compile: create object files from C source files. +steer1.o: steer1.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/navswitch.h ../../fonts/font3x5_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +steer1.out: display.o ledmat.o navswitch.o pacer.o pio.o steer1.o system.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: steer1.out + $(OBJCOPY) -O ihex steer1.out steer1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash steer1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/steer1/Makefile.test b/apps/steer1/Makefile.test new file mode 100644 index 0000000..dce8574 --- /dev/null +++ b/apps/steer1/Makefile.test @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for steer1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: steer1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +steer1-test.o: steer1.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../utils/font.h ../../drivers/test/system.h ../../fonts/font3x5_1.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/test/avrtest.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +steer1: timer-test.o display-test.o pio-test.o steer1-test.o pacer-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) steer1 timer-test.o display-test.o pio-test.o steer1-test.o pacer-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/steer1/steer1.c b/apps/steer1/steer1.c new file mode 100644 index 0000000..aff36af --- /dev/null +++ b/apps/steer1/steer1.c @@ -0,0 +1,118 @@ +/** @file steer1.c + @author M.P. Hayes + @date 28 Aug 2008 + + @defgroup steer1 Simple steering program using navswitchs +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "pio.h" +#include "../fonts/font3x5_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + +#define SIZE 35 + +static tinygl_point_t buffer[SIZE]; +static int in = 0; +static int out = 0; + + +void point_add (tinygl_point_t pos) +{ + int old = in; + + tinygl_draw_point (pos, 1); + buffer[in] = pos; + in++; + if (in >= SIZE) + in = 0; + if (in == out) + in = old; +} + + +void point_remove (void) +{ + if (in == out) + return; + + tinygl_draw_point (buffer[out], 0); + out++; + if (out >= SIZE) + out = 0; +} + + +int main (void) +{ + uint16_t tick = 0; + tinygl_point_t pos = tinygl_point (2, 3); + + system_init (); + pio_config_set (LED1_PIO, PIO_OUTPUT_HIGH); + + tinygl_init (LOOP_RATE); + tinygl_font_set (&font3x5_1); + + navswitch_init (); + + pacer_init (LOOP_RATE); + + point_add (pos); + + /* Paced loop. */ + while (1) + { + bool push = 0; + + /* Wait for next tick. */ + pacer_wait (); + + tick++; + if (tick > LOOP_RATE / 2) + { + tick = 0; + pio_output_toggle (LED1_PIO); + point_remove (); + } + + navswitch_update (); + + if (navswitch_push_event_p (2) && pos.x < TINYGL_WIDTH) + { + pos.x++; + push = 1; + } + if (navswitch_push_event_p (3) && pos.x > 0) + { + pos.x--; + push = 1; + } + if (navswitch_push_event_p (NAVSWITCH_EAST) && pos.y < TINYGL_HEIGHT) + { + pos.y++; + push = 1; + } + if (navswitch_push_event_p (4) && pos.y > 0) + { + pos.y--; + push = 1; + } + + if (push) + { + push = 0; + point_add (pos); + } + + tinygl_update (); + } + + return 0; +} diff --git a/apps/task1/Makefile b/apps/task1/Makefile new file mode 100644 index 0000000..efe6beb --- /dev/null +++ b/apps/task1/Makefile @@ -0,0 +1,57 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for task1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: task1.out + + +# Compile: create object files from C source files. +task1.o: task1.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../drivers/led.h ../../utils/task.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +task.o: ../../utils/task.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/task.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +task1.out: led.o pio.o system.o task.o task1.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: task1.out + $(OBJCOPY) -O ihex task1.out task1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash task1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/task1/Makefile.test b/apps/task1/Makefile.test new file mode 100644 index 0000000..93b26c1 --- /dev/null +++ b/apps/task1/Makefile.test @@ -0,0 +1,51 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for task1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: task1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +task-test.o: ../../utils/task.c ../../drivers/test/timer.h ../../utils/task.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +task1-test.o: task1.c ../../drivers/led.h ../../drivers/test/timer.h ../../utils/task.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +task1: timer-test.o led-test.o pio-test.o task-test.o task1-test.o system-test.o mgetkey-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) task1 timer-test.o led-test.o pio-test.o task-test.o task1-test.o system-test.o mgetkey-test.o + + diff --git a/apps/task1/task1.c b/apps/task1/task1.c new file mode 100644 index 0000000..2e9f455 --- /dev/null +++ b/apps/task1/task1.c @@ -0,0 +1,36 @@ +/** @file task1.c + @author M.P. Hayes + @date 5 Oct 2010 +*/ + +#include "system.h" +#include "led.h" +#include "task.h" + + +/* Define polling rate in Hz. */ +#define LED_TASK_RATE 2 + + +static void led_flash_task (__unused__ void *data) +{ + static uint8_t state = 0; + + led_set (LED1, state); + state = !state; +} + + +int main (void) +{ + task_t tasks[] = + { + {.func = led_flash_task, .period = TASK_RATE / LED_TASK_RATE, .data = 0}, + }; + + system_init (); + led_init (); + + task_schedule (tasks, 1); + return 0; +} diff --git a/apps/task2/Makefile b/apps/task2/Makefile new file mode 100644 index 0000000..ad075de --- /dev/null +++ b/apps/task2/Makefile @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for task2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: task2.out + + +# Compile: create object files from C source files. +task2.o: task2.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../drivers/display.h ../../drivers/led.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/task.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +task.o: ../../utils/task.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/task.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +task2.out: display.o led.o ledmat.o pio.o system.o task.o task2.o timer.o tinygl.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: task2.out + $(OBJCOPY) -O ihex task2.out task2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash task2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/task2/Makefile.test b/apps/task2/Makefile.test new file mode 100644 index 0000000..1a6d99a --- /dev/null +++ b/apps/task2/Makefile.test @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for task2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: task2 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +task-test.o: ../../utils/task.c ../../drivers/test/timer.h ../../utils/task.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +task2-test.o: task2.c ../../utils/task.h ../../drivers/test/system.h ../../drivers/test/timer.h ../../utils/font.h ../../drivers/led.h ../../utils/tinygl.h ../../drivers/display.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +task2: timer-test.o led-test.o display-test.o pio-test.o task-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o task2-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) task2 timer-test.o led-test.o display-test.o pio-test.o task-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o task2-test.o + + diff --git a/apps/task2/task2.c b/apps/task2/task2.c new file mode 100644 index 0000000..6de0409 --- /dev/null +++ b/apps/task2/task2.c @@ -0,0 +1,69 @@ +/** @file task2.c + @author M.P. Hayes + @date 5 Oct 2010 +*/ + +#include "system.h" +#include "led.h" +#include "task.h" +#include "tinygl.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LED_TASK_RATE 2 + +#define DISPLAY_TASK_RATE 400 + +/* Define text update rate (characters per 10 s). */ +#define MESSAGE_RATE 10 + + +static void led_flash_task_init (void) +{ + led_init (); +} + + +static void led_flash_task (__unused__ void *data) +{ + static uint8_t state = 0; + + led_set (LED1, state); + state = !state; +} + + +static void display_task_init (void) +{ + tinygl_init (DISPLAY_TASK_RATE); + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (MESSAGE_RATE); + tinygl_text_mode_set (TINYGL_TEXT_MODE_SCROLL_LEFT); + + tinygl_text ("HELLO WORLD "); +} + + +static void display_task (__unused__ void *data) +{ + tinygl_update (); +} + + +int main (void) +{ + task_t tasks[] = + { + {.func = display_task, .period = TASK_RATE / DISPLAY_TASK_RATE, .data = 0}, + {.func = led_flash_task, .period = TASK_RATE / LED_TASK_RATE, .data = 0}, + }; + + system_init (); + + led_flash_task_init (); + display_task_init (); + + task_schedule (tasks, 2); + return 0; +} diff --git a/apps/tdmdemo1/Makefile b/apps/tdmdemo1/Makefile new file mode 100644 index 0000000..da52441 --- /dev/null +++ b/apps/tdmdemo1/Makefile @@ -0,0 +1,57 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for tdmdemo1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: tdmdemo1.out + + +# Compile: create object files from C source files. +tdmdemo1.o: tdmdemo1.c ../../drivers/avr/system.h ../../drivers/ledmat.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +tdmdemo1.out: ledmat.o pacer.o pio.o system.o tdmdemo1.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: tdmdemo1.out + $(OBJCOPY) -O ihex tdmdemo1.out tdmdemo1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash tdmdemo1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/tdmdemo1/Makefile.test b/apps/tdmdemo1/Makefile.test new file mode 100644 index 0000000..e91d41e --- /dev/null +++ b/apps/tdmdemo1/Makefile.test @@ -0,0 +1,51 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for tdmdemo1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: tdmdemo1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +tdmdemo1-test.o: tdmdemo1.c ../../utils/pacer.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +tdmdemo1: timer-test.o pio-test.o pacer-test.o ledmat-test.o system-test.o mgetkey-test.o tdmdemo1-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) tdmdemo1 timer-test.o pio-test.o pacer-test.o ledmat-test.o system-test.o mgetkey-test.o tdmdemo1-test.o + + diff --git a/apps/tdmdemo1/tdmdemo1.c b/apps/tdmdemo1/tdmdemo1.c new file mode 100644 index 0000000..f17094a --- /dev/null +++ b/apps/tdmdemo1/tdmdemo1.c @@ -0,0 +1,80 @@ +/** @file tdmdemo1.c + @author M. P. Hayes, UCECE + @date 10 August 2011 + @brief Time division multiplexing of display demo + + @defgroup tdmdemo1 Time division multiplexing of display demo +*/ + +#include "system.h" +#include "pacer.h" +#include "ledmat.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 1000 + +/* Define display refresh rate in Hz. */ +#define REFRESH_RATE 1 + +#define UPDATE_PERIOD (LOOP_RATE / (LEDMAT_COLS_NUM * REFRESH_RATE)) + + +int main (void) +{ + uint8_t col = 0; + uint8_t tick = 0; + + system_init (); + ledmat_init (); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + tick++; + if (tick >= UPDATE_PERIOD) + { + tick = 0; + + /* Display an 'H' character by time division multiplexing + the columns of the LED matrix. */ + switch (col) + { + case 0: + ledmat_display_column (0x7f, 0); + break; + + case 1: + ledmat_display_column (0x08, 1); + break; + + case 2: + ledmat_display_column (0x08, 2); + break; + + case 3: + ledmat_display_column (0x08, 3); + break; + + case 4: + ledmat_display_column (0x7f, 4); + break; + + default: + break; + } + + col++; + if (col >= LEDMAT_COLS_NUM ) + col = 0; + } + + } + + return 0; +} diff --git a/apps/tdmdemo2/Makefile b/apps/tdmdemo2/Makefile new file mode 100644 index 0000000..1cff4f4 --- /dev/null +++ b/apps/tdmdemo2/Makefile @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for tdmdemo2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: tdmdemo2.out + + +# Compile: create object files from C source files. +tdmdemo2.o: tdmdemo2.c ../../drivers/avr/system.h ../../drivers/ledmat.h ../../drivers/navswitch.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +tdmdemo2.out: ledmat.o navswitch.o pacer.o pio.o system.o tdmdemo2.o timer.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: tdmdemo2.out + $(OBJCOPY) -O ihex tdmdemo2.out tdmdemo2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash tdmdemo2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/tdmdemo2/Makefile.test b/apps/tdmdemo2/Makefile.test new file mode 100644 index 0000000..ae549be --- /dev/null +++ b/apps/tdmdemo2/Makefile.test @@ -0,0 +1,54 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for tdmdemo2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: tdmdemo2 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tdmdemo2-test.o: tdmdemo2.c ../../utils/pacer.h ../../drivers/ledmat.h ../../drivers/navswitch.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +tdmdemo2: timer-test.o pio-test.o pacer-test.o ledmat-test.o system-test.o tdmdemo2-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) tdmdemo2 timer-test.o pio-test.o pacer-test.o ledmat-test.o system-test.o tdmdemo2-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/tdmdemo2/tdmdemo2.c b/apps/tdmdemo2/tdmdemo2.c new file mode 100644 index 0000000..774c8d2 --- /dev/null +++ b/apps/tdmdemo2/tdmdemo2.c @@ -0,0 +1,97 @@ +/** @file tdmdemo2.c + @author M. P. Hayes, UCECE + @date 10 August 2011 + @brief Time division multiplexing of display demo + + @defgroup tdmdemo2 Time division multiplexing of display demo +*/ + +#include "system.h" +#include "pacer.h" +#include "navswitch.h" +#include "ledmat.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 1000 + + +int main (void) +{ + uint8_t col = 0; + uint8_t tick = 0; + uint8_t refresh_rate = 1; + uint16_t update_period; + + system_init (); + ledmat_init (); + navswitch_init (); + + pacer_init (LOOP_RATE); + + update_period = LOOP_RATE / (LEDMAT_COLS_NUM * refresh_rate); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + { + refresh_rate++; + update_period = LOOP_RATE / (LEDMAT_COLS_NUM * refresh_rate); + } + + if (navswitch_push_event_p (NAVSWITCH_WEST) && refresh_rate > 0) + { + /* Allow refresh_rate to go to zero so can stop display + refreshing. */ + refresh_rate--; + update_period = LOOP_RATE / (LEDMAT_COLS_NUM * refresh_rate); + } + + tick++; + if (tick >= update_period) + { + tick = 0; + + /* Display an 'H' character by time division multiplexing + the columns of the LED matrix. */ + switch (col) + { + case 0: + ledmat_display_column (0x7f, 0); + break; + + case 1: + ledmat_display_column (0x08, 1); + break; + + case 2: + ledmat_display_column (0x08, 2); + break; + + case 3: + ledmat_display_column (0x08, 3); + break; + + case 4: + ledmat_display_column (0x7f, 4); + break; + + default: + break; + } + + col++; + if (col >= LEDMAT_COLS_NUM ) + col = 0; + } + + } + + return 0; +} diff --git a/apps/updown1/Makefile b/apps/updown1/Makefile new file mode 100644 index 0000000..8677813 --- /dev/null +++ b/apps/updown1/Makefile @@ -0,0 +1,63 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for updown1 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: updown1.out + + +# Compile: create object files from C source files. +updown1.o: updown1.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/display.h ../../fonts/font5x7_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +updown1.out: display.o ledmat.o pacer.o pio.o system.o timer.o tinygl.o updown1.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: updown1.out + $(OBJCOPY) -O ihex updown1.out updown1.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash updown1.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/updown1/Makefile.test b/apps/updown1/Makefile.test new file mode 100644 index 0000000..e92684d --- /dev/null +++ b/apps/updown1/Makefile.test @@ -0,0 +1,57 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for updown1 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: updown1 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +updown1-test.o: updown1.c ../../drivers/test/pio.h ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/test/avrtest.h ../../fonts/font5x7_1.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +updown1: timer-test.o updown1-test.o display-test.o pio-test.o pacer-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) updown1 timer-test.o updown1-test.o display-test.o pio-test.o pacer-test.o tinygl-test.o ledmat-test.o system-test.o mgetkey-test.o + + diff --git a/apps/updown1/updown1.c b/apps/updown1/updown1.c new file mode 100644 index 0000000..4ba362d --- /dev/null +++ b/apps/updown1/updown1.c @@ -0,0 +1,68 @@ +/** @file updown1.c + @author M.P. Hayes + @date 28 Aug 2008 + + @defgroup updown1 Simple counting program using button. +*/ + +#include "system.h" +#include "pacer.h" +#include "pio.h" +#include "tinygl.h" +#include "../fonts/font5x7_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + + +static void show_count (uint8_t count) +{ + char buffer[2]; + + /* FIXME! */ + buffer[0] = count + '0'; + buffer[1] = 0; + tinygl_text (buffer); +} + + +int main (void) +{ + bool state; + bool prev_state; + uint8_t count = 0; + + system_init (); + tinygl_init (LOOP_RATE); + + tinygl_font_set (&font5x7_1); + + pio_config_set (BUTTON1_PIO, PIO_INPUT); + + state = !pio_input_get (BUTTON1_PIO); + prev_state = state; + + show_count (count); + + pacer_init (LOOP_RATE); + + /* Paced loop. */ + while (1) + { + /* Wait for next tick. */ + pacer_wait (); + + state = pio_input_get (BUTTON1_PIO); + if (state && !prev_state) + { + count++; + show_count (count); + } + prev_state = state; + + tinygl_update (); + } + + return 0; +} diff --git a/apps/updown2/Makefile b/apps/updown2/Makefile new file mode 100644 index 0000000..92601e2 --- /dev/null +++ b/apps/updown2/Makefile @@ -0,0 +1,66 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for updown2 + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: updown2.out + + +# Compile: create object files from C source files. +updown2.o: updown2.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/navswitch.h ../../fonts/font3x5_1.h ../../utils/font.h ../../utils/pacer.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h + $(CC) -c $(CFLAGS) $< -o $@ + +display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h + $(CC) -c $(CFLAGS) $< -o $@ + +pacer.o: ../../utils/pacer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/pacer.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h + $(CC) -c $(CFLAGS) $< -o $@ + + + +# Link: create ELF output file from object files. +updown2.out: display.o ledmat.o navswitch.o pacer.o pio.o system.o timer.o tinygl.o updown2.o + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: updown2.out + $(OBJCOPY) -O ihex updown2.out updown2.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash updown2.hex; dfu-programmer atmega32u2 start + + diff --git a/apps/updown2/Makefile.test b/apps/updown2/Makefile.test new file mode 100644 index 0000000..380f272 --- /dev/null +++ b/apps/updown2/Makefile.test @@ -0,0 +1,60 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for updown2 + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test + +DEL = rm + + +# Default target. +all: updown2 + + +# Compile: create object files from C source files. +timer-test.o: ../../drivers/test/timer.c ../../drivers/test/timer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +display-test.o: ../../drivers/display.c ../../drivers/ledmat.h ../../drivers/display.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +updown2-test.o: updown2.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../utils/font.h ../../drivers/test/system.h ../../fonts/font3x5_1.h ../../utils/tinygl.h ../../utils/pacer.h ../../drivers/display.h ../../drivers/test/avrtest.h + $(CC) -c $(CFLAGS) $< -o $@ + +pio-test.o: ../../drivers/test/pio.c + $(CC) -c $(CFLAGS) $< -o $@ + +pacer-test.o: ../../utils/pacer.c ../../drivers/test/timer.h ../../utils/pacer.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +ledmat-test.o: ../../drivers/ledmat.c ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/ledmat.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +system-test.o: ../../drivers/test/system.c ../../drivers/test/pio.h ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + +mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h + $(CC) -c $(CFLAGS) $< -o $@ + +navswitch-test.o: ../../drivers/navswitch.c ../../drivers/test/pio.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/system.h + $(CC) -c $(CFLAGS) $< -o $@ + + + + +# Link: create executable file from object files. +updown2: timer-test.o display-test.o updown2-test.o pio-test.o pacer-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o navswitch-test.o + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) updown2 timer-test.o display-test.o updown2-test.o pio-test.o pacer-test.o ledmat-test.o tinygl-test.o system-test.o mgetkey-test.o navswitch-test.o + + diff --git a/apps/updown2/updown2.c b/apps/updown2/updown2.c new file mode 100644 index 0000000..f72c070 --- /dev/null +++ b/apps/updown2/updown2.c @@ -0,0 +1,80 @@ +/** @file updown2.c + @author M.P. Hayes + @date 28 Aug 2008 + + @defgroup updown2 Simple counting program using navswitch +*/ + +#include "system.h" +#include "navswitch.h" +#include "tinygl.h" +#include "pacer.h" +#include "pio.h" +#include "../fonts/font3x5_1.h" + + +/* Define polling rate in Hz. */ +#define LOOP_RATE 300 + + +static void show_count (uint8_t count) +{ + char buffer[2]; + + /* FIXME! */ + buffer[0] = count + '0'; + buffer[1] = 0; + tinygl_text (buffer); +} + + +int main (void) +{ + uint8_t count = 5; + uint16_t tick = 0; + + system_init (); + pio_config_set (LED1_PIO, PIO_OUTPUT_HIGH); + + tinygl_init (LOOP_RATE); + tinygl_font_set (&font3x5_1); + + navswitch_init (); + pacer_init (LOOP_RATE); + + show_count (count); + + + /* Paced loop. */ + while (1) + { + + /* Wait for next tick. */ + pacer_wait (); + + tick++; + if (tick > LOOP_RATE / 2) + { + tick = 0; + pio_output_toggle (LED1_PIO); + } + + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + { + count--; + show_count (count); + } + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + { + count++; + show_count (count); + } + + tinygl_update (); + } + + return 0; +} diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..93be787 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,15 @@ +all: docs + + +docs: + doxygen doxygen.config + + +zip: ucfk3.zip + + +ucfk3.zip: html/* + zip -r $@ html/ + +clean: + rm -r html diff --git a/doc/README b/doc/README new file mode 100644 index 0000000..850718e --- /dev/null +++ b/doc/README @@ -0,0 +1,4 @@ +To create the online documentation run make then load the file +html/index.html into a web browser, for example, +$ make +$ firefox html/index.html diff --git a/doc/UCFK4.pdf b/doc/UCFK4.pdf new file mode 100644 index 0000000000000000000000000000000000000000..71f5362c572ab9381dc56f2d4258ecff9c41e3b6 GIT binary patch literal 439174 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

YM+m+ly<(~%dy`FUfz-Y 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 UDqzM)4ysnAWlQ`1J*!puUly+5>Hq)$ literal 0 HcmV?d00001 diff --git a/doc/doxygen.config b/doc/doxygen.config new file mode 100644 index 0000000..67729db --- /dev/null +++ b/doc/doxygen.config @@ -0,0 +1,1241 @@ +# Doxyfile 1.4.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "UCFK4" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +# EXTRACT_ALL = NO +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES +# SHOW_INCLUDE_FILES = NO ? + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = NO + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . ../utils ../drivers ../drivers/avr ../apps/bounce1 ../apps/bounce2 ../apps/bounce3 ../apps/bounce4 ../apps/bounce5 ../apps/chooser ../apps/demo1 ../apps/demo2 ../apps/fonttest1 ../apps/game ../apps/hello1 ../apps/hello2 ../apps/hello3../apps/hello4 ../apps/ir_blat1 ../apps/ir_grab1 ../apps/ir_grab2 ../apps/ir_grab3 ../apps/ir_serial_test1 ../apps/ir_serial_test2 ../apps/ir_serial_test3 ../apps/ir_uart_test1 ../apps/ir_uart_test2 ../apps/led0 ../apps/led1 ../apps/led2 ../apps/led3 ../apps/led4 ../apps/led5 ../apps/led6 ../apps/pio1 ../apps/pio2 ../apps/pio3 ../apps/scribble1 ../apps/snake1 ../apps/space10 ../apps/space11 ../apps/space9 ../apps/spacey_remote1 ../apps/steer1 ../apps/task1 ../apps/task2 ../apps/task3 ../apps/tdmdemo1 ../apps/tdmdemo2 ../apps/updown1 ../apps/updown2 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = *.c *.h *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = ../drivers/avr/pio-simple.c ../drivers/avr/pio-simple.h ../drivers/avr/pio-opt.h ../drivers/avr/pio-alt.h ../drivers/avr/pio-opt.c + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES +#REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +#REFERENCES_RELATION = YES +REFERENCES_RELATION = NO + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/doc/main_page.dox b/doc/main_page.dox new file mode 100644 index 0000000..f3f6098 --- /dev/null +++ b/doc/main_page.dox @@ -0,0 +1,259 @@ +/** \mainpage UCFK4 + +@section intro Introduction + +@section structure Directory structure + +- @c apps --- contains a sub-directory for each example application +- @c drivers --- generic device driver modules +- @c drivers/avr --- AVR dependent device driver modules +- @c drivers/test --- test scaffold modules +- @c utils --- utility modules +- @c doc --- documentation +- @c etc --- miscellaneous scripts +- @c fonts --- fonts and font creation program + + +@section examples Example applications + + +There are a number of example applications of increasing complexity in the @c apps directory: +- @ref bounce1 "bounce1" --- a simple application that +drives the LED matrix display directly. +- @ref bounce2 "bounce2" --- this does not worry about refreshing the display; instead this is +performed by the @ref display "display" and @ref ledmat "ledmat" +modules. +- @ref hello1 "hello1" --- displays a message using the @ref ledmat "ledmat" module. +- @ref updown1 "updown1" --- increments or decrements a count depending on button pushes. +- @ref bounce3 "bounce3" --- uses a higher abstraction for driving the display; a tiny graphics library (@ref tinygl "tinygl") built on top of the @ref display "display" and @ref ledmat "ledmat" modules. +- @ref hello2 "hello2" --- displays a message using the @ref tinygl "tinygl" module. +- @ref updown2 "updown2" --- increments or decrements a count depending on button pushes provided by the @ref button "button" module. +- @ref hello4 "hello4" --- displays a message using the @ref tinygl "tinygl" module. It also switches from text step to text scroll modes using the @ref button "button" module to detect button presses. +- @ref bounce4 "bounce4" --- controls two balls using the +@ref boing "boing" module to update the state of a bouncing ball. +- @ref bounce5 "bounce5" --- controls three balls, again using +the @ref boing "boing" module. It also flashes the balls at different +rates and detects collisions. +- @ref fonttest1 "fonttest1" --- displays each character in two fonts. +- @ref ir_serial_test1 "ir_serial_test1" --- tests the @ref IR_serial "ir_serial" module. +- @ref ir_serial_test2 "ir_serial_test2" --- tests the @ref IR_serial "ir_serial" module. +- @ref ir_serial_test3 "ir_serial_test3" --- tests the @ref IR_serial "ir_serial" module. +- @ref space9 "space9" --- a space invaders style game. +- @ref space10 "space10" --- a space invaders style game remotely controlled using the @ref IR_serial "ir_serial" module from the @ref spacey_remote1 "spacey_remote1" application. +- @ref space11 "space11" --- similar to @ref space9 but using @ref task "task" module instead of @ref pacer "pacer" module. + + +Each application sub-directory contains a makefile called @c Makefile. +This contains rules for the @c make program to compile the application. +For example, an application can be compiled using +@verbatim +$ make +@endverbatim + +Similarly, the application can be programmed into the flash memory of +the ATmega32u2 using +@verbatim +$ make program +@endverbatim + +The executable and object files can be deleted using +@verbatim +$ make clean +@endverbatim + +Here's an example of building and programming the application @c hello1.hex: +@verbatim +$ cd apps/hello1 +$ ls +hello1.c Makefile Makefile.test simplefont.h +$ makeavr-gcc -c -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr hello1.c -o hello1.o +avr-gcc -c -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr ../../drivers/avr/target.c -o target.o +avr-gcc -c -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr ../../drivers/pacer.c -o pacer.o +avr-gcc -c -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr ../../drivers/avr/timer.c -o timer.o +avr-gcc -c -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr ../../drivers/led.c -o led.o +avr-gcc -c -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr ../../drivers/avr/pio.c -o pio.o +avr-gcc -c -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr ../../drivers/ledmat.c -o ledmat.o +avr-gcc -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../fonts -I../../drivers -I../../drivers/avr hello1.o target.o pacer.o timer.o led.o pio.o ledmat.o -o hello1.out -lm +avr-size hello1.out + text data bss dec hex filename + 1038 374 5 1417 589 hello1.out +avr-objcopy -O ihex hello1.out hello1.hex +$ ls +hello1.c hello1.o ledmat.o Makefile.test pio.o +hello1.hex hello1.out Makefile pacer.o simplefont.h +$ make program +make program +dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash hello1.hex; dfu-programmer atmega32u2 start +Validating... +1236 bytes used (4.31%) +$ make clean +rm *.o *.out *.hex +$ ls +hello1.c Makefile Makefile.test simplefont.h +@endverbatim + +@section utils Utility modules + +Useful utility modules are grouped in the directory +called @c utils. These include: +- @ref boing "boing" --- @ref boing +- @ref pacer "pacer" --- @ref pacer +- @ref task "task" --- @ref task +- @ref tinygl "tinygl" --- @ref tinygl +- @ref uint8toa "uint8toa" --- @ref uint8toa (this uses much less memory than @c sprintf) + + +@section drivers Device drivers + +Device drivers are grouped in the directory called @c drivers. These include: +- @ref button "button" --- @ref button +- @ref display "display" --- @ref display +- @ref LED "LED" --- @ref LED +- @ref ledmat "ledmat" --- @ref ledmat +- @ref IR "IR" --- @ref IR +- @ref IR_serial "IR serial" --- @ref IR_serial (this has been superseded by @ref IR_uart "IR uart") +- @ref IR_uart "IR uart" --- @ref IR_uart +- @ref navswitch "navswitch" --- @ref navswitch + + +@section HAL Hardware abstraction layer + + +Hardware dependent modules are grouped in the directory @c drivers/avr. These provide a hardware abstraction layer (HAL) and include: +- @ref delay "delay" --- @ref delay (useful for short relative delays) +- @ref eeprom "eeprom" --- @ref eeprom (useful for storing high-scores etc. in non-volatile EEPROM memory) +- @ref timer "timer" --- @ref timer (useful for absolute delays) +- @ref pio "pio" --- @ref pio (provides an abstraction of the I/O ports. Note @c pio.h uses advanced techniques to reduce memory usage; I suggest looking at @c pio-simple.h and @c pio-simple.c for a much simpler implementation.) + + +@section documentation Documentation + +This documentation has been created by a program called doxygen. This +is derived from comment information specified in the program files. +Special comment block markers are required by doxygen. These start +with \** . Inside these comment blocks, doxygen looks for +special names such as \@file or \\file. + +Running the @c make program in the @c doc directory will run doxygen. +It will generate html files that can be viewed by a web browser in the +@c doc/html directory. The main page is called @c index.html. Note, +you may need to edit the INPUT field in @c doc/doxygen.config to +specify new directories. + + +@section Testing + +I have created a simple test-scaffold so that programs can be run +natively on a PC. The test-scaffold files in drivers/test replaces the +hardware specific modules in avr_test, such as @ref timer "timer". +Each example application has a makefile called Makefile.test. This is +selected using the -f option to make. For example, +@verbatim +$ make -f Makefile.test +@endverbatim +will produce a native application that will run in a terminal window +of a PC. When the program runs, the output of the display is shown in +the terminal window. The arrow keys correspond to the four directions +of the navswitch and the spacebar corresponds to the push of the +navswitch. The period key corresponds to the pushbutton. + +Here's an example of building the test application @c hello1. + +@verbatim +$ cd apps/hello1 +$ make -f Makefile.test +gcc -c -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test hello1.c -o hello1-test.o +gcc -c -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test ../../drivers/test/target.c -o target-test.o +gcc -c -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test ../../drivers/test/avrtest.c -o avrtest-test.o +gcc -c -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test ../../drivers/test/pio.c -o pio-test.o +gcc -c -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test ../../drivers/test/mgetkey.c -o mgetkey-test.o +gcc -c -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test ../../drivers/pacer.c -o pacer-test.o +gcc -c -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test ../../drivers/test/timer.c -o timer-test.o +gcc -c -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test ../../drivers/led.c -o led-test.o +gcc -c -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test ../../drivers/ledmat.c -o ledmat-test.o +gcc -Wall -Wstrict-prototypes -Wextra -g -I. -I../../utils -I../../drivers -I../../drivers/test hello1-test.o target-test.o avrtest-test.o pio-test.o mgetkey-test.o pacer-test.o timer-test.o led-test.o ledmat-test.o -o hello1 -lrt +$ ls +avrtest-test.d hello1-test.d Makefile pio-test.d ucfktest-test.o +avrtest-test.o hello1-test.o Makefile.test pio-test.o +hello1 ledmat-test.d pacer-test.d simplefont.h +hello1.c ledmat-test.o pacer-test.o ucfktest-test.d +@endverbatim + +The test application is called @c hello1. It can be run using: +@verbatim +$ ./hello1 +..... +..... +..... +..... +..... +..... +..... + +@.... +@.... +@.... +@.... +@.... +@.... +@.... + +@.... +@.... +@.... +@@... +@.... +@.... +@.... + +@endverbatim + + +@section Fonts + +The @c font directory contains a program (@c fontgen.c) that will +convert custom fonts specified in a text file into a header file with +a lookup table that can be loaded into an application. For further +details, see @c font/README. + +For example, here's a custom face font mapped to the ASCII characters +'A', 'B', and 'C'. + +

+width=5
+height=7
+A
+.#.#.
+.#.#.
+.#.#.
+.....
+.....
+#...#
+.###.
+B
+.....
+##.##
+##.##
+.....
+..#..
+#...#
+.###.
+C
+.....
+##.##
+##.##
+.....
+..#..
+.###.
+#...#
+
+ + +@section bugs Bugs + +If you think you've found a bug, or have a suggestion for an +improvement, either in this documentation or in the code itself, +please email michael.hayes@canterbury.ac.nz. + +*/ + diff --git a/drivers/avr/README b/drivers/avr/README new file mode 100644 index 0000000..5e81646 --- /dev/null +++ b/drivers/avr/README @@ -0,0 +1,10 @@ +This is a collection of modules to provide a hardware abstraction +layer for the AVR architecture. + +delay.h --- provides timing delay routines. +eeprom.h --- provides routines for reading/writing EEPROM memory for non-volatile storage. +pacer.h --- provides routines for constructing paced loops using timer 1. +pio.h --- provides routines for accessing the I/O ports. Note pio.h uses + advanced techniques; I suggest looking at pio-simple.h and pio-simple.c for a much simpler implementation. +system.h --- system dependent definitions, such as CPU speed and pin allocations. +config.h --- common configuration definitions. diff --git a/drivers/avr/adc.c b/drivers/avr/adc.c new file mode 100644 index 0000000..23bdb95 --- /dev/null +++ b/drivers/avr/adc.c @@ -0,0 +1,90 @@ +/** @file adc.c + @author Michael Hayes + @date 3 Feb 2005 + @brief Routines to use AVR onboard ADC in polling mode. +*/ + +#include "system.h" +#include "adc.h" +#include "bits.h" + +/* ADC Clock Prescaler : 2, 2, 4, 8, 16, 32, 64, 128 = + 2^ADCCLKPRE. Prescale ADC clock by 64 (13 cycles * (64 + prescaler) / 12MHz = 70 us conversion time). */ +#define ADCCLKPRE 6 + + +void adc_reference_select (adc_ref_mode_t mode) +{ + BITS_INSERT (ADMUX, mode, 6, 7); +} + + +/* Initalise the ADC. */ +void adc_init (uint8_t channels) +{ + adc_reference_select (ADC_REF_AVCC); + + /* Right adjust result. */ + ADMUX &= ~BIT (ADLAR); + + /* Enable ADC, prescale ADC clock by 64. */ + ADCSRA |= BIT (ADEN) | BIT (ADPS2) | BIT (ADPS1); + +#ifdef ADATE + /* Disable ADC auto trigger, disable ADC interrupt. */ + ADCSRA &= ~ (BIT (ADATE) | BIT (ADIE) | BIT (ADPS0)); +#else + ADCSRA &= ~ (BIT (ADIE) | BIT (ADPS0)); +#endif + + /* Disable digital input buffers for selected ADC channels. */ +#ifdef DIDR0 + DIDR0 = channels; +#endif +} + + +/* Starts a conversion in the ADC on the specified channel. */ +bool adc_start (adc_channel_t channel) +{ + if (channel >= ADC_NUM) + return 0; + + BITS_INSERT (ADMUX, channel, 0, 3); + + if (!adc_ready_p ()) + return 0; + + /* Start conversion. */ + ADCSRA |= BIT (ADSC); + return 1; +} + + +/* Returns true if a conversion is not in progress. */ +bool adc_ready_p (void) +{ + return ! (ADCSRA & BIT (ADSC)); +} + + +/** Read the current ADC value. + @return the ADC value. + @note this blocks until the ADC has finished a conversion. */ +adc_sample_t +adc_read (void) +{ + while (!adc_ready_p ()) + continue; + + return ADC; +} + + +/** Disables the ADC. */ +void adc_disable (void) +{ + /* Disable ADC, Disable ADC Interrupt. */ + ADCSRA &= ~ (BIT (ADEN) | BIT (ADIE)); +} diff --git a/drivers/avr/adc.h b/drivers/avr/adc.h new file mode 100644 index 0000000..ae12038 --- /dev/null +++ b/drivers/avr/adc.h @@ -0,0 +1,63 @@ +/** @file adc.h + @author M. P. Hayes, UCECE + @date 3 Feb 2005 + + @brief Routines to use AVR onboard ADC in both a polling mode + and automatically updating mode. +*/ + +#ifndef ADC_H +#define ADC_H + +#include "system.h" + +/** ADC channels. */ +typedef enum {ADC0 = 0, ADC1, ADC2, ADC3, ADC4, ADC5, ADC6, ADC7, ADC_NUM} + adc_channel_t; + +/** ADC reference modes. */ +typedef enum {ADC_REF_EXT = 0, ADC_REF_AVCC, ADC_REF_RESERVED, ADC_REF_2V56} + adc_ref_mode_t; + +/** ADC bits per conversion. */ +enum {ADC_BITS = 10}; + +/** ADC sample size. */ +typedef uint16_t adc_sample_t; + + +/** Select ADC reference mode. */ +extern void +adc_reference_select (adc_ref_mode_t mode); + + +/** Initalises the ADC registers for polling operation. */ +extern void +adc_init (uint8_t channels); + + +/** Starts a conversion in the ADC on the specified channel. */ +extern bool +adc_conversion_start (adc_channel_t channel); + + +/** Returns true if a conversion is not in progress. */ +extern bool +adc_ready_p (void); + + +/** Returns 1 if valid sample read. */ +extern int8_t +adc_read (adc_sample_t *value); + + +/** Halts any currently running conversion. */ +extern void +adc_stop (void); + + +/** Disables the ADC from doing anything. Requires reinitalisation. */ +extern void +adc_disable (void); + +#endif diff --git a/drivers/avr/bits.h b/drivers/avr/bits.h new file mode 100644 index 0000000..680c131 --- /dev/null +++ b/drivers/avr/bits.h @@ -0,0 +1,22 @@ +/** @file bits.h + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief Macros for manipulating bit fields. +*/ +#ifndef BITS_H +#define BITS_H + +#define BITS_MASK(first, last) ((1 << ((last) + 1)) - (1 << (first))) + +#define BITS_CLEAR(reg, first, last) ((reg) &= BITS_MASK (first, last)) + +#define BITS_SET(reg, first, last) ((reg) |= BITS_MASK (first, last)) + +#define BITS_EXTRACT(reg, first, last) \ + (((reg) & BITS_MASK (first, last)) >> (first)) + +#define BITS_INSERT(reg, val, first, last) \ + (reg) = ((reg) & ~BITS_MASK (first, last)) \ + | (((val) & BITS_MASK (0, last - first)) << (first)) + +#endif diff --git a/drivers/avr/delay.h b/drivers/avr/delay.h new file mode 100644 index 0000000..ac00736 --- /dev/null +++ b/drivers/avr/delay.h @@ -0,0 +1,93 @@ +/** @file delay.h + @author M. P. Hayes, UCECE + @date 08 June 2002 + @brief Delay routines for timing. + @note All files that use DELAY_US must be compiled with full optimisation. + + @defgroup delay Delay routines + + This module provides routines for short timing delays. These are + implemented as delay loops. DELAY_US must not be called with a variable + argument since the run-time floating point calculation will almost certainly + swamp the delay you require. + + @note The macro F_CPU defines the CPU clock frequency (in Hertz). +*/ +#ifndef DELAY_H +#define DELAY_H + +#include "system.h" +#include + +/* There are two timing loop routines provided in delay.h: + + _delay_loop_1 uses an 8-bit counter and requires 3 cycles per + iteration but has a maximum of 256 iterations. With F_CPU = 8e6 + then the range of delays is 0.375 us to 96.0 us in steps of 0.375 us. + + _delay_loop_2 uses a 16-bit counter, requires 4 cycles per + iteration and has a maximum of 65536 iterations. With F_CPU = 8e6 + then the range of delays is 0.5 us to 32.768 ms in steps of 0.5 us. */ + +#define DELAY1_LOOP_CYCLES 3 +#define DELAY1_LOOPS_MAX 256 +#define DELAY1_US_MAX (DELAY1_LOOPS_MAX * DELAY1_LOOP_CYCLES * 1e6 / (F_CPU)) + +#define DELAY2_LOOP_CYCLES 4 +#define DELAY2_LOOPS_MAX 65536 +#define DELAY2_US_MAX (DELAY2_LOOPS_MAX * DELAY2_LOOP_CYCLES * 1e6 / (F_CPU)) + + +/** Delay a selected number of microseconds + @param us number of microseconds to delay. This must be a constant. + @note This should be only used with a constant argument otherwise + it will expand to a lot of code and require slow floating point operations to compute + the number of cycles to delay. */ +#define DELAY_US(us) \ +do \ +{ \ + if ((us) > DELAY1_US_MAX) \ + { \ + double __tmp2 = ((double)F_CPU * (us)) / (DELAY2_LOOP_CYCLES * 1e6); \ + uint16_t __ticks2; \ + \ + if ((us) > DELAY2_US_MAX) \ + __ticks2 = 0; \ + else \ + __ticks2 = (uint16_t)__tmp2; \ + _delay_loop_2 (__ticks2); \ + } \ + else \ + { \ + double __tmp1 = ((double)F_CPU * (us)) / (DELAY1_LOOP_CYCLES * 1e6); \ + uint8_t __ticks1; \ + \ + if (__tmp1 < 1.0) \ + __ticks1 = 1; \ + else \ + __ticks1 = (uint8_t)__tmp1; \ + _delay_loop_1 (__ticks1); \ + } \ +} \ +while (0) + + +/** Delay a selected number of milliseconds + @param ms number of milliseconds to delay. This can be a variable. */ +static __inline__ void +delay_ms (uint8_t ms) +{ + /* FIXME: "i" should only need to be a uint8_t + Some bug has occured using a newer version of the avr-gcc that + causes this routine to only delay 1ms if both "ms" and "i" + are uint8_t. */ + uint16_t i; + + for (i = 0; i < ms; i++) + { + DELAY_US (1000); + } +} + + +#endif /* DELAY_H */ diff --git a/drivers/avr/eeprom.c b/drivers/avr/eeprom.c new file mode 100644 index 0000000..42aeda7 --- /dev/null +++ b/drivers/avr/eeprom.c @@ -0,0 +1,71 @@ +/** @file eeprom.c + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief +*/ +#include "system.h" +#include +#include "eeprom.h" + +static void +eeprom_write_byte (eeprom_addr_t addr, uint8_t data) +{ + /* Wait for completion of previous write. */ + while (EECR & _BV (EEPE)) + continue; + + /* Set up address and data registers */ + EEAR = addr; + EEDR = data; + + EECR |= _BV (EEMPE); + + /* Start eeprom write. */ + EECR |= _BV (EEPE); +} + + +static uint8_t eeprom_read_byte (eeprom_addr_t addr) +{ + /* Wait for completion of previous write. */ + while (EECR & _BV (EEPE)) + continue; + + /* Set up address register */ + EEAR = addr; + + /* Start eeprom read. */ + EECR |= _BV (EERE); + + /* Return data from data register */ + return EEDR; +} + + +/* Read SIZE bytes from ADDR into BUFFER. */ +eeprom_size_t +eeprom_read (eeprom_addr_t addr, void *buffer, eeprom_size_t size) +{ + eeprom_size_t i; + uint8_t *data = buffer; + + for (i = 0; i < size; i++) + data[i] = eeprom_read_byte (addr++); + + return i; + +} + + +/* Write SIZE bytes to ADDR from BUFFER. */ +eeprom_size_t +eeprom_write (eeprom_addr_t addr, const void *buffer, eeprom_size_t size) +{ + eeprom_size_t i; + const uint8_t *data = buffer; + + for (i = 0; i < size; i++) + eeprom_write_byte (addr++, data[i]); + + return i; +} diff --git a/drivers/avr/eeprom.h b/drivers/avr/eeprom.h new file mode 100644 index 0000000..20bbcff --- /dev/null +++ b/drivers/avr/eeprom.h @@ -0,0 +1,30 @@ +/** @file eeprom.h + @author Michael Hayes + @date 13 Nov 2006 + @brief Routines to read/write internal EEPROM. + + @defgroup eeprom EEPROM driver + + This module implements a driver to read/write the EEPROM of an + ATmega microcontroller. The EEPROM is useful for storing + persistent data when the power is removed. + */ + +#ifndef EEPROM_H +#define EEPROM_H + +#include "system.h" + +typedef uint16_t eeprom_addr_t; +typedef uint16_t eeprom_size_t; + +/* Read SIZE bytes from ADDR into BUFFER. */ +extern eeprom_size_t +eeprom_read (eeprom_addr_t addr, void *buffer, eeprom_size_t size); + + +/* Write SIZE bytes to ADDR from BUFFER. */ +extern eeprom_size_t +eeprom_write (eeprom_addr_t addr, const void *buffer, eeprom_size_t size); + +#endif diff --git a/drivers/avr/ir_uart.c b/drivers/avr/ir_uart.c new file mode 100644 index 0000000..34ef792 --- /dev/null +++ b/drivers/avr/ir_uart.c @@ -0,0 +1,85 @@ +/** @file ir_uart.c + @author Michael Hayes + @date 10 December 2004 +*/ +#include "system.h" +#include "ir_uart.h" +#include "usart1.h" +#include "timer0.h" +#include "pio.h" + + +/* Return non-zero if there is a character ready to be read. */ +bool +ir_uart_read_ready_p (void) +{ + return usart1_read_ready_p (); +} + + +/* Read character from IR_UART. This blocks if nothing + is available to read. */ +int8_t +ir_uart_getc (void) +{ + return usart1_getc (); +} + + +/* Return non-zero if a character can be written without blocking. */ +bool +ir_uart_write_ready_p (void) +{ + return usart1_write_ready_p (); +} + + +/* Return non-zero if transmitter finished. */ +bool +ir_uart_write_finished_p (void) +{ + return usart1_write_finished_p (); +} + + +/* Write character to IR_UART. This returns zero if + the character could not be written. */ +int8_t +ir_uart_putc (char ch) +{ + return usart1_putc (ch); +} + + +/* Write string to IR_UART. */ +void +ir_uart_puts (const char *str) +{ + usart1_puts (str); +} + + +/* Initialise ir_uart and set baud rate. */ +uint8_t +ir_uart_init (void) +{ + usart1_cfg_t usart1_cfg = + { + .baud_divisor = USART1_BAUD_DIVISOR (IR_UART_BAUD_RATE), + .bits = 8 + }; + + + /* Need to configure DDR so pin is an output. + The state is controlled by the timer. */ + pio_config_set (IR_TX_HIGH_PIO, PIO_OUTPUT_HIGH); + timer0_init (); + + timer0_period_set (F_CPU / (IR_MODULATION_FREQ * 2)); + timer0_mode_set (TIMER0_MODE_CTC); + timer0_output_set (TIMER0_OUTPUT_B, TIMER0_OUTPUT_MODE_TOGGLE); + + timer0_start (); + + return usart1_init (&usart1_cfg); +} diff --git a/drivers/avr/ir_uart.h b/drivers/avr/ir_uart.h new file mode 100644 index 0000000..800bbe3 --- /dev/null +++ b/drivers/avr/ir_uart.h @@ -0,0 +1,95 @@ +/** @file ir_uart.h + @author Michael Hayes + @date 10 December 2004 + + @defgroup IR_uart Infrared uart driver + + Here's a simple application that transmits the ASCII code '?' repeatedly. + + @code + #include "ir_uart.h" + + void main (void) + { + system_init (); + ir_uart_init (); + + while (1) + { + ir_uart_putc ('?'); + } + } + @endcode + + Here's another simple application that receives data bytes. + + @code + #include "ir_uart.h" + + void main (void) + { + system_init (); + ir_uart_init (); + + while (1) + { + if (ir_uart_read_ready_p ()) + { + char ch; + + ch = ir_uart_getc (); + + // Process the received byte. + } + } + } + @endcode +*/ + +#ifndef IR_UART_H +#define IR_UART_H + +#include "system.h" + +#ifndef IR_UART_BAUD_RATE +#define IR_UART_BAUD_RATE 2400 +#endif + + +/* Return non-zero if there is a character ready to be read. */ +bool +ir_uart_read_ready_p (void); + + +/* Read character from IR_UART. This blocks if nothing + is available to read. */ +int8_t +ir_uart_getc (void); + + +/* Return non-zero if a character can be written without blocking. */ +bool +ir_uart_write_ready_p (void); + + +/* Return non-zero if transmitter finished. */ +bool +ir_uart_write_finished_p (void); + + +/* Write character to IR_UART. This returns zero if + the character could not be written. */ +int8_t +ir_uart_putc (char ch); + + +/* Write string to IR_UART. */ +void +ir_uart_puts (const char *str); + + +/* Initialise ir_uart and set baud rate. */ +uint8_t +ir_uart_init (void); + +#endif diff --git a/drivers/avr/pio-simple.c b/drivers/avr/pio-simple.c new file mode 100644 index 0000000..5ae6716 --- /dev/null +++ b/drivers/avr/pio-simple.c @@ -0,0 +1,306 @@ +/** @file pio-simple.c + @author M. P. Hayes, UCECE + @date 13 Aug 2009 + @brief PIO hardware abstraction for AVR microcontroller. + @note This implementation is written for clarity not efficiency. +*/ + +#include "pio-simple.h" + + +/** Configure pio + @param pio */ +bool pio_config_set (pio_t pio, pio_config_t config) +{ + uint8_t bitmask; + + bitmask = BIT (pio.bit); + + switch (pio.port) + { + case PORT_B: + switch (config) + { + case PIO_INPUT: + DDRB &= ~bitmask; + PORTB &= ~bitmask; + break; + + case PIO_PULLUP: + DDRB &= ~bitmask; + PORTB |= bitmask; + break; + + case PIO_OUTPUT_LOW: + PORTB &= ~bitmask; + DDRB |= bitmask; + break; + + case PIO_OUTPUT_HIGH: + PORTB |= bitmask; + DDRB |= bitmask; + break; + + default: + return 0; + } + break; + + case PORT_C: + switch (config) + { + case PIO_INPUT: + DDRC &= ~bitmask; + PORTC &= ~bitmask; + break; + + case PIO_PULLUP: + DDRC &= ~bitmask; + PORTC |= bitmask; + break; + + case PIO_OUTPUT_LOW: + PORTC &= ~bitmask; + DDRC |= bitmask; + break; + + case PIO_OUTPUT_HIGH: + PORTC |= bitmask; + DDRC |= bitmask; + break; + + default: + return 0; + } + break; + + case PORT_D: + switch (config) + { + case PIO_INPUT: + DDRD &= ~bitmask; + PORTD &= ~bitmask; + break; + + case PIO_PULLUP: + DDRD &= ~bitmask; + PORTD |= bitmask; + break; + + case PIO_OUTPUT_LOW: + PORTD &= ~bitmask; + DDRD |= bitmask; + break; + + case PIO_OUTPUT_HIGH: + PORTD |= bitmask; + DDRD |= bitmask; + break; + + default: + return 0; + } + break; + + default: + return 0; + } + return 1; +} + + +/** Set pio high. + @param pio */ +void pio_output_high (pio_t pio) +{ + uint8_t bitmask; + + bitmask = BIT (pio.bit); + + switch (pio.port) + { + case PORT_B: + PORTB |= bitmask; + break; + + case PORT_C: + PORTC |= bitmask; + break; + + case PORT_D: + PORTD |= bitmask; + break; + + default: + break; + } +} + + +/** Set pio low. + @param pio */ +void pio_output_low (pio_t pio) +{ + uint8_t bitmask; + + bitmask = BIT (pio.bit); + + switch (pio.port) + { + case PORT_B: + PORTB &= ~bitmask; + break; + + case PORT_C: + PORTC &= ~bitmask; + break; + + case PORT_D: + PORTD &= ~bitmask; + break; + + default: + break; + } +} + + +/** Toggle pio. + @param pio */ +void pio_output_toggle (pio_t pio) +{ + uint8_t bitmask; + + bitmask = BIT (pio.bit); + + switch (pio.port) + { + case PORT_B: + PORTB ^= bitmask; + break; + + case PORT_C: + PORTC ^= bitmask; + break; + + case PORT_D: + PORTD ^= bitmask; + break; + + default: + break; + } +} + + +/** Read input state from pio. + @param pio + @return input state of pio */ +bool pio_input_get (pio_t pio) +{ + uint8_t bitmask; + + bitmask = BIT (pio.bit); + + switch (pio.port) + { + case PORT_B: + return (PINB & bitmask) != 0; + + case PORT_C: + return (PINC & bitmask) != 0; + + case PORT_D: + return (PIND & bitmask) != 0; + + default: + return 0; + } +} + + +/** Set pio to desired state + @param pio + @param state value to write pio */ +void pio_output_set (pio_t pio, bool state) +{ + if (state) + pio_output_high (pio); + else + pio_output_low (pio); +} + + +/** Return pio configuration + @param pio + @return config */ +pio_config_t pio_config_get (pio_t pio) +{ + bool ddr; + bool port; + uint8_t bitmask; + + bitmask = BIT (pio.bit); + + switch (pio.port) + { + case PORT_B: + ddr = (DDRB & bitmask) != 0; + port = (DDRB & bitmask) != 0; + break; + + case PORT_C: + ddr = (DDRC & bitmask) != 0; + port = (DDRC & bitmask) != 0; + break; + + case PORT_D: + ddr = (DDRD & bitmask) != 0; + port = (DDRD & bitmask) != 0; + break; + + default: + return 0; + } + + if (ddr) + { + if (port) + return PIO_OUTPUT_HIGH; + else + return PIO_OUTPUT_LOW; + } + + if (port) + return PIO_PULLUP; + + return PIO_INPUT; +} + + +/** Get output state of pio. + @param pio + @return output state of pio */ +bool pio_output_get (pio_t pio) +{ + uint8_t bitmask; + + bitmask = BIT (pio.bit); + + switch (pio.port) + { + case PORT_B: + return (PORTB & bitmask) != 0; + + case PORT_C: + return (PORTC & bitmask) != 0; + + case PORT_D: + return (PORTD & bitmask) != 0; + + default: + return 0; + } +} + + diff --git a/drivers/avr/pio-simple.h b/drivers/avr/pio-simple.h new file mode 100644 index 0000000..f5c0c84 --- /dev/null +++ b/drivers/avr/pio-simple.h @@ -0,0 +1,77 @@ +/** @file pio-simple.h + @author M. P. Hayes, UCECE + @date 11 Jan 2006 + @brief PIO hardware abstraction. +*/ +#ifndef PIO_H +#define PIO_H + +#include "system.h" + +typedef enum pio_port_enum +{ + PORT_A, PORT_B, PORT_C, PORT_D, PORT_E +} pio_port_t; + +typedef uint8_t pio_bit_t; + +typedef struct pio_struct +{ + pio_port_t port; + pio_bit_t bit; +} pio_t; + +typedef enum pio_config_enum +{ + PIO_INPUT = 1, PIO_OUTPUT_LOW, PIO_OUTPUT_HIGH, PIO_PULLUP +} pio_config_t; + + +/* Define a PIO as a structure in terms of a port and a bit. */ +#define PIO_DEFINE(PORT, PORTBIT) (pio_t){PORT, PORTBIT} + + +/** Configure pio. + @param pio */ +bool pio_config_set (pio_t pio, pio_config_t config); + + +/** Configure pio. + @param pio + @return config */ +pio_config_t pio_config_get (pio_t pio); + + +/** Set pio high. + @param pio */ +void pio_output_high (pio_t pio); + + +/** Set pio low. + @param pio */ +void pio_output_low (pio_t pio); + + +/** Toggle pio. + @param pio */ +void pio_output_toggle (pio_t pio); + + +/** Read input state from pio. + @param pio + @return input state of pio */ +bool pio_input_get (pio_t pio); + + +/** Set pio to desired state + @param pio + @param state value to write pio */ +void pio_output_set (pio_t pio, bool state); + + +/** Get output state of pio. + @param pio + @return output state of pio */ +bool pio_output_get (pio_t pio); + +#endif diff --git a/drivers/avr/pio.c b/drivers/avr/pio.c new file mode 100644 index 0000000..cde2a3c --- /dev/null +++ b/drivers/avr/pio.c @@ -0,0 +1,9 @@ +/** @file pio.c + @author M. P. Hayes, UCECE + @date 11 Jan 2006 + @brief PIO hardware abstraction for AVR microcontroller. + @note This implementation is empty; inline functions are used + in pio.h instead for performance. +*/ +#include "system.h" +#include "pio.h" diff --git a/drivers/avr/pio.h b/drivers/avr/pio.h new file mode 100644 index 0000000..131713f --- /dev/null +++ b/drivers/avr/pio.h @@ -0,0 +1,253 @@ +/** @file pio.h + @author M. P. Hayes, UCECE + @date 11 Jan 2006 + @brief PIO hardware abstraction for AVR microcontroller. + @note Macros and inline functions are used to avoid function + call overhead and to allow compile-time constant folding. + + @defgroup pio PIO driver + + This module implements a driver for general purpose I/O ports. + Its purpose is to provide a microcontroller independent + abstraction of a PIO pin. Here's an example: + + @code + #include "pio.h" + + #define LED_PIO PIO_DEFINE (PORT_B, 5) + + void main (void) + { + system_init (); + pio_config_set (LED_PIO, PIO_OUTPUT_LOW); + + pio_output_high (LED_PIO); + + while (1); + } + @endcode + + In this example, when port B5 is configured as an output it is set + low. +*/ +#ifndef PIO_H +#define PIO_H + +#include "system.h" +#ifdef AVR +#include +#endif + + +/** Define port names, note not all the ports are available on some AVRs. */ + +#ifdef PORTA +#define PORT_A &PORTA +#endif + +#ifdef PORTB +#define PORT_B &PORTB +#endif + +#ifdef PORTC +#define PORT_C &PORTC +#endif + +#ifdef PORTD +#define PORT_D &PORTD +#endif + +#ifdef PORTE +#define PORT_E &PORTE +#endif + +#ifdef PORTF +#define PORT_F &PORTF +#endif + +#ifdef PORTG +#define PORT_G &PORTG +#endif + + +typedef volatile uint8_t *pio_port_t; +typedef uint8_t pio_mask_t; + + +typedef struct +{ + pio_port_t port; + pio_mask_t bitmask; +} pio_t; + + +/** Define PIO pin types. The two flavours of PIO_OUTPUT are to + ensure the port is configured without glitching due to the time + taken before calling pio_output_set. */ +typedef enum pio_config_enum +{ + PIO_INPUT = 1, /** Configure as input pin. */ + PIO_PULLUP, /** Configure as input pin with pullup. */ + PIO_OUTPUT_LOW, /** Configure as output, initially low. */ + PIO_OUTPUT_HIGH, /** Configure as output, initially high. */ +} pio_config_t; + + +/** DDR PORT Comment + 0 0 High impedance input + 0 1 Weak pullup input + 1 0 Output low + 1 1 Output high +*/ + + +/** Define a PIO as a compound literal structure in terms of a port and + a bitmask. */ +#define PIO_DEFINE(PORT, PORTBIT) (pio_t){.port = PORT, .bitmask = BIT (PORTBIT)} + + +/** Private macro to lookup bitmask. */ +#define PIO_BITMASK_(pio) (pio.bitmask) + + +/** Private macro to lookup port register. */ +#define PIO_PORT_(pio) (pio.port) + + +/** Private macro to map a pio to its corresponding data direction + register (DDR). NB, the DDR and PORT registers must be separated + by the same number of bytes in all cases. PORTB is used for the + pattern since PORTA is not always defined for some AVRs. */ +#define PIO_DDR_(pio) (*(PIO_PORT_ (pio) + (&DDRB - &PORTB))) + +/** Private macro to map a pio to its input register (PIN). NB, the + PIN and PORT registers must be separated by the same number of + bytes in all cases. PORTB is used for the pattern since PORTA is + not always defined for some AVRs. */ +#define PIO_PIN_(pio) (*(PIO_PORT_ (pio) + (&PINB - &PORTB))) + +/** Private macro to access a pio data register. */ +#define PIO_DATA_(pio) (*PIO_PORT_ (pio)) + + + +/** Configure pio. + @param pio PIO to configure + @param config PIO configuration type + @return non-zero for success. */ +static inline +bool pio_config_set (pio_t pio, pio_config_t config) +{ + switch (config) + { + case PIO_OUTPUT_LOW: + PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio); + PIO_DDR_ (pio) |= PIO_BITMASK_ (pio); + return 1; + + case PIO_OUTPUT_HIGH: + PIO_DATA_ (pio) |= PIO_BITMASK_ (pio); + PIO_DDR_ (pio) |= PIO_BITMASK_ (pio); + return 1; + + case PIO_INPUT: + PIO_DDR_ (pio) &= ~PIO_BITMASK_ (pio); + PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio); + return 1; + + case PIO_PULLUP: + PIO_DDR_ (pio) &= ~PIO_BITMASK_ (pio); + PIO_DATA_ (pio) |= PIO_BITMASK_ (pio); + return 1; + + default: + return 0; + } +} + + +/** Return pio configuration + @param pio + @return config */ +static inline +pio_config_t pio_config_get (pio_t pio) +{ + bool ddr; + bool port; + + ddr = PIO_DDR_ (pio) & PIO_BITMASK_ (pio); + port = PIO_DATA_ (pio) & PIO_BITMASK_ (pio); + + if (ddr) + { + if (port) + return PIO_OUTPUT_HIGH; + else + return PIO_OUTPUT_LOW; + } + + if (port) + return PIO_PULLUP; + + return PIO_INPUT; +} + + + +/** Set pio high. + @param pio */ +static inline +void pio_output_high (pio_t pio) +{ + PIO_DATA_ (pio) |= PIO_BITMASK_ (pio); +} + + +/** Set pio low. + @param pio */ +static inline +void pio_output_low (pio_t pio) +{ + PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio); +} + + +/** Toggle pio. + @param pio */ +static inline +void pio_output_toggle (pio_t pio) +{ + PIO_DATA_ (pio) ^= PIO_BITMASK_ (pio); +} + + +/** Read input state from pio. + @param pio + @return input state of pio */ +static inline +bool pio_input_get (pio_t pio) +{ + return (PIO_PIN_ (pio) & PIO_BITMASK_ (pio)) != 0; +} + + +/** Get output state of pio. + @param pio + @return output state of pio */ +static inline +bool pio_output_get (pio_t pio) +{ + return (PIO_DATA_ (pio) & PIO_BITMASK_ (pio)) != 0; +} + + +/** Set pio to desired state. + @param pio + @param state value to write pio */ +static inline +void pio_output_set (pio_t pio, bool state) +{ + state ? pio_output_high (pio) : pio_output_low (pio); +} + +#endif diff --git a/drivers/avr/prescale.c b/drivers/avr/prescale.c new file mode 100644 index 0000000..f2d2454 --- /dev/null +++ b/drivers/avr/prescale.c @@ -0,0 +1,35 @@ +/** @file prescale.c + @author M. P. Hayes, UCECE + @date 3 Aug 2011 + @brief Prescaler selection for 8-bit timer/counters. +*/ +#include "system.h" +#include "prescale.h" +#include + +uint8_t prescale_select (uint16_t period, const uint8_t *log_prescales, + uint8_t num_prescales, uint8_t *pcounter) +{ + uint8_t i; + uint16_t counter = 0; + + /* If the desired period is T we need to choose the counter value + c and the prescale value s to minimise |T - c x 2^p| with the + constraints that 0 < c < 256 and 2^p is one of the available + prescale values. */ + + /* Search through prescaler options looking for first one + where the counter value is < 256. */ + for (i = 0; i < num_prescales; i++) + { + uint16_t divisor; + + divisor = 1 << log_prescales[i]; + counter = (period + (divisor + 1) / 2) >> log_prescales[i]; + if (counter < 256) + break; + } + + *pcounter = counter; + return i; +} diff --git a/drivers/avr/prescale.h b/drivers/avr/prescale.h new file mode 100644 index 0000000..6423432 --- /dev/null +++ b/drivers/avr/prescale.h @@ -0,0 +1,15 @@ +/** @file prescale.h + @author M. P. Hayes, UCECE + @date 3 Aug 2011 + @brief Prescaler selection for timer/counters. +*/ + +#ifndef PRESCALE_H +#define PRESCALE_H + +#include "system.h" + +uint8_t prescale_select (uint16_t period, const uint8_t *log_prescales, + uint8_t num_prescales, uint8_t *pcounter); + +#endif diff --git a/drivers/avr/system.c b/drivers/avr/system.c new file mode 100644 index 0000000..f7b30af --- /dev/null +++ b/drivers/avr/system.c @@ -0,0 +1,40 @@ +/** @file system.c + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief UCFK system initialisation +*/ +#include "system.h" +#include + + +static void system_clock_init (void) +{ + /* Switch 1 MHz CPU clock to 8 MHz. */ + CLKPR = BIT (CLKPCE); + CLKPR = 0; +} + + +static void system_watchdog_timer_init (void) +{ + wdt_reset (); + + /* Clear WDRF in MCUSR. */ + MCUSR &= ~BIT (WDRF); + /* Write logical one to WDCE and WDE and keep old prescaler + setting to prevent unintentional time-out. */ + WDTCSR |= BIT (WDCE) | BIT (WDE); + + /* Turn off watchdog timer. */ + WDTCSR = 0x00; +} + + +void system_init (void) +{ + system_clock_init (); + system_watchdog_timer_init (); +} + + + diff --git a/drivers/avr/system.h b/drivers/avr/system.h new file mode 100644 index 0000000..7afbebf --- /dev/null +++ b/drivers/avr/system.h @@ -0,0 +1,74 @@ +/** @file system.h + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief System specific definitions +*/ +#ifndef SYSTEM_H +#define SYSTEM_H + +/* Data typedefs. */ +#include + +typedef uint8_t bool; + + +/* Useful macros. */ +#define BIT(X) (1 << (X)) + +#define ARRAY_SIZE(ARRAY) (sizeof (ARRAY) / sizeof (ARRAY[0])) + +#define __unused__ __attribute__ ((unused)) + + +/* Clock frequency Hz. */ +#define F_CPU 8000000 + + +/* LED matrix columns. */ +#define LEDMAT_COL1_PIO PIO_DEFINE(PORT_C, 6) +#define LEDMAT_COL2_PIO PIO_DEFINE(PORT_B, 7) +#define LEDMAT_COL3_PIO PIO_DEFINE(PORT_C, 4) +#define LEDMAT_COL4_PIO PIO_DEFINE(PORT_C, 7) +#define LEDMAT_COL5_PIO PIO_DEFINE(PORT_C, 5) +#define LEDMAT_COLS_NUM 5 + + +/* LED matrix rows. */ +#define LEDMAT_ROW1_PIO PIO_DEFINE(PORT_B, 6) +#define LEDMAT_ROW2_PIO PIO_DEFINE(PORT_B, 5) +#define LEDMAT_ROW3_PIO PIO_DEFINE(PORT_B, 4) +#define LEDMAT_ROW4_PIO PIO_DEFINE(PORT_B, 3) +#define LEDMAT_ROW5_PIO PIO_DEFINE(PORT_B, 2) +#define LEDMAT_ROW6_PIO PIO_DEFINE(PORT_B, 1) +#define LEDMAT_ROW7_PIO PIO_DEFINE(PORT_B, 0) +#define LEDMAT_ROWS_NUM 7 + + +/* Button. */ +#define BUTTON1 0 +#define BUTTON1_PIO PIO_DEFINE(PORT_D, 7) + + +/* Navswitch. */ +#define NAVSWITCH_PUSH_PIO LEDMAT_COL3_PIO +#define NAVSWITCH_EAST_PIO LEDMAT_COL1_PIO +#define NAVSWITCH_WEST_PIO LEDMAT_COL2_PIO +#define NAVSWITCH_NORTH_PIO LEDMAT_COL4_PIO +#define NAVSWITCH_SOUTH_PIO LEDMAT_COL5_PIO + + +/* LED (active high). */ +#define LED1 0 +#define LED1_PIO PIO_DEFINE(PORT_C, 2) + + +/* Infrared transmitter LED and receiver. */ +#define IR_TX_LOW_PIO PIO_DEFINE(PORT_D, 3) +#define IR_TX_HIGH_PIO PIO_DEFINE(PORT_D, 0) +#define IR_RX_PIO PIO_DEFINE(PORT_D, 2) + +#define IR_MODULATION_FREQ 36e3 + +void system_init (void); + +#endif diff --git a/drivers/avr/target.h b/drivers/avr/target.h new file mode 100644 index 0000000..a3e9df0 --- /dev/null +++ b/drivers/avr/target.h @@ -0,0 +1,73 @@ +/** @file system.h + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief System specific definitions +*/ +#ifndef SYSTEM_H +#define SYSTEM_H + +/* Data typedefs. */ +#include + +typedef uint8_t bool; + + +/* Useful macros. */ +#define BIT(X) (1 << (X)) + +#define ARRAY_SIZE(ARRAY) (sizeof (ARRAY) / sizeof (ARRAY[0])) + +#define __unused__ __attribute__ ((unused)) + + +/* Clock frequency Hz. */ +#define F_CPU 8000000 + + +/* LED matrix columns. */ +#define LEDMAT_COL1_PIO PIO_DEFINE(PORT_C, 6) +#define LEDMAT_COL2_PIO PIO_DEFINE(PORT_B, 7) +#define LEDMAT_COL3_PIO PIO_DEFINE(PORT_C, 4) +#define LEDMAT_COL4_PIO PIO_DEFINE(PORT_C, 7) +#define LEDMAT_COL5_PIO PIO_DEFINE(PORT_C, 5) +#define LEDMAT_COLS_NUM 5 + + +/* LED matrix rows. */ +#define LEDMAT_ROW1_PIO PIO_DEFINE(PORT_B, 6) +#define LEDMAT_ROW2_PIO PIO_DEFINE(PORT_B, 5) +#define LEDMAT_ROW3_PIO PIO_DEFINE(PORT_B, 4) +#define LEDMAT_ROW4_PIO PIO_DEFINE(PORT_B, 3) +#define LEDMAT_ROW5_PIO PIO_DEFINE(PORT_B, 2) +#define LEDMAT_ROW6_PIO PIO_DEFINE(PORT_B, 1) +#define LEDMAT_ROW7_PIO PIO_DEFINE(PORT_B, 0) +#define LEDMAT_ROWS_NUM 7 + + +/* Button. */ +#define BUTTON1 0 +#define BUTTON1_PIO PIO_DEFINE(PORT_D, 7) + + +/* Navswitch. */ +#define NAVSWITCH_PUSH_PIO LEDMAT_COL3_PIO +#define NAVSWITCH_EAST_PIO LEDMAT_COL1_PIO +#define NAVSWITCH_WEST_PIO LEDMAT_COL2_PIO +#define NAVSWITCH_NORTH_PIO LEDMAT_COL4_PIO +#define NAVSWITCH_SOUTH_PIO LEDMAT_COL5_PIO + + +/* LED (active high). */ +#define LED1 0 +#define LED1_PIO PIO_DEFINE(PORT_C, 2) + + +/* Infrared transmitter LED and receiver. */ +#define IR_TX_LOW_PIO PIO_DEFINE(PORT_D, 3) +#define IR_TX_HIGH_PIO PIO_DEFINE(PORT_D, 0) +#define IR_RX_PIO PIO_DEFINE(PORT_D, 2) + +#define IR_MODULATION_FREQ 36e3 + + +#endif diff --git a/drivers/avr/timer.c b/drivers/avr/timer.c new file mode 100644 index 0000000..e8bf725 --- /dev/null +++ b/drivers/avr/timer.c @@ -0,0 +1,74 @@ +/** @file timer.c + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief Timer module +*/ +#include +#include "timer.h" +#include "system.h" + +/* Timer/counter1 is a 16-bit counter with prescale options 1, 8, 64, + 256, and 1024. With F_CPU = 8e6 this corresponds to clock rates of + 8 MHz, 1 MHz, 125 kHz, 31.25 kHz, 7.8125 kHz. The counter will + roll over every 8.192 ms, 65.536 ms, 524.288 ms, 2.097s, and 8.389 s. */ + +#if TIMER_CLOCK_DIVISOR == 1 +#define TCCR1B_INIT 0x01 +#elif TIMER_CLOCK_DIVISOR == 8 +#define TCCR1B_INIT 0x02 +#elif TIMER_CLOCK_DIVISOR == 64 +#define TCCR1B_INIT 0x03 +#elif TIMER_CLOCK_DIVISOR == 256 +#define TCCR1B_INIT 0x04 +#elif TIMER_CLOCK_DIVISOR == 1024 +#define TCCR1B_INIT 0x05 +#else +#error Invalid TIMER_CLOCK_DIVISOR +#endif + + +/** Initialise timer. */ +void timer_init (void) +{ + /* Start timer in normal mode so that it rolls over at 65535 to 0. */ + TCCR1A = 0x00; + TCCR1B = TCCR1B_INIT; + TCNT1 = 0; +} + + +/** Get current time: + @return current time in ticks. */ +timer_tick_t timer_get (void) +{ + return TCNT1; +} + + +/** Wait until specified time: + @param when time to sleep until + @return current time. */ +timer_tick_t timer_wait_until (timer_tick_t when) +{ + while (1) + { + timer_tick_t diff; + timer_tick_t now; + + now = timer_get (); + + diff = now - when; + + if (diff < TIMER_OVERRUN_MAX) + return now; + } +} + + +/** Wait for specified period: + @param period how long to wait + @return current time. */ +timer_tick_t timer_wait (timer_tick_t period) +{ + return timer_wait_until (timer_get () + period); +} diff --git a/drivers/avr/timer.h b/drivers/avr/timer.h new file mode 100644 index 0000000..b01c1ff --- /dev/null +++ b/drivers/avr/timer.h @@ -0,0 +1,97 @@ +/** @file timer.h + @author M. P. Hayes, UCECE + @date 18 August 2011 + @brief Timer support. + + @defgroup timer Timer module + + This module provides simple timer support by abstracting a + hardware timer. + + The rate that the timer is incremented is specified by the + macro TIMER_CLOCK_DIVISOR in the header file system.h This + must be a value supported by the timer clock prescaler. + + The rate that the timer is incremented can be found using the + macro TIMER_RATE. + + The timer is free-running and will increment TIMER_RATE times + per second. When the timer reaches 65535 on the next increment + it rolls over to 0. + + Here's a simple example for turning an LED on for 0.5 second + and then off for 0.75 second. + + @code + #include "timer.h" + #include "led.h" + + void main (void) + { + timer_tick_t now; + + system_init (); + timer_init (); + led_init (); + + now = timer_get (); + while (1) + { + led_set (LED1, 1); + + now = timer_wait_until (now + (timer_tick_t)(TIMER_RATE * 0.5)); + + led_set (LED1, 0); + + now = timer_wait_until (now + (timer_tick_t)(TIMER_RATE * 0.75)); + } + } + @endcode +*/ +#ifndef TIMER_H +#define TIMER_H + +#include "system.h" + + +#ifndef TIMER_CLOCK_DIVISOR +#define TIMER_CLOCK_DIVISOR 256 +#endif + +/** Rate in Hz that the timer is incremented. */ +#define TIMER_RATE (F_CPU / TIMER_CLOCK_DIVISOR) + + +/** The maximum overrun (in ticks). */ +#define TIMER_OVERRUN_MAX 1000 + + +/** The maximum delay (in ticks). */ +#define TIMER_DELAY_MAX (65536u - TIMER_OVERRUN_MAX) + + +/** Define timer ticks. */ +typedef uint16_t timer_tick_t; + + +/** Get current time: + @return current time in ticks. */ +timer_tick_t timer_get (void); + + +/** Wait until specified time: + @param when time to sleep until + @return current time. */ +timer_tick_t timer_wait_until (timer_tick_t when); + + +/** Wait for specified period: + @param period how long to wait + @return current time. */ +timer_tick_t timer_wait (timer_tick_t period); + + +/** Initialise timer. */ +void timer_init (void); + +#endif /* TIMER_H */ diff --git a/drivers/avr/timer0.c b/drivers/avr/timer0.c new file mode 100644 index 0000000..898f4c0 --- /dev/null +++ b/drivers/avr/timer0.c @@ -0,0 +1,182 @@ +/** @file timer0.c + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief +*/ +#include "system.h" +#include "timer0.h" +#include "bits.h" +#include "prescale.h" +#include + +/* Timer/Counter0 is a general purpose, single channel, 8-bit + Timer/Counter module. + + It has two output compare registers and two corresponding outputs: + OC0A and OCOB. However, the counter can only be reset by the A + output compure register, OCRA. + + TODO: rewrite driver to provide standard modes, such as: + * generate square wave on OCOA + * generate square wave on OCOB + * generate PWM on OCOA + * generate PWM on OCOB + * programmable timer (for delays) + + */ + + +/* Prescale values: + 1, 8, 64, 256, or 1024. + + 2^0, 2^3, 2^6, 2^8, 2^10. */ + +static const uint8_t log_prescales[] = {0, 3, 6, 8, 10}; +static uint8_t prescale_bits = 1; + + + +static inline void +timer0_prescaler_set (uint8_t value) +{ + BITS_INSERT (TCCR0B, value, CS00, CS02); +} + + +static inline uint8_t +timer0_prescaler_get (void) +{ + return BITS_EXTRACT (TCCR0B, CS00, CS02); +} + + +static inline bool +timer0_running_p (void) +{ + return timer0_prescaler_get () != 0; +} + + +/* Set the desired period in terms of clocks. The closest + suitable period is returned. */ +uint16_t timer0_period_set (uint16_t period) +{ + uint8_t counter; + uint8_t index; + + index = prescale_select (period, log_prescales, + ARRAY_SIZE (log_prescales), &counter); + + prescale_bits = index + 1; + + + /* F = F_CPU / (2 * prescale * (1 + OCR0A)) + Maximum frequency of F_CPU / 2 when OCR0A = 0. */ + OCR0A = counter - 1; + OCR0B = 0; + + /* A prescale value of 0 selects no clock source and is used to + stop the timer. The cheapskates haven't provided a start/stop + bit so we cannot set the prescaler if the timer is stopped. */ + if (timer0_running_p ()) + timer0_prescaler_set (prescale_bits); + + /* Return the quantised period. */ + return counter << log_prescales[index]; +} + + +void +timer0_start (void) +{ + /* Clear overflow flags. */ + TIFR0 &= ~(BIT (OCF0A) | BIT (OCF0B)); + + /* Enable timer by supplying a clock from the prescaler. */ + timer0_prescaler_set (prescale_bits); +} + + +void +timer0_stop (void) +{ + /* Disable clock source. */ + timer0_prescaler_set (0); +} + + +bool +timer0_compare_p (void) +{ + return (TIFR0 & BIT (OCF0B)) != 0; +} + + +void timer0_output_set (timer0_output_t output, timer0_output_mode_t mode) +{ + switch (output) + { + case TIMER0_OUTPUT_A: + /* Select output mode for 0C0A. + The port pin must also be configured as an output. */ + BITS_INSERT (TCCR0A, mode, COM0A0, COM0A1); + break; + + case TIMER0_OUTPUT_B: + /* Select output mode for 0C0B. + The port pin must also be configured as an output. */ + BITS_INSERT (TCCR0A, mode, COM0B0, COM0B1); + break; + + default: + break; + } +} + + +int8_t +timer0_mode_set (timer0_mode_t mode) +{ + switch (mode) + { + case TIMER0_MODE_NORMAL: + /* Normal mode---counter not reset. It rolls over when it + gets to 0xFF and thus there is limited frequency control + for waveform generation. */ + TCCR0A &= ~(BIT (WGM01) | BIT (WGM00)); + + /* Need WGM02 bit set to 0 (default) in TCCR0B. */ + break; + + case TIMER0_MODE_CTC: + /* Set CTC mode. Clear timer in compare mode---counter reset + on match with OCRA. */ + TCCR0A &= ~BIT (WGM00); + TCCR0A |= BIT (WGM01); + + /* Need WGM02 bit set to 0 (default) in TCCR0B. */ + break; + + default: + return 0; + } + + return 1; +} + + +int8_t +timer0_init (void) +{ + /* Set to normal mode, clock source disabled, output compare match + to not trigger OC pin. */ + TCCR0A = 0; + TCCR0B = 0; + TCNT0 = 0; + + OCR0B = 0; + + timer0_mode_set (TIMER0_MODE_CTC); + + return 1; +} diff --git a/drivers/avr/timer0.h b/drivers/avr/timer0.h new file mode 100644 index 0000000..a7f23ed --- /dev/null +++ b/drivers/avr/timer0.h @@ -0,0 +1,48 @@ +/** @file timer0.h + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief +*/ +#ifndef TIMER0_H +#define TIMER0_H + +#include "system.h" + + +#define TIMER0_DIVISOR(FREQ) ((uint16_t) (F_CPU / (FREQ))) + + +typedef enum {TIMER0_MODE_NORMAL, + TIMER0_MODE_CTC, + TIMER0_MODE_PWM} timer0_mode_t; + +typedef enum {TIMER0_OUTPUT_A, + TIMER0_OUTPUT_B} timer0_output_t; + +typedef enum {TIMER0_OUTPUT_MODE_DISABLE, + TIMER0_OUTPUT_MODE_TOGGLE, + TIMER0_OUTPUT_MODE_CLEAR, + TIMER0_OUTPUT_MODE_SET} timer0_output_mode_t; + + +uint16_t timer0_period_set (uint16_t period); + + +void timer0_start (void); + + +void timer0_stop (void); + + +bool timer0_compare_p (void); + + +void timer0_output_set (timer0_output_t output, timer0_output_mode_t mode); + + +int8_t timer0_mode_set (timer0_mode_t mode); + + +int8_t timer0_init (void); + +#endif diff --git a/drivers/avr/usart1.c b/drivers/avr/usart1.c new file mode 100644 index 0000000..005ba72 --- /dev/null +++ b/drivers/avr/usart1.c @@ -0,0 +1,98 @@ +/** @file usart1.c + @author Michael Hayes + @date 10 March 2005 + + @brief Routines for interfacing with the usart1 on an Atmega32u2 +*/ +#include "system.h" +#include "usart1.h" +#include + +void +usart1_baud_divisor_set (uint16_t divisor) +{ + UBRR1H = (divisor - 1) >> 8; + UBRR1L = (divisor - 1) & 0xFF; +} + + +uint8_t +usart1_init (const usart1_cfg_t *cfg) +{ + uint8_t bits; + + usart1_baud_divisor_set (cfg->baud_divisor); + + /* Default to 8 bits. */ + bits = cfg->bits; + if (bits < 5 || bits > 8) + bits = 8; + + /* Enable receiver and transmitter. */ + UCSR1B = BIT (RXEN1) | BIT (TXEN1); + + /* Set frame format: asynchronous USART, 1 stop bit, no parity. */ + UCSR1C = ((bits - 5) << UCSZ10) | (0 << UPM10); + + return 1; +} + + +/* Return non-zero if there is a character ready to be read. */ +bool +usart1_read_ready_p (void) +{ + return (UCSR1A & BIT (RXC1)) != 0; +} + + +/* Return non-zero if a character can be written without blocking. */ +bool +usart1_write_ready_p (void) +{ + return (UCSR1A & BIT (UDRE1)) != 0; +} + + +/* Return non-zero if transmitter finished. */ +bool +usart1_write_finished_p (void) +{ + return (UCSR1A & BIT (TXC1)) != 0; +} + + +/* Write character to USART1. */ +int8_t +usart1_putc (char ch) +{ + if (ch == '\n') + usart1_putc ('\r'); + + while (!usart1_write_ready_p ()) + continue; + + UDR1 = ch; + return 1; +} + + +/* Read character from USART1. This blocks until a character is read. */ +int8_t +usart1_getc (void) +{ + /* Wait for something in receive buffer. */ + while (!usart1_read_ready_p ()) + continue; + + return UDR1; +} + + +/* Write string to USART1. This blocks until the string is written. */ +void +usart1_puts (const char *str) +{ + while (*str) + usart1_putc (*str++); +} diff --git a/drivers/avr/usart1.h b/drivers/avr/usart1.h new file mode 100644 index 0000000..6eb14ff --- /dev/null +++ b/drivers/avr/usart1.h @@ -0,0 +1,60 @@ +/** @file usart1.h + @author Michael Hayes + @date 10 December 2004 + + @brief Routines for interfacing with the usart1 on an Atmega32u2 +*/ + +#ifndef USART1_H +#define USART1_H + +#include "system.h" + +typedef struct usart1_cfg_struct +{ + uint16_t baud_divisor; + uint8_t bits; +} usart1_cfg_t; + + + +#define USART1_BAUD_DIVISOR(BAUD_RATE) ((F_CPU / 16) / (BAUD_RATE)) + + +/* Return non-zero if there is a character ready to be read. */ +bool +usart1_read_ready_p (void); + + +/* Read character from USART1. This blocks if nothing is available to + read. */ +int8_t +usart1_getc (void); + + +/* Return non-zero if a character can be written without blocking. */ +bool +usart1_write_ready_p (void); + + +/* Return non-zero if transmitter finished. */ +bool +usart1_write_finished_p (void); + + +/* Write character to USART1. This returns zero if the character + could not be written. */ +int8_t +usart1_putc (char ch); + + +/* Write string to USART1. */ +void +usart1_puts (const char *str); + + +/* Initialise usart1 and set baud rate. */ +uint8_t +usart1_init (const usart1_cfg_t *cfg); + +#endif diff --git a/drivers/button.c b/drivers/button.c new file mode 100644 index 0000000..d12d8fd --- /dev/null +++ b/drivers/button.c @@ -0,0 +1,123 @@ +/** @file button.c + @author M.P. Hayes + @date 28 Aug 2008 + @brief Button polling (no debouncing). + @note This polls pushbutton switches but does not do debouncing. +*/ + +#include "system.h" +#include "button.h" +#include "pio.h" + + +/* Button configuration structure. */ +typedef struct +{ + pio_t pio; + /* True for active high, false for active low. */ + bool active_high; +} button_cfg_t; + + +/** Button state structure. */ +typedef struct +{ + bool current; + bool previous; +} button_state_t; + + +/* Define buttons. */ +static const button_cfg_t buttons_cfg[] = +{ + {.pio = BUTTON1_PIO, .active_high = 1}, + /* Add config data for additional buttons here. */ +}; + +#define BUTTONS_NUM ARRAY_SIZE (buttons_cfg) + +static button_state_t buttons_state[BUTTONS_NUM]; + + +/** Return true if button state changed from up to down since + last call to button_update + @param button index of button to select + @return 1 if button changed from up to down otherwise 0 */ +bool +button_push_event_p (uint8_t button) +{ + if (button >= BUTTONS_NUM) + return 0; + return buttons_state[button].current && !buttons_state[button].previous; +} + + +/** Return true if button state changed from down to up since + last call to button_update + @param button index of button to select + @return 1 if button changed from down to up otherwise 0 */ +bool +button_release_event_p (uint8_t button) +{ + if (button >= BUTTONS_NUM) + return 0; + return !buttons_state[button].current && buttons_state[button].previous; +} + + +/** Return true if button down (pushed). + @param button index of button to select + @return 1 if button down otherwise 0 */ +bool +button_down_p (uint8_t button) +{ + if (button >= BUTTONS_NUM) + return 0; + return buttons_state[button].current; +} + + +/** Return true if button up (released). + @param button index of button to select + @return 1 if button down otherwise 0 */ +bool +button_up_p (uint8_t button) +{ + if (button >= BUTTONS_NUM) + return 0; + return !buttons_state[button].current; +} + + +/** Poll all the buttons and update their state. */ +void +button_update (void) +{ + uint8_t i; + + for (i = 0; i < BUTTONS_NUM; i++) + { + buttons_state[i].previous = buttons_state[i].current; + + buttons_state[i].current = ! (pio_input_get (buttons_cfg[i].pio) + ^ buttons_cfg[i].active_high); + } +} + + +/** Initialise button driver. */ +void button_init (void) +{ + uint8_t i; + + for (i = 0; i < BUTTONS_NUM; i++) + { + /* Configure pio for input and enable internal pullup resistor + if active low. If button is active high it is assumed + there is an external pulldon resistor. */ + pio_config_set (buttons_cfg[i].pio, + buttons_cfg[i].active_high ? PIO_INPUT : PIO_PULLUP); + } +} + + diff --git a/drivers/button.h b/drivers/button.h new file mode 100644 index 0000000..c93fc9d --- /dev/null +++ b/drivers/button.h @@ -0,0 +1,74 @@ +/** @file button.h + @author M. P. Hayes, UCECE + @date 15 Feb 2003 + @brief Button polling (no debouncing). + + @defgroup button Pushbutton driver + + This module implements a simple pushbutton driver. Note, no + debouncing is performed. + + Here's an example application where the buttons are polled at 50 Hz: + + @code + #include "pacer.h" + #include "button.h" + + void main (void) + { + system_init (); + button_init (); + pacer_init (50); + + while (1) + { + pacer_wait (); + button_update (); + + if (button_push_event_p (0)) + // Do something + } + } + @endcode +*/ + + +#ifndef BUTTON_H +#define BUTTON_H + +#include "system.h" + + +/** Poll all the buttons and update their state. */ +void button_update (void); + + +/** Return true if button state changed from up to down since + last call to button_update + @param button index of button to select + @return 1 if button changed from up to down otherwise 0 */ +bool button_push_event_p (uint8_t button); + + +/** Return true if button state changed from down to up since + last call to button_update + @param button index of button to select + @return 1 if button changed from down to up otherwise 0 */ +bool button_release_event_p (uint8_t button); + + +/** Return true if button down (pushed). + @param button index of button to select + @return 1 if button down otherwise 0 */ +bool button_down_p (uint8_t button); + + +/** Return true if button up (released). + @param button index of button to select + @return 1 if button down otherwise 0 */ +bool button_up_p (uint8_t button); + + +/** Initialise button driver. */ +void button_init (void); +#endif diff --git a/drivers/display.c b/drivers/display.c new file mode 100644 index 0000000..2bf2324 --- /dev/null +++ b/drivers/display.c @@ -0,0 +1,101 @@ +/** @file display.c + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief Bit-mapped display driver. +*/ +#include "system.h" +#include "ledmat.h" +#include "display.h" + +/** The state of the display (frame buffer). */ +static uint8_t display[DISPLAY_WIDTH]; + + +/** Set state of a display pixel. + @param col pixel column + @param row pixel row + @param val pixel state. */ +void display_pixel_set (uint8_t col, uint8_t row, bool val) +{ + uint8_t bitmask; + uint8_t pattern; + + if (col >= DISPLAY_WIDTH || row >= DISPLAY_HEIGHT) + return; + + bitmask = BIT (row); + pattern = display[col] & ~bitmask; + + if (val) + pattern |= bitmask; + + display[col] = pattern; +} + + +/** Get state of a display pixel. + @param col pixel column + @param row pixel row + @return pixel state or zero if outside display. */ +bool display_pixel_get (uint8_t col, uint8_t row) +{ + uint8_t bitmask; + + if (col >= DISPLAY_WIDTH || row >= DISPLAY_HEIGHT) + return 0; + + bitmask = BIT (row); + + return (display[col] & bitmask) != 0; +} + + +/** Scroll display contents one column to left. */ +void display_scroll_left (void) +{ + int col; + + for (col = 0; col < DISPLAY_WIDTH - 1; col++) + display[col] = display[col + 1]; +} + + +/** Scroll display contents one row down. */ +void display_scroll_down (void) +{ + int col; + + for (col = 0; col < DISPLAY_WIDTH; col++) + display[col] <<= 1; +} + + +/** Update display (perform refreshing). */ +void display_update (void) +{ + static uint8_t col = 0; + + ledmat_display_column (display[col], col); + + col++; + if (col >= DISPLAY_WIDTH) + col = 0; +} + + +/** Clear display. */ +void display_clear (void) +{ + int col; + + for (col = 0; col < DISPLAY_WIDTH; col++) + display[col] = 0; +} + + +/** Initialise display. */ +void display_init (void) +{ + ledmat_init (); + display_clear (); +} diff --git a/drivers/display.h b/drivers/display.h new file mode 100644 index 0000000..81c89c2 --- /dev/null +++ b/drivers/display.h @@ -0,0 +1,52 @@ +/** @file display.h + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief Bit-mapped display driver. + + @defgroup display Bit-mapped display driver + + This module implements a simple display frame-buffer. It abstracts the + multiplexing of a LED matrix display. +*/ +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "system.h" + +#define DISPLAY_WIDTH LEDMAT_COLS_NUM +#define DISPLAY_HEIGHT LEDMAT_ROWS_NUM + +/** Set state of a display pixel. + @param col pixel column (0 left) + @param row pixel row (0 top) + @param val pixel state. */ +void display_pixel_set (uint8_t col, uint8_t row, bool val); + + +/** Get state of a display pixel. + @param col pixel column (0 left) + @param row pixel row (0 top) + @return pixel state or zero if outside display. */ +bool display_pixel_get (uint8_t col, uint8_t row); + + +/** Scroll display contents one column to left. */ +void display_scroll_left (void); + + +/** Scroll display contents one row down. */ +void display_scroll_down (void); + + +/** Update display (perform refreshing). */ +void display_update (void); + + +/** Clear display. */ +void display_clear (void); + + +/** Initialise display. */ +void display_init (void); + +#endif diff --git a/drivers/ir.c b/drivers/ir.c new file mode 100644 index 0000000..8c265da --- /dev/null +++ b/drivers/ir.c @@ -0,0 +1,95 @@ +/** @file ir.c + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief Infrared driver. + @note This should be compiled with optimisation enabled to make the + modulation frequency more accurate. +*/ +#include "system.h" +#include "delay.h" +#include "ir.h" +#include "pio.h" + +#define IR_MODULATION_PERIOD_US (1e6 / IR_MODULATION_FREQ) + +#define IR_SET_TWEAK_US 0.2 +#define LOOP_TWEAK_US 0.8 + + +/** Turn IR transmitter LED on. */ +static inline void ir_tx_on (void) +{ + pio_output_high (IR_TX_HIGH_PIO); +} + + +/** Turn IR transmitter LED off. */ +static inline void ir_tx_off (void) +{ + pio_output_low (IR_TX_HIGH_PIO); +} + + +/** Modulate the IR transmitter LED at IR_MODULATION_FREQ. + @param state is 1 to enable modulation, 0 to disable modulation + @param count is the number of modulation periods + @note This returns after @c count modulation periods. */ +void ir_tx_set (uint8_t state, uint16_t count) +{ + uint16_t i = 0; + + /* This modulates the IR LED by bit bashing the IR LED PIO. + Alternatively, we could use the timer0 peripheral for a more + accurate modulation frequency. + + The timing could be made more accurate by tweaking the delays + to compensate for the other instructions. */ + + if (state) + { + for (i = 0; i < count; i++) + { + ir_tx_on (); + DELAY_US (IR_MODULATION_PERIOD_US / 2 - IR_SET_TWEAK_US); + ir_tx_off (); + DELAY_US (IR_MODULATION_PERIOD_US / 2 - IR_SET_TWEAK_US - LOOP_TWEAK_US); + } + } + else + { + for (i = 0; i < count; i++) + { + DELAY_US (IR_MODULATION_PERIOD_US - LOOP_TWEAK_US); + } + } +} + + +/** Return output state of IR receiver. + @return IR receiver state (1 = IR modulation detected). */ +uint8_t ir_rx_get (void) +{ + /* The output of the IR receiver (TSOP6136TR) is inverted. It is + normally high but goes low when it detects modulated IR light. */ + + return !pio_input_get (IR_RX_PIO); +} + + +/** Initialise PIOs for IR transmitter LED and receiver. */ +void ir_init (void) +{ + /* Configure transmitter PIOs as outputs and ensure IR LED off. + Note that the IR LED is driven by two PIOs; to turn on the LED + IR_TX_HIGH_PIO must be high and IR_TX_LOW_PIO must be low. + IR_TX_HIGH_PIO is also connected to the timer 0 output so + we can modulate the LED by the timer. IR_TX_LOW_PIO is also + connected to the UART TXD output so we can drive it from the UART. */ + + pio_config_set (IR_TX_HIGH_PIO, PIO_OUTPUT_LOW); + pio_config_set (IR_TX_LOW_PIO, PIO_OUTPUT_LOW); + + /* Configure receiver PIO as input. IR_RX_PIO is also connected to the + UART RXD input. */ + pio_config_set (IR_RX_PIO, PIO_INPUT); +} diff --git a/drivers/ir.h b/drivers/ir.h new file mode 100644 index 0000000..c2c5f7e --- /dev/null +++ b/drivers/ir.h @@ -0,0 +1,38 @@ +/** @file ir.h + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief Infrared driver. + + @defgroup IR Infrared (IR) driver + + This module implements a simple hardware abstraction of an IR LED and + IR receiver. +*/ + +#ifndef IR_H +#define IR_H + +#include "system.h" + +#ifndef IR_MODULATION_FREQ +/** The nominal modulation frequency. This can be defined in system.h. */ +#define IR_MODULATION_FREQ 36e3 +#endif + + +/** Modulate the IR transmitter LED at IR_MODULATION_FREQ. + @param state is 1 to enable modulation, 0 to disable modulation + @param count is the number of modulation periods + @note This returns after @c count modulation periods. */ +void ir_tx_set (uint8_t state, uint16_t count); + + +/** Return output state of IR receiver. + @return IR receiver state (1 = IR modulation detected). */ +uint8_t ir_rx_get (void); + + +/* Initialise PIOs for IR transmitter LED and receiver. */ +void ir_init (void); + +#endif diff --git a/drivers/ir_serial.c b/drivers/ir_serial.c new file mode 100644 index 0000000..963c7d3 --- /dev/null +++ b/drivers/ir_serial.c @@ -0,0 +1,152 @@ +/** @file ir_serial.c + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief Infrared serial driver. +*/ + +/* This should probably be compiled with optimisation enabled + otherwise some of the timing may be dodgy. + For documentation of the protocol see ir_serial.h */ + + +#include "ir_serial.h" +#include "delay.h" + +#define IR_SERIAL_DIT_PERIOD 0.6e-3 +#define IR_SERIAL_MODULATIONS_PER_DIT ((int)(IR_SERIAL_DIT_PERIOD * IR_MODULATION_FREQ)) +#define IR_SERIAL_MODULATIONS_PER_START (IR_SERIAL_MODULATIONS_PER_DIT * 4) +#define IR_SERIAL_MODULATIONS_PER_ONE (IR_SERIAL_MODULATIONS_PER_DIT * 2) +#define IR_SERIAL_MODULATIONS_PER_ZERO (IR_SERIAL_MODULATIONS_PER_DIT) +#define IR_SERIAL_MODULATIONS_PER_BREAK (IR_SERIAL_MODULATIONS_PER_DIT) + +#define IR_SERIAL_DELAY_US 10 +#define IR_SERIAL_DIT_COUNT (1e6 * IR_SERIAL_DIT_PERIOD / IR_SERIAL_DELAY_US) +#define IR_SERIAL_START_COUNT_MAX ((int)(4.5 * IR_SERIAL_DIT_COUNT)) +#define IR_SERIAL_ONE_COUNT_MAX ((int)(2.5 * IR_SERIAL_DIT_COUNT)) +#define IR_SERIAL_ZERO_COUNT_MAX ((int)(1.25 * IR_SERIAL_DIT_COUNT)) +#define IR_SERIAL_BREAK_COUNT_MAX ((int)(1.5 * IR_SERIAL_DIT_COUNT)) + + +/** Transmit a start code. */ +static inline void ir_serial_tx_start (void) +{ + ir_tx_set (1, IR_SERIAL_MODULATIONS_PER_START); + ir_tx_set (0, IR_SERIAL_MODULATIONS_PER_BREAK); +} + + +/** Transmit a data bit. */ +static inline void ir_serial_tx_bit (uint8_t state) +{ + ir_tx_set (1, state ? IR_SERIAL_MODULATIONS_PER_ONE + : IR_SERIAL_MODULATIONS_PER_ZERO); + ir_tx_set (0, IR_SERIAL_MODULATIONS_PER_BREAK); +} + + +/** Transmit a stop code. */ +static inline void ir_serial_tx_stop (void) +{ + ir_tx_set (0, IR_SERIAL_MODULATIONS_PER_BREAK); +} + + +/** Transmit 8 bits of data over IR serial link. + @param data byte to transmit + @note No error checking is performed. This function blocks until + the frame is transmitted. */ +void ir_serial_transmit (uint8_t data) +{ + int i; + + /* Send start code. */ + ir_serial_tx_start (); + + /* Eight data bits LSB first. */ + for (i = 0; i < 8; i++) + { + ir_serial_tx_bit (data & 1); + data >>= 1; + } + + /* Send stop code. */ + ir_serial_tx_stop (); +} + + +/** Receive 8 bits of data over IR serial link. + @param pdata pointer to byte to store received data + @return status code + @note No error checking is performed. If there is no activity on the + IR serial link, this function returns immediately. Otherwise, this + function blocks until the entire frame is received. */ +ir_serial_ret_t ir_serial_receive (uint8_t *pdata) +{ + int i; + int count; + uint8_t data; + bool data_err; + + /* Check for start code; if not present return. */ + if (!ir_rx_get ()) + return IR_SERIAL_NONE; + + /* Wait for end of start code or timeout. */ + for (count = 0; ir_rx_get (); count++) + { + if (count >= IR_SERIAL_START_COUNT_MAX) + return IR_SERIAL_START_ERR; + + DELAY_US (IR_SERIAL_DELAY_US); + } + + /* We may have received a rogue short pulse but we may have been + called closed to the falling transition. */ + + data_err = 0; + data = 0; + + /* Eight data bits LSB first. */ + for (i = 0; i < 8; i++) + { + data >>= 1; + + /* Wait for IR modulation to start or timeout (indicating + detection of a false start code). */ + for (count = 0; !ir_rx_get (); count++) + { + if (count >= IR_SERIAL_BREAK_COUNT_MAX) + return IR_SERIAL_BREAK_ERR; + + DELAY_US (IR_SERIAL_DELAY_US); + } + + /* If there is another transmission in this slot we will get + too small a value for dit_count. This is likely to trigger + a bit error. */ + for (count = 0; count < IR_SERIAL_ONE_COUNT_MAX + && ir_rx_get (); count++) + { + DELAY_US (IR_SERIAL_DELAY_US); + } + + if (count >= IR_SERIAL_ONE_COUNT_MAX) + data_err = 1; + + if (count >= IR_SERIAL_ZERO_COUNT_MAX) + data |= 0x80; + } + + /* Perhaps should check for stop code. If not found, there is + likely to have been interference from another transmitter. */ + + *pdata = data; + return data_err ? IR_SERIAL_DATA_ERR : IR_SERIAL_OK; +} + + +/** Initialise IR serial driver. */ +void ir_serial_init (void) +{ + ir_init (); +} diff --git a/drivers/ir_serial.h b/drivers/ir_serial.h new file mode 100644 index 0000000..125a407 --- /dev/null +++ b/drivers/ir_serial.h @@ -0,0 +1,117 @@ +/** @file ir_serial.h + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief Infrared serial driver. + + @defgroup IR_serial Infrared serial driver + + This module implements a simple serial protocol over an IR link. + It uses a pulse width modulation technique similar to the Sony + SIRC protocol. A message consists of a start code, 8 data bits + (transmitted LSB first), and a stop code. Each code is comprised + of 0.6 ms intervals called a dit: + + Start bit: four dits on, one dit off + + One bit: two dits on, one dit off + + Zero bit: one dit on, one dit off + + Stop bit: one dit off + + Note, the duration of a message depends on the number of non-zero + bits in the data byte. The shortest message duration is 22 dits + (13.2 ms) and the longest message is 30 dits (18 ms). No error + detection or correction is performed apart from checking of + invalid codes. + + Here's a simple application that transmits a data byte of value 7. + + @code + #include "ir_serial.h" + + void main (void) + { + system_init (); + ir_serial_init (); + + ir_serial_transmit (7); + + while (1) + { + } + } + @endcode + + Here's another simple application that receives a data byte. + + @code + #include "ir_serial.h" + #include "pacer.h" + + void main (void) + { + system_init (); + ir_serial_init (); + + pacer_init (1000); + + while (1) + { + ir_serial_ret_t ret; + + pacer_wait (); + + ret = ir_serial_receive (&data); + if (ret == IR_SERIAL_OK) + { + // Process the received byte. + } + } + } + @endcode +*/ + +#ifndef IR_SERIAL_H +#define IR_SERIAL_H + +#include "ir.h" + +/** Status return codes. */ +typedef enum ir_serial_ret +{ + /** A valid frame has been received. */ + IR_SERIAL_OK = 1, + /** No data to read. */ + IR_SERIAL_NONE = 0, + /** Invalid start code detected. */ + IR_SERIAL_START_ERR = -1, + /** Invalid data code detected. */ + IR_SERIAL_DATA_ERR = -2, + /** Invalid break code detected. */ + IR_SERIAL_BREAK_ERR = -3, + /** Invalid stop code detected. */ + IR_SERIAL_STOP_ERR = -4 +} ir_serial_ret_t; + + +/** Transmit 8 bits of data over IR serial link. + @param data byte to transmit + @note No error checking is performed. This function blocks until + the frame is transmitted. */ +void ir_serial_transmit (uint8_t data); + + +/** Receive 8 bits of data over IR serial link. + @param pdata pointer to byte to store received data + @return status code + @note No error checking is performed. If there is no activity on the + IR serial link, this function returns immediately. Otherwise, this + function blocks until the entire frame is received. */ +ir_serial_ret_t ir_serial_receive (uint8_t *pdata); + + +/** Initialise IR serial driver. */ +void ir_serial_init (void); + +#endif diff --git a/drivers/led.c b/drivers/led.c new file mode 100644 index 0000000..64e3fd3 --- /dev/null +++ b/drivers/led.c @@ -0,0 +1,50 @@ +/** @file led.c + @author M. P. Hayes, UCECE + @date 15 Feb 2003 + @brief LED driver. +*/ + +#include "system.h" +#include "pio.h" +#include "led.h" + + +typedef struct +{ + pio_t pio; + /* True for active high, false for active low. */ + bool active_high; +} led_cfg_t; + + +/* Define LEDs. */ +static const led_cfg_t leds_cfg[] = +{ + {.pio = LED1_PIO, .active_high = 1}, + /* Add config data for additional LEDs here. */ +}; + +#define LEDS_NUM ARRAY_SIZE (leds_cfg) + + +void led_set (uint8_t led, bool state) +{ + if (led >= LEDS_NUM) + return; + pio_output_set (leds_cfg[led].pio, + leds_cfg[led].active_high ? state : !state); +} + + +/** Initialise LED driver. */ +void led_init (void) +{ + uint8_t i; + + for (i = 0; i < LEDS_NUM; i++) + { + pio_config_set (leds_cfg[i].pio, + leds_cfg[i].active_high ? PIO_OUTPUT_HIGH + : PIO_OUTPUT_LOW); + } +} diff --git a/drivers/led.h b/drivers/led.h new file mode 100644 index 0000000..fcd07be --- /dev/null +++ b/drivers/led.h @@ -0,0 +1,44 @@ +/** @file led.h + @author M. P. Hayes, UCECE + @date 15 Feb 2003 + @brief LED driver. + + @defgroup LED LED driver + + This module implements a simple LED driver. + + Here's an example application that turns on a LED: + + @code + #include "led.h" + + void main (void) + { + system_init (); + led_init (); + + led_set (LED1, 1); + + while (1) + { + } + } + @endcode +*/ + +#ifndef LED_H +#define LED_H + +#include "system.h" + + +/** Set LED to desired state. + @param led LED to select + @param state desired state (non-zero for on). */ +void led_set (uint8_t led, bool state); + + +/** Initialise LED driver. */ +void led_init (void); + +#endif diff --git a/drivers/ledmat.c b/drivers/ledmat.c new file mode 100644 index 0000000..7879d9b --- /dev/null +++ b/drivers/ledmat.c @@ -0,0 +1,78 @@ +/** @file ledmat.c + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief LED matrix driver. +*/ +#include "system.h" +#include "pio.h" +#include "ledmat.h" + + +/** Define PIO pins driving LED matrix rows. */ +static const pio_t ledmat_rows[] = +{ + LEDMAT_ROW1_PIO, LEDMAT_ROW2_PIO, LEDMAT_ROW3_PIO, + LEDMAT_ROW4_PIO, LEDMAT_ROW5_PIO, LEDMAT_ROW6_PIO, + LEDMAT_ROW7_PIO +}; + + +/** Define PIO pins driving LED matrix columns. */ +static const pio_t ledmat_cols[] = +{ + LEDMAT_COL1_PIO, LEDMAT_COL2_PIO, LEDMAT_COL3_PIO, + LEDMAT_COL4_PIO, LEDMAT_COL5_PIO +}; + + +/** Initialise PIO pins to drive LED matrix. */ +void ledmat_init (void) +{ + uint8_t row; + uint8_t col; + + for (row = 0; row < LEDMAT_ROWS_NUM; row++) + { + /* The rows are active low so configure PIO as an initially + high output. */ + pio_config_set (ledmat_rows[row], PIO_OUTPUT_HIGH); + } + + for (col = 0; col < LEDMAT_COLS_NUM; col++) + { + /* The columns are active low so configure PIO as an initially + high output. */ + pio_config_set (ledmat_cols[col], PIO_OUTPUT_HIGH); + } +} + + +/** Display pattern on specified column. + @param pattern bit pattern to display for selected column + @param col selected column. */ +void ledmat_display_column (uint8_t pattern, uint8_t col) +{ + static uint8_t col_prev = 0; + uint8_t row; + + /* Disable previous column to prevent ghosting while rows modified. */ + pio_output_high (ledmat_cols[col_prev]); + + /* Activate desired rows based on desired pattern. */ + for (row = 0; row < LEDMAT_ROWS_NUM; row++) + { + /* The rows are active low. */ + if (pattern & 1) + pio_output_low (ledmat_rows[row]); + else + pio_output_high (ledmat_rows[row]); + + pattern >>= 1; + } + + /* Enable new column. */ + pio_output_low (ledmat_cols[col]); + col_prev = col; +} + + diff --git a/drivers/ledmat.h b/drivers/ledmat.h new file mode 100644 index 0000000..cd838e6 --- /dev/null +++ b/drivers/ledmat.h @@ -0,0 +1,25 @@ +/** @file ledmat.h + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief LED matrix driver. + + @defgroup ledmat LED matrix driver + + This module implements a simple hardware abstraction of a LED matrix. +*/ +#ifndef LEDMAT_H +#define LEDMAT_H + +#include "system.h" + +/** Initialise PIO pins to drive LED matrix. */ +void ledmat_init (void); + + +/** Display pattern on specified column. + @param pattern bit pattern to display for selected column + @param col selected column. */ +void ledmat_display_column (uint8_t pattern, uint8_t col); + + +#endif diff --git a/drivers/navswitch.c b/drivers/navswitch.c new file mode 100644 index 0000000..2780cf7 --- /dev/null +++ b/drivers/navswitch.c @@ -0,0 +1,140 @@ +/** @file navswitch.c + @author M.P. Hayes + @date 28 Aug 2008 + @brief Navswitch driver. + @note This polls the 5-way navigation switch + but does not do debouncing. +*/ + +#include "system.h" +#include "navswitch.h" +#include "delay.h" +#include "pio.h" + + +/* Navswitch configuration structure. */ +typedef struct +{ + pio_t pio; +} navswitch_cfg_t; + + +/** Navswitch state structure. */ +typedef struct +{ + bool current; + bool previous; +} navswitch_state_t; + + +/* Define navswitch PIO connections. */ +static const navswitch_cfg_t navswitch_cfg[] = +{ + {NAVSWITCH_NORTH_PIO}, + {NAVSWITCH_EAST_PIO}, + {NAVSWITCH_SOUTH_PIO}, + {NAVSWITCH_WEST_PIO}, + {NAVSWITCH_PUSH_PIO} +}; + +#define NAVSWITCH_NUM ARRAY_SIZE (navswitch_cfg) + +static navswitch_state_t navswitch_state[NAVSWITCH_NUM]; + + +/** Return true if navswitch state changed from up to down since + last call to navswitch_update + @param navswitch index of navswitch to select + @return 1 if navswitch changed from up to down otherwise 0 */ +bool +navswitch_push_event_p (uint8_t navswitch) +{ + return navswitch_state[navswitch].current + && !navswitch_state[navswitch].previous; +} + + +/** Return true if navswitch state changed from down to up since + last call to navswitch_update + @param navswitch index of navswitch to select + @return 1 if navswitch changed from down to up otherwise 0 */ +bool +navswitch_release_event_p (uint8_t navswitch) +{ + return !navswitch_state[navswitch].current + && navswitch_state[navswitch].previous; +} + + +/** Return true if navswitch down (pushed). + @param navswitch index of navswitch to select + @return 1 if navswitch down otherwise 0 */ +bool +navswitch_down_p (uint8_t navswitch) +{ + return navswitch_state[navswitch].current; +} + + +/** Return true if navswitch up (released). + @param navswitch index of navswitch to select + @return 1 if navswitch down otherwise 0 */ +bool +navswitch_up_p (uint8_t navswitch) +{ + return !navswitch_state[navswitch].current; +} + + +/** Poll all the navswitches and update their state. */ +void +navswitch_update (void) +{ + uint8_t i; + + /* The switch is a 5-way navigation switch with push. The common + connection is connected to ground. The 5 switch outputs share + the LED matrix display column drive pins so we need to save the + state of the column outputs, switch them to inputs, read the + switch states, switch them back to outputs, and restore the + output state. + + When a switch is pushed it will pull its associated PIO pin low + via a 2.2 kohm resistor. When the PIO pin is configured as an + input with pullup, pushing the switch will also turn on the + column MOSFET driver. This will cause some ghosting of the + display. This can be avoided by driving all the LED matrix + rows low while we read the switches. */ + + for (i = 0; i < NAVSWITCH_NUM; i++) + { + pio_config_t config; + + navswitch_state[i].previous = navswitch_state[i].current; + + config = pio_config_get (navswitch_cfg[i].pio); + + /* Momentarily force PIO pin high to charge gate capacitance of + MOSFET otherwise will always read logic low and think the + switch is pushed. Alternatively, we need to wait 10 us or + more so that the gate capacitance of the MOSFET charges via + the pullup resistor. */ + pio_config_set (navswitch_cfg[i].pio, PIO_OUTPUT_HIGH); + + /* Switch PIO to an input to read switch. */ + pio_config_set (navswitch_cfg[i].pio, PIO_PULLUP); + navswitch_state[i].current = pio_input_get (navswitch_cfg[i].pio) == 0; + + /* Restore PIO state. */ + pio_config_set (navswitch_cfg[i].pio, config); + } +} + + +/** Initialise navswitch driver. */ +void navswitch_init (void) +{ + /* Nothing to do since we configure PIOs when polling the switch. */ +} + + diff --git a/drivers/navswitch.h b/drivers/navswitch.h new file mode 100644 index 0000000..a943d3b --- /dev/null +++ b/drivers/navswitch.h @@ -0,0 +1,81 @@ +/** @file navswitch.h + @author M. P. Hayes, UCECE + @date 15 Feb 2003 + @brief Navswitch polling (no debouncing). + + @defgroup navswitch Navswitch driver + + This module implements a simple navswitch driver. Note, no + debouncing is performed. + + Here's an example application where the navswitches are polled at 50 Hz: + + @code + #include "pacer.h" + #include "navswitch.h" + + void main (void) + { + system_init (); + navswitch_init (); + pacer_init (50); + + while (1) + { + pacer_wait (); + navswitch_update (); + + if (navswitch_push_event_p (NAVSWITCH_EAST)) + // Do something + + if (navswitch_push_event_p (NAVSWITCH_WEST)) + // Do something + } + } + @endcode +*/ + + +#ifndef NAVSWITCH_H +#define NAVSWITCH_H + +#include "system.h" + + +enum {NAVSWITCH_NORTH, NAVSWITCH_EAST, NAVSWITCH_SOUTH, NAVSWITCH_WEST, + NAVSWITCH_PUSH}; + + +/** Poll all the navswitches and update their state. */ +void navswitch_update (void); + + +/** Return true if navswitch state changed from up to down since + last call to navswitch_update + @param navswitch index of navswitch to select + @return 1 if navswitch changed from up to down otherwise 0 */ +bool navswitch_push_event_p (uint8_t navswitch); + + +/** Return true if navswitch state changed from down to up since + last call to navswitch_update + @param navswitch index of navswitch to select + @return 1 if navswitch changed from down to up otherwise 0 */ +bool navswitch_release_event_p (uint8_t navswitch); + + +/** Return true if navswitch down (pushed). + @param navswitch index of navswitch to select + @return 1 if navswitch down otherwise 0 */ +bool navswitch_down_p (uint8_t navswitch); + + +/** Return true if navswitch up (released). + @param navswitch index of navswitch to select + @return 1 if navswitch down otherwise 0 */ +bool navswitch_up_p (uint8_t navswitch); + + +/** Initialise navswitch driver. */ +void navswitch_init (void); +#endif diff --git a/drivers/test/avrtest.h b/drivers/test/avrtest.h new file mode 100644 index 0000000..7e5e433 --- /dev/null +++ b/drivers/test/avrtest.h @@ -0,0 +1,33 @@ +#ifndef AVRTEST_H +#define AVRTEST_H + +#include "system.h" + +typedef struct SFR +{ + uint8_t PORTB; + uint8_t DDRB; + uint8_t PINB; + uint8_t PORTC; + uint8_t DDRC; + uint8_t PINC; + uint8_t PORTD; + uint8_t DDRD; + uint8_t PIND; +} SFR_t; + + +#define PORTB SFR.PORTB +#define PORTC SFR.PORTC +#define PORTD SFR.PORTD +#define DDRB SFR.DDRB +#define DDRC SFR.DDRC +#define DDRD SFR.DDRD +#define PINB SFR.PINB +#define PINC SFR.PINC +#define PIND SFR.PINC + +extern SFR_t SFR; + + +#endif diff --git a/drivers/test/delay.h b/drivers/test/delay.h new file mode 100644 index 0000000..7b5186e --- /dev/null +++ b/drivers/test/delay.h @@ -0,0 +1 @@ +#define DELAY_US(US) diff --git a/drivers/test/eeprom.c b/drivers/test/eeprom.c new file mode 100644 index 0000000..72eb8dd --- /dev/null +++ b/drivers/test/eeprom.c @@ -0,0 +1,85 @@ +/** @file eeprom.c + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief +*/ +#include "system.h" +#include "eeprom.h" +#include +#include + + +#define EEPROM_SIZE 512 +#define EEPROM_FILENAME "ucfk_eeprom.dat" + + +static void +eeprom_file_write (void *buffer, eeprom_size_t size) +{ + FILE *fp; + + fp = fopen (EEPROM_FILENAME, "w"); + if (fp) + { + fwrite (buffer, size, 1, fp); + fclose (fp); + } +} + + +static void +eeprom_file_read (void *buffer, eeprom_size_t size) +{ + FILE *fp; + + fp = fopen (EEPROM_FILENAME, "r"); + if (!fp) + { + /* If file not present assume EEPROM is erased and thus + contains the values 0xff. */ + memset (buffer, 0xff, size); + eeprom_file_write (buffer, size); + } + else + { + fread (buffer, size, 1, fp); + fclose (fp); + } +} + + +/* Read SIZE bytes from ADDR into BUFFER. */ +eeprom_size_t +eeprom_read (eeprom_addr_t addr, void *buffer, eeprom_size_t size) +{ + eeprom_size_t i; + uint8_t *data = buffer; + uint8_t eeprom[EEPROM_SIZE]; + + eeprom_file_read (eeprom, EEPROM_SIZE); + + for (i = 0; i < size && addr < EEPROM_SIZE; i++) + data[i] = eeprom[addr++]; + + return i; + +} + + +/* Write SIZE bytes to ADDR from BUFFER. */ +eeprom_size_t +eeprom_write (eeprom_addr_t addr, const void *buffer, eeprom_size_t size) +{ + eeprom_size_t i; + const uint8_t *data = buffer; + uint8_t eeprom[EEPROM_SIZE]; + + eeprom_file_read (eeprom, EEPROM_SIZE); + + for (i = 0; i < size && addr < EEPROM_SIZE; i++) + eeprom[addr++] = data[i]; + + eeprom_file_write (eeprom, EEPROM_SIZE); + + return i; +} diff --git a/drivers/test/eeprom.h b/drivers/test/eeprom.h new file mode 100644 index 0000000..20bbcff --- /dev/null +++ b/drivers/test/eeprom.h @@ -0,0 +1,30 @@ +/** @file eeprom.h + @author Michael Hayes + @date 13 Nov 2006 + @brief Routines to read/write internal EEPROM. + + @defgroup eeprom EEPROM driver + + This module implements a driver to read/write the EEPROM of an + ATmega microcontroller. The EEPROM is useful for storing + persistent data when the power is removed. + */ + +#ifndef EEPROM_H +#define EEPROM_H + +#include "system.h" + +typedef uint16_t eeprom_addr_t; +typedef uint16_t eeprom_size_t; + +/* Read SIZE bytes from ADDR into BUFFER. */ +extern eeprom_size_t +eeprom_read (eeprom_addr_t addr, void *buffer, eeprom_size_t size); + + +/* Write SIZE bytes to ADDR from BUFFER. */ +extern eeprom_size_t +eeprom_write (eeprom_addr_t addr, const void *buffer, eeprom_size_t size); + +#endif diff --git a/drivers/test/ir_uart.c b/drivers/test/ir_uart.c new file mode 100644 index 0000000..5a01999 --- /dev/null +++ b/drivers/test/ir_uart.c @@ -0,0 +1,64 @@ +/** @file ir_uart.c + @author M. P. Hayes, UCECE + @date 1 Aug 2011 + @brief This is just a stub that does nothing. +*/ +#include "system.h" +#include "ir_uart.h" + + +/* Return non-zero if there is a character ready to be read. */ +bool +ir_uart_read_ready_p (void) +{ + return 0; +} + + +/* Read character from IR_UART. This blocks if nothing + is available to read. */ +int8_t +ir_uart_getc (void) +{ + return 0; +} + + +/* Return non-zero if a character can be written without blocking. */ +bool +ir_uart_write_ready_p (void) +{ + return 0; +} + + +/* Return non-zero if transmitter finished. */ +bool +ir_uart_write_finished_p (void) +{ + return 1; +} + + +/* Write character to IR_UART. This returns zero if + the character could not be written. */ +int8_t +ir_uart_putc (__unused__ char ch) +{ + return 0; +} + + +/* Write string to IR_UART. */ +void +ir_uart_puts (__unused__ const char *str) +{ +} + + +/* Initialise ir_uart and set baud rate. */ +uint8_t +ir_uart_init (void) +{ + return 0; +} diff --git a/drivers/test/ir_uart.h b/drivers/test/ir_uart.h new file mode 100644 index 0000000..fbb1b41 --- /dev/null +++ b/drivers/test/ir_uart.h @@ -0,0 +1,52 @@ +/** @file ir_uart.h + @author Michael Hayes + @date 10 December 2004 +*/ + +#ifndef IR_UART_H +#define IR_UART_H + +#include "system.h" + +#ifndef IR_UART_BAUD_RATE +#define IR_UART_BAUD_RATE 2400 +#endif + + +/* Return non-zero if there is a character ready to be read. */ +bool +ir_uart_read_ready_p (void); + + +/* Read character from IR_UART. This blocks if nothing + is available to read. */ +int8_t +ir_uart_getc (void); + + +/* Return non-zero if a character can be written without blocking. */ +bool +ir_uart_write_ready_p (void); + + +/* Return non-zero if transmitter finished. */ +bool +ir_uart_write_finished_p (void); + + +/* Write character to IR_UART. This returns zero if + the character could not be written. */ +int8_t +ir_uart_putc (char ch); + + +/* Write string to IR_UART. */ +void +ir_uart_puts (const char *str); + + +/* Initialise ir_uart and set baud rate. */ +uint8_t +ir_uart_init (void); + +#endif diff --git a/drivers/test/mgetkey.c b/drivers/test/mgetkey.c new file mode 100644 index 0000000..45e65e5 --- /dev/null +++ b/drivers/test/mgetkey.c @@ -0,0 +1,94 @@ +/** @file mgetkey.c + @author M. P. Hayes, UCECE + @date 17 July 2011 + @brief Non-blocking raw keyboard reading. +*/ + +#include +#include + +#include +#include +#include +#include + +#include "mgetkey.h" + +static struct termios tty_attr_save; +static int tty_init = 0; + + +/* Get the terminal attributes. */ +static int mgetkey_attr_get (struct termios *ptty_attr) +{ + if (tcgetattr (STDIN_FILENO, ptty_attr) == -1) + { + perror ("mgetkey: Cannot get tty attributes"); + return 0; + } + return 1; +} + + +/* Set terminal attributes ensuring that the original attributes are + saved. */ +static int mgetkey_attr_set (struct termios *ptty_attr) +{ + if (tcsetattr (STDIN_FILENO, TCSANOW, ptty_attr) == -1) + { + perror ("mgetkey: Cannot set tty attributes"); + return 0; + } + return 1; +} + + +int mgetkey_init (void) +{ + struct termios tty_attr; + + if (!tty_init) + { + if (!mgetkey_attr_get (&tty_attr_save)) + return 0; + + /* Restore terminal attributes on exit. */ + atexit (mgetkey_reset); + tty_init = 1; + } + + /* Get terminal attributes. */ + if (!mgetkey_attr_get (&tty_attr)) + return 0; + + tty_attr.c_lflag &= ~ECHO; /* No echo. */ + tty_attr.c_lflag &= ~ICANON; /* No canonical processing. */ + tty_attr.c_cc[VMIN] = 0; /* Don't block. */ + tty_attr.c_cc[VTIME] = 0; /* Don't block. */ + + /* Set terminal attributes. */ + return mgetkey_attr_set (&tty_attr); +} + + +/* Restore the terminal attributes. */ +void mgetkey_reset (void) +{ + mgetkey_attr_set (&tty_attr_save); +} + + +/* Read a single key stroke from the keyboard. This blocks until + a key is typed. Note that the key is not echoed. */ +int mgetkey (void) +{ + int key; + + if (!tty_init) + mgetkey_init (); + + key = getchar (); + if (key == -1) + key = 0; + return key; +} diff --git a/drivers/test/mgetkey.h b/drivers/test/mgetkey.h new file mode 100644 index 0000000..88e3299 --- /dev/null +++ b/drivers/test/mgetkey.h @@ -0,0 +1,19 @@ +/** @file mgetkey.h + @author M. P. Hayes, UCECE + @date 17 July 2011 + @brief Non-blocking raw keyboard reading. +*/ + +#ifndef MGETKEY_H +#define MGETKEY_H + +/* Read a single key stroke from the keyboard. If no key has been + pressed then zero is returned. Note that the key is not + echoed. */ +int mgetkey (void); + + +/* Restore terminal settings. */ +void mgetkey_reset (void); + +#endif diff --git a/drivers/test/pio.c b/drivers/test/pio.c new file mode 100644 index 0000000..f5d1115 --- /dev/null +++ b/drivers/test/pio.c @@ -0,0 +1,7 @@ +/** @file pio.c + @author M. P. Hayes, UCECE + @date 11 Jan 2006 + @brief PIO hardware abstraction for AVR microcontroller. + @note This implementation is empty; inline functions are used + in pio.h instead for performance. +*/ diff --git a/drivers/test/pio.h b/drivers/test/pio.h new file mode 100644 index 0000000..b9b2d63 --- /dev/null +++ b/drivers/test/pio.h @@ -0,0 +1,265 @@ +/** @file pio.h + @author M. P. Hayes, UCECE + @date 11 Jan 2006 + @brief PIO hardware abstraction for AVR microcontroller. + @note Macros and inline functions are used to avoid function + call overhead and to allow compile-time constant folding. + + @defgroup pio PIO driver + + This module implements a driver for general purpose I/O ports. + Its purpose is to provide a microcontroller independent + abstraction of a PIO pin. Here's an example: + + @code + #include "pio.h" + + #define LED_PIO PIO_DEFINE (PORT_B, 5) + + void main (void) + { + system_init (); + pio_config_set (LED_PIO, PIO_OUTPUT_LOW); + + pio_output_high (LED_PIO); + + while (1); + } + @endcode + + In this example, when port B5 is configured as an output it is set + low. +*/ +#ifndef PIO_H +#define PIO_H + +#include "system.h" +#include "avrtest.h" + + +/** Define port names, note not all the ports are available on some AVRs. */ + +#ifdef PORTA +#define PORT_A &PORTA +#endif + +#ifdef PORTB +#define PORT_B &PORTB +#endif + +#ifdef PORTC +#define PORT_C &PORTC +#endif + +#ifdef PORTD +#define PORT_D &PORTD +#endif + +#ifdef PORTE +#define PORT_E &PORTE +#endif + +#ifdef PORTF +#define PORT_F &PORTF +#endif + +#ifdef PORTG +#define PORT_G &PORTG +#endif + + +typedef volatile uint8_t *pio_port_t; +typedef uint8_t pio_mask_t; + + +typedef struct +{ + pio_port_t port; + pio_mask_t bitmask; +} pio_t; + + +/** Define PIO pin types. The two flavours of PIO_OUTPUT are to + ensure the port is configured without glitching due to the time + taken before calling pio_output_set. */ +typedef enum pio_config_enum +{ + PIO_INPUT = 1, /** Configure as input pin. */ + PIO_PULLUP, /** Configure as input pin with pullup. */ + PIO_OUTPUT_LOW, /** Configure as output, initially low. */ + PIO_OUTPUT_HIGH, /** Configure as output, initially high. */ +} pio_config_t; + + +/** DDR PORT Comment + 0 0 High impedance input + 0 1 Weak pullup input + 1 0 Output low + 1 1 Output high +*/ + + +/** Define a PIO as a compound literal structure in terms of a port and + a bitmask. */ +#define PIO_DEFINE(PORT, PORTBIT) (pio_t){.port = PORT, .bitmask = BIT (PORTBIT)} + + +/** Private macro to lookup bitmask. */ +#define PIO_BITMASK_(pio) (pio.bitmask) + + +/** Private macro to lookup port register. */ +#define PIO_PORT_(pio) (pio.port) + + +/** Private macro to map a pio to its corresponding data direction + register (DDR). NB, the DDR and PORT registers must be separated + by the same number of bytes in all cases. PORTB is used for the + pattern since PORTA is not always defined for some AVRs. */ +#define PIO_DDR_(pio) (*(PIO_PORT_ (pio) + (&DDRB - &PORTB))) + +/** Private macro to map a pio to its input register (PIN). NB, the + PIN and PORT registers must be separated by the same number of + bytes in all cases. PORTB is used for the pattern since PORTA is + not always defined for some AVRs. */ +#define PIO_PIN_(pio) (*(PIO_PORT_ (pio) + (&PINB - &PORTB))) + +/** Private macro to access a pio data register. */ +#define PIO_DATA_(pio) (*PIO_PORT_ (pio)) + + + +/** Configure pio. + @param pio PIO to configure + @param config PIO configuration type + @return non-zero for success. */ +static inline +bool pio_config_set (pio_t pio, pio_config_t config) +{ + switch (config) + { + case PIO_OUTPUT_LOW: + PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio); + PIO_DDR_ (pio) |= PIO_BITMASK_ (pio); + return 1; + + case PIO_OUTPUT_HIGH: + PIO_DATA_ (pio) |= PIO_BITMASK_ (pio); + PIO_DDR_ (pio) |= PIO_BITMASK_ (pio); + return 1; + + case PIO_INPUT: + PIO_DDR_ (pio) &= ~PIO_BITMASK_ (pio); + PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio); + return 1; + + case PIO_PULLUP: + PIO_DDR_ (pio) &= ~PIO_BITMASK_ (pio); + PIO_DATA_ (pio) |= PIO_BITMASK_ (pio); + return 1; + + default: + return 0; + } +} + + +/** Return pio configuration + @param pio + @return config */ +static inline +pio_config_t pio_config_get (pio_t pio) +{ + bool ddr; + bool port; + + ddr = PIO_DDR_ (pio) & PIO_BITMASK_ (pio); + port = PIO_DATA_ (pio) & PIO_BITMASK_ (pio); + + if (ddr) + { + if (port) + return PIO_OUTPUT_HIGH; + else + return PIO_OUTPUT_LOW; + } + + if (port) + return PIO_PULLUP; + + return PIO_INPUT; +} + + + +/** Set pio high. + @param pio */ +static inline +void pio_output_high (pio_t pio) +{ + PIO_DATA_ (pio) |= PIO_BITMASK_ (pio); +} + + +/** Set pio low. + @param pio */ +static inline +void pio_output_low (pio_t pio) +{ + PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio); +} + + +/** Toggle pio. + @param pio */ +static inline +void pio_output_toggle (pio_t pio) +{ + PIO_DATA_ (pio) ^= PIO_BITMASK_ (pio); +} + + +/** Read input state from pio. + @param pio + @return input state of pio */ +static inline +bool pio_input_get (pio_t pio) +{ + return (PIO_PIN_ (pio) & PIO_BITMASK_ (pio)) != 0; +} + + +/** Get output state of pio. + @param pio + @return output state of pio */ +static inline +bool pio_output_get (pio_t pio) +{ + return (PIO_DATA_ (pio) & PIO_BITMASK_ (pio)) != 0; +} + + +/** Set pio to desired state. + @param pio + @param state value to write pio */ +static inline +void pio_output_set (pio_t pio, bool state) +{ + state ? pio_output_high (pio) : pio_output_low (pio); +} + + +/** Set input state for pio (this is only for use by the test scaffold). + @param pio + @param state value for pio input */ +static inline +void pio_input_set (pio_t pio, bool state) +{ + if (state) + PIO_PIN_ (pio) |= PIO_BITMASK_ (pio); + else + PIO_PIN_ (pio) &= ~PIO_BITMASK_ (pio); +} + + +#endif diff --git a/drivers/test/system.c b/drivers/test/system.c new file mode 100644 index 0000000..c13f3c9 --- /dev/null +++ b/drivers/test/system.c @@ -0,0 +1,402 @@ +/** @file system.c + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief Test scaffold for UCFK4. +*/ + +#include "system.h" +#include "avrtest.h" +#include "pio.h" +#include "mgetkey.h" +#include +#include +#include +#include +#include + + +/** This needs to be at least 50 * 5 for a refresh rate of 50 Hz. */ +#define SYSTEM_UPDATE_RATE 400 + +#define SYSTEM_KEYBOARD_RELEASE_PERIOD 0.2 + +#define SYSTEM_DISPLAY_PERSISTENCE_PERIOD 0.01 + + +SFR_t SFR = {0, 0, ~0, 0, 0, ~0, 0, 0, ~0}; + + +/* Define PIO pins driving LED matrix rows and columns. */ +static const pio_t ledmat_rows[] = +{ + LEDMAT_ROW1_PIO, + LEDMAT_ROW2_PIO, + LEDMAT_ROW3_PIO, + LEDMAT_ROW4_PIO, + LEDMAT_ROW5_PIO, + LEDMAT_ROW6_PIO, + LEDMAT_ROW7_PIO +}; + +static const pio_t ledmat_cols[] = +{ + LEDMAT_COL1_PIO, + LEDMAT_COL2_PIO, + LEDMAT_COL3_PIO, + LEDMAT_COL4_PIO, + LEDMAT_COL5_PIO +}; + + +typedef enum {NAVSWITCH_NONE, NAVSWITCH_EAST, NAVSWITCH_SOUTH, + NAVSWITCH_WEST, NAVSWITCH_NORTH, NAVSWITCH_PUSH} system_navswitch_t; + +typedef enum {BUTTON_NONE, BUTTON_1} system_button_t; + + +static void system_display_update (void) +{ + int row; + int col; + /* Persistence time (s). */ + double tau = SYSTEM_DISPLAY_PERSISTENCE_PERIOD; + /* Sample period (s). */ + double delta_t; + /* Exponential filter parameter. */ + double alpha; + static uint8_t tdisplay[LEDMAT_ROWS_NUM][LEDMAT_COLS_NUM]; + + delta_t = 1.0 / SYSTEM_UPDATE_RATE; + alpha = tau / (tau + delta_t); + + for (row = 0; row < LEDMAT_ROWS_NUM; row++) + { + for (col = 0; col < LEDMAT_COLS_NUM; col++) + { + uint8_t on; + + /* Determine if pixel is on. */ + on = !pio_output_get (ledmat_rows[row]) + && !pio_output_get (ledmat_cols[col]); + + /* Apply exponential filter to provide some persistence. + This has an impulse response of exp(-t / tau) u(t). */ + tdisplay[row][col] = tdisplay[row][col] * alpha + (1 - alpha) * 100 * on; + + printf ("%c", tdisplay[row][col] > 1 ? '@' : '.'); + } + printf ("\n"); + } + printf ("\n"); + /* Move cursor up. */ + printf ("\e[%dA", LEDMAT_ROWS_NUM + 1); +} + + +static void system_navswitch_set (system_navswitch_t navswitch, bool state) +{ + switch (navswitch) + { + case NAVSWITCH_NONE: + break; + + case NAVSWITCH_NORTH: + pio_input_set (NAVSWITCH_NORTH_PIO, !state); + break; + + case NAVSWITCH_EAST: + pio_input_set (NAVSWITCH_EAST_PIO, !state); + break; + + case NAVSWITCH_SOUTH: + pio_input_set (NAVSWITCH_SOUTH_PIO, !state); + break; + + case NAVSWITCH_WEST: + pio_input_set (NAVSWITCH_WEST_PIO, !state); + break; + + case NAVSWITCH_PUSH: + pio_input_set (NAVSWITCH_PUSH_PIO, !state); + break; + } + +} + + +static void system_navswitch_press (system_navswitch_t navswitch) +{ + system_navswitch_set (navswitch, 1); +} + + +static void system_navswitch_release (system_navswitch_t navswitch) +{ + system_navswitch_set (navswitch, 0); +} + + +static void system_navswitch_select (system_navswitch_t navswitch) +{ + static system_navswitch_t last = NAVSWITCH_NONE; + static int counter; + + if (navswitch == NAVSWITCH_NONE) + { + if (counter) + { + counter--; + if (!counter) + { + system_navswitch_release (last); + last = NAVSWITCH_NONE; + } + } + return; + } + + counter = SYSTEM_UPDATE_RATE * SYSTEM_KEYBOARD_RELEASE_PERIOD; + + if (last == navswitch) + return; + + system_navswitch_release (last); + + last = navswitch; + + system_navswitch_press (navswitch); +} + + +static void system_button_set (system_button_t button, bool state) +{ + switch (button) + { + case BUTTON_NONE: + break; + + case BUTTON_1: + /* Active high. */ + pio_input_set (BUTTON1_PIO, state); + break; + } + +} + + +static void system_button_press (system_button_t button) +{ + system_button_set (button, 1); +} + + +static void system_button_release (system_button_t button) +{ + system_button_set (button, 0); +} + + +static void system_button_select (system_button_t button) +{ + static system_button_t last = BUTTON_NONE; + static int counter; + + if (button == BUTTON_NONE) + { + if (counter) + { + counter--; + if (!counter) + { + system_button_release (last); + last = BUTTON_NONE; + } + } + return; + } + + counter = SYSTEM_UPDATE_RATE * SYSTEM_KEYBOARD_RELEASE_PERIOD; + + if (last == button) + return; + + system_button_release (last); + + last = button; + + system_button_press (button); +} + + +static void system_keyboard_init (void) +{ + system_navswitch_release (NAVSWITCH_PUSH); + system_navswitch_release (NAVSWITCH_NORTH); + system_navswitch_release (NAVSWITCH_EAST); + system_navswitch_release (NAVSWITCH_SOUTH); + system_navswitch_release (NAVSWITCH_WEST); + + system_button_release (BUTTON_1); +} + + +static void system_keyboard_update (void) +{ + static int state = 0; + int key; + + system_navswitch_select (NAVSWITCH_NONE); + system_button_select (BUTTON_NONE); + + key = mgetkey (); + + switch (state) + { + case 0: + switch (key) + { + case '\e': + /* Have escape character. */ + state = 1; + break; + + case ' ': + system_navswitch_select (NAVSWITCH_PUSH); + break; + + case '.': + system_button_select (BUTTON_1); + break; + + default: + break; + } + break; + + case 1: + if (key == '[') + state = 2; + else + state = 0; + break; + + case 2: + state = 0; + switch (key) + { + case 'A': + system_navswitch_select (NAVSWITCH_NORTH); + break; + + case 'B': + system_navswitch_select (NAVSWITCH_SOUTH); + break; + + case 'C': + system_navswitch_select (NAVSWITCH_EAST); + break; + + case 'D': + system_navswitch_select (NAVSWITCH_WEST); + break; + + default: + break; + } + break; + } +} + + +static void system_update (void) +{ + system_display_update (); + system_keyboard_update (); +} + + +static void +system_handler (__unused__ int sig, __unused__ siginfo_t *si, + __unused__ void *uc) +{ + system_update (); +} + + +typedef void (*sighandler_t)(int, siginfo_t *, void *); + +static int system_interrupt_init (sighandler_t handler, int rate) +{ + timer_t timerid; + struct sigevent sev; + struct itimerspec its; + long long period_ns; + sigset_t mask; + struct sigaction sa; + static int signum = 0; + + /* When debugging with gdb use "handle SIG34 noprint". */ + + if (!signum) + signum = SIGRTMIN; + + /* Establish handler for timer signal. */ + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = handler; + sigemptyset (&sa.sa_mask); + if (sigaction (signum, &sa, NULL) == -1) + { + perror ("sigaction"); + exit (EXIT_FAILURE); + } + + /* Block timer signal temporarily. */ + sigemptyset (&mask); + sigaddset (&mask, signum); + if (sigprocmask (SIG_SETMASK, &mask, NULL) == -1) + { + perror ("sigprocmask"); + exit (EXIT_FAILURE); + } + + /* Create the timer. */ + sev.sigev_notify = SIGEV_SIGNAL; + sev.sigev_signo = signum; + sev.sigev_value.sival_ptr = &timerid; + if (timer_create (CLOCK_REALTIME, &sev, &timerid) == -1) + { + perror ("timer_create"); + exit (EXIT_FAILURE); + } + + /* Start the timer */ + period_ns = 1000000000 / rate; + its.it_value.tv_sec = period_ns / 1000000000; + its.it_value.tv_nsec = period_ns % 1000000000; + its.it_interval.tv_sec = its.it_value.tv_sec; + its.it_interval.tv_nsec = its.it_value.tv_nsec; + if (timer_settime (timerid, 0, &its, NULL) == -1) + { + perror ("timer_settime"); + exit (EXIT_FAILURE); + } + + /* Unlock the timer signal, so that timer notification + can be delivered. */ + if (sigprocmask (SIG_UNBLOCK, &mask, NULL) == -1) + { + perror ("sigprocmask"); + exit (EXIT_FAILURE); + } + + signum++; + return signum; +} + + +void system_init (void) +{ + system_keyboard_init (); + + system_interrupt_init (system_handler, SYSTEM_UPDATE_RATE); +} diff --git a/drivers/test/system.h b/drivers/test/system.h new file mode 100644 index 0000000..7afbebf --- /dev/null +++ b/drivers/test/system.h @@ -0,0 +1,74 @@ +/** @file system.h + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief System specific definitions +*/ +#ifndef SYSTEM_H +#define SYSTEM_H + +/* Data typedefs. */ +#include + +typedef uint8_t bool; + + +/* Useful macros. */ +#define BIT(X) (1 << (X)) + +#define ARRAY_SIZE(ARRAY) (sizeof (ARRAY) / sizeof (ARRAY[0])) + +#define __unused__ __attribute__ ((unused)) + + +/* Clock frequency Hz. */ +#define F_CPU 8000000 + + +/* LED matrix columns. */ +#define LEDMAT_COL1_PIO PIO_DEFINE(PORT_C, 6) +#define LEDMAT_COL2_PIO PIO_DEFINE(PORT_B, 7) +#define LEDMAT_COL3_PIO PIO_DEFINE(PORT_C, 4) +#define LEDMAT_COL4_PIO PIO_DEFINE(PORT_C, 7) +#define LEDMAT_COL5_PIO PIO_DEFINE(PORT_C, 5) +#define LEDMAT_COLS_NUM 5 + + +/* LED matrix rows. */ +#define LEDMAT_ROW1_PIO PIO_DEFINE(PORT_B, 6) +#define LEDMAT_ROW2_PIO PIO_DEFINE(PORT_B, 5) +#define LEDMAT_ROW3_PIO PIO_DEFINE(PORT_B, 4) +#define LEDMAT_ROW4_PIO PIO_DEFINE(PORT_B, 3) +#define LEDMAT_ROW5_PIO PIO_DEFINE(PORT_B, 2) +#define LEDMAT_ROW6_PIO PIO_DEFINE(PORT_B, 1) +#define LEDMAT_ROW7_PIO PIO_DEFINE(PORT_B, 0) +#define LEDMAT_ROWS_NUM 7 + + +/* Button. */ +#define BUTTON1 0 +#define BUTTON1_PIO PIO_DEFINE(PORT_D, 7) + + +/* Navswitch. */ +#define NAVSWITCH_PUSH_PIO LEDMAT_COL3_PIO +#define NAVSWITCH_EAST_PIO LEDMAT_COL1_PIO +#define NAVSWITCH_WEST_PIO LEDMAT_COL2_PIO +#define NAVSWITCH_NORTH_PIO LEDMAT_COL4_PIO +#define NAVSWITCH_SOUTH_PIO LEDMAT_COL5_PIO + + +/* LED (active high). */ +#define LED1 0 +#define LED1_PIO PIO_DEFINE(PORT_C, 2) + + +/* Infrared transmitter LED and receiver. */ +#define IR_TX_LOW_PIO PIO_DEFINE(PORT_D, 3) +#define IR_TX_HIGH_PIO PIO_DEFINE(PORT_D, 0) +#define IR_RX_PIO PIO_DEFINE(PORT_D, 2) + +#define IR_MODULATION_FREQ 36e3 + +void system_init (void); + +#endif diff --git a/drivers/test/target.h b/drivers/test/target.h new file mode 100644 index 0000000..9646226 --- /dev/null +++ b/drivers/test/target.h @@ -0,0 +1,57 @@ +/** @file system.h + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief Target specific definitions +*/ +#ifndef TARGET_H +#define TARGET_H + +#define F_CPU 8000000 + + +/* LED matrix columns. */ +#define LEDMAT_COL1_PIO PIO_DEFINE(PORT_C, 5) +#define LEDMAT_COL2_PIO PIO_DEFINE(PORT_C, 7) +#define LEDMAT_COL3_PIO PIO_DEFINE(PORT_C, 4) +#define LEDMAT_COL4_PIO PIO_DEFINE(PORT_B, 7) +#define LEDMAT_COL5_PIO PIO_DEFINE(PORT_C, 6) +#define LEDMAT_COLS_NUM 5 + + +/* LED matrix rows. */ +#define LEDMAT_ROW1_PIO PIO_DEFINE(PORT_B, 0) +#define LEDMAT_ROW2_PIO PIO_DEFINE(PORT_B, 1) +#define LEDMAT_ROW3_PIO PIO_DEFINE(PORT_B, 2) +#define LEDMAT_ROW4_PIO PIO_DEFINE(PORT_B, 3) +#define LEDMAT_ROW5_PIO PIO_DEFINE(PORT_B, 4) +#define LEDMAT_ROW6_PIO PIO_DEFINE(PORT_B, 5) +#define LEDMAT_ROW7_PIO PIO_DEFINE(PORT_B, 6) +#define LEDMAT_ROWS_NUM 7 + + +/* Button. */ +#define BUTTON1 0 +#define BUTTON1_PIO PIO_DEFINE(PORT_D, 7) + + +/* Navswitch. */ +#define NAVSWITCH_PUSH_PIO LEDMAT_COL3_PIO +#define NAVSWITCH_EAST_PIO LEDMAT_COL1_PIO +#define NAVSWITCH_WEST_PIO LEDMAT_COL2_PIO +#define NAVSWITCH_NORTH_PIO LEDMAT_COL4_PIO +#define NAVSWITCH_SOUTH_PIO LEDMAT_COL5_PIO + + +/* LED (active high). */ +#define LED1 0 +#define LED1_PIO PIO_DEFINE(PORT_C, 2) + + +/* Infrared transmitter LED and receiver. */ +#define IR_TX_HIGH_PIO PIO_DEFINE(PORT_D, 3) +#define IR_TX_LOW_PIO PIO_DEFINE(PORT_D, 0) +#define IR_RX_PIO PIO_DEFINE(PORT_D, 2) + +#define IR_MODULATION_FREQ 36e3 + +#endif diff --git a/drivers/test/timer.c b/drivers/test/timer.c new file mode 100644 index 0000000..756312b --- /dev/null +++ b/drivers/test/timer.c @@ -0,0 +1,72 @@ +/** @file timer.c + @author M. P. Hayes, UCECE + @date 21 August 2007 + Description: Support for timer. +*/ +#include "timer.h" +#include "unistd.h" +#include "time.h" +#include "system.h" + +#include + +static timer_tick_t offset; + + +/* Initialise timer. */ +void timer_init (void) +{ + /* Save offset so timer starts around 0. */ + offset = timer_get (); +} + + +/** Get current time: + @return current time in ticks. */ +timer_tick_t timer_get (void) +{ + struct timespec time; + uint64_t time_us; + uint64_t ticks; + + clock_gettime (CLOCK_MONOTONIC, &time); + + time_us = time.tv_sec * 1000000 + time.tv_nsec / 1000; + ticks = time_us * TIMER_RATE / 1000000; + + return (timer_tick_t) ticks - offset; +} + + +/** Wait until specified time: + @param when time to sleep until + @return current time. */ +timer_tick_t timer_wait_until (timer_tick_t when) +{ + while (1) + { + timer_tick_t diff; + timer_tick_t now; + + now = timer_get (); + + diff = now - when; + + /* Allow for 1000 tick overrun. */ + if (diff < TIMER_OVERRUN_MAX) + return now; + + diff = when - now; + + usleep (diff * 1e6 / TIMER_RATE); + } +} + + +/** Wait for specified period: + @param period how long to wait + @return current time. */ +timer_tick_t timer_wait (timer_tick_t period) +{ + return timer_wait_until (timer_get () + period); +} diff --git a/drivers/test/timer.h b/drivers/test/timer.h new file mode 100644 index 0000000..b01c1ff --- /dev/null +++ b/drivers/test/timer.h @@ -0,0 +1,97 @@ +/** @file timer.h + @author M. P. Hayes, UCECE + @date 18 August 2011 + @brief Timer support. + + @defgroup timer Timer module + + This module provides simple timer support by abstracting a + hardware timer. + + The rate that the timer is incremented is specified by the + macro TIMER_CLOCK_DIVISOR in the header file system.h This + must be a value supported by the timer clock prescaler. + + The rate that the timer is incremented can be found using the + macro TIMER_RATE. + + The timer is free-running and will increment TIMER_RATE times + per second. When the timer reaches 65535 on the next increment + it rolls over to 0. + + Here's a simple example for turning an LED on for 0.5 second + and then off for 0.75 second. + + @code + #include "timer.h" + #include "led.h" + + void main (void) + { + timer_tick_t now; + + system_init (); + timer_init (); + led_init (); + + now = timer_get (); + while (1) + { + led_set (LED1, 1); + + now = timer_wait_until (now + (timer_tick_t)(TIMER_RATE * 0.5)); + + led_set (LED1, 0); + + now = timer_wait_until (now + (timer_tick_t)(TIMER_RATE * 0.75)); + } + } + @endcode +*/ +#ifndef TIMER_H +#define TIMER_H + +#include "system.h" + + +#ifndef TIMER_CLOCK_DIVISOR +#define TIMER_CLOCK_DIVISOR 256 +#endif + +/** Rate in Hz that the timer is incremented. */ +#define TIMER_RATE (F_CPU / TIMER_CLOCK_DIVISOR) + + +/** The maximum overrun (in ticks). */ +#define TIMER_OVERRUN_MAX 1000 + + +/** The maximum delay (in ticks). */ +#define TIMER_DELAY_MAX (65536u - TIMER_OVERRUN_MAX) + + +/** Define timer ticks. */ +typedef uint16_t timer_tick_t; + + +/** Get current time: + @return current time in ticks. */ +timer_tick_t timer_get (void); + + +/** Wait until specified time: + @param when time to sleep until + @return current time. */ +timer_tick_t timer_wait_until (timer_tick_t when); + + +/** Wait for specified period: + @param period how long to wait + @return current time. */ +timer_tick_t timer_wait (timer_tick_t period); + + +/** Initialise timer. */ +void timer_init (void); + +#endif /* TIMER_H */ diff --git a/etc/Makefile b/etc/Makefile new file mode 100644 index 0000000..c3f42aa --- /dev/null +++ b/etc/Makefile @@ -0,0 +1,102 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile to create application Makefiles from a template. + +# Abandon all hope ye who look in here. +# This Makefile builds all the application Makefiles +# using template Makefiles and a python script makemake.py that parses +# the applications looking for #includes. + +APPS = $(filter-out ../apps/README, $(wildcard ../apps/*)) + +MAKEAPPS = $(addsuffix .apps, $(APPS)) +MAKETEST = $(addsuffix .test, $(APPS)) +CLEANAPPS = $(addsuffix .cleanapps, $(APPS)) +CLEANTEST = $(addsuffix .cleantest, $(APPS)) + +TESTMAKEFILES = $(addsuffix /Makefile.test, $(APPS)) +APPMAKEFILES = $(addsuffix /Makefile, $(APPS)) + +APPFILEDEPS = $(addsuffix /file_dependencies.pdf, $(APPS)) +APPMODULEDEPS = $(addsuffix /module_dependencies.pdf, $(APPS)) +APPMAKEFILEDEPS = $(addsuffix /makefile_dependencies.pdf, $(APPS)) +APPBUILDDEPS = $(addsuffix /build_dependencies.pdf, $(APPS)) +APPCALLGRAPHDDEPS = $(addsuffix /callgraph.pdf, $(APPS)) + +all: testmakefiles appmakefiles apps test appfiledeps appmoduledeps appmakefiledeps appbuilddeps appcallgraph + +clean: cleanapps cleantest + +testmakefiles: $(TESTMAKEFILES) + +appmakefiles: $(APPMAKEFILES) + +../%/Makefile.test: Makefile.test.template Makefile + (cd $(dir $@); ../../etc/makemake.py --relpath --objext=-test.o --template ../../etc/Makefile.test.template . . ../../utils ../../drivers ../../drivers/test > Makefile.test) + + +../%/Makefile: Makefile.template Makefile makemake.py + (cd $(dir $@); ../../etc/makemake.py --relpath --template ../../etc/Makefile.template . . ../../utils ../../fonts ../../drivers ../../drivers/avr > Makefile) + + +appfiledeps: $(APPFILEDEPS) +appmoduledeps: $(APPMODULEDEPS) +appmakefiledeps: $(APPMAKEFILEDEPS) +appbuilddeps: $(APPBUILDDEPS) +appcallgraph: $(APPCALLGRAPH) + +../%/file_dependencies.pdf: ../%/files.d + ./graphdeps.py $< --out $@ + +../%/module_dependencies.pdf: ../%/modules.d + ./graphdeps.py $< --modules --out $@ + +../%/makefile_dependencies.pdf: ../%/Makefile + ./graphdeps.py $< --out $@ + +../%/build_dependencies.pdf: ../%/Makefile + ./graphdeps.py $< --out $@ --showops + +../%/callgraph.pdf: ../%/callgraph.d + (cd $(dir $@); ../../etc/graphdeps.py $(notdir $<) --out $(notdir $@)) + +../%/files.d: ../%/Makefile + (cd $(dir $@); ../../etc/makemake.py --relpath --files . . ../../drivers ../../drivers/avr ../../utils --exclude system.h > files.d) + + +../%/modules.d: ../%/Makefile + (cd $(dir $@); ../../etc/makemake.py --relpath --modules . . ../../drivers ../../drivers/avr ../../utils --exclude system > modules.d) + +../%/callgraph.d: ../%/Makefile + (cd $(dir $@); ../../etc/makemake.py --relpath --calls . . ../../drivers ../../drivers/avr ../../utils --exclude system > callgraph.d) + + +# Compile all the applications. +apps: $(MAKEAPPS) + +.PHONY: $(MAKEAPPS) +$(MAKEAPPS): + -@$(MAKE) -C $(subst .apps,,$@) + +# Compile all the test applications. +test: $(MAKETEST) + +.PHONY: $(MAKETEST) +$(MAKETEST): + -@$(MAKE) -f Makefile.test -C $(subst .test,,$@) + +# Clean all the applications. +cleanapps: $(CLEANAPPS) + +.PHONY: $(CLEANAPPS) +$(CLEANAPPS): + -@$(MAKE) -C $(subst .cleanapps,,$@) clean + +# Clean all the test applications. +cleantest: $(CLEANTEST) + +.PHONY: $(CLEANTEST) +$(CLEANTEST): + -@$(MAKE) -f Makefile.test -C $(subst .cleantest,,$@) clean + diff --git a/etc/Makefile.template b/etc/Makefile.template new file mode 100644 index 0000000..4fa959c --- /dev/null +++ b/etc/Makefile.template @@ -0,0 +1,38 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 12 Sep 2010 +# Descr: Makefile for @PROJECT@ + +# Definitions. +CC = avr-gcc +CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g @INCLUDES@ +OBJCOPY = avr-objcopy +SIZE = avr-size +DEL = rm + + +# Default target. +all: @PROJECT@.out + + +# Compile: create object files from C source files. +@CCRULES@ + +# Link: create ELF output file from object files. +@PROJECT@.out: @OBJ@ + $(CC) $(CFLAGS) $^ -o $@ -lm + $(SIZE) $@ + + +# Target: clean project. +.PHONY: clean +clean: + -$(DEL) *.o *.out *.hex + + +# Target: program project. +.PHONY: program +program: @PROJECT@.out + $(OBJCOPY) -O ihex @PROJECT@.out @PROJECT@.hex + dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash @PROJECT@.hex; dfu-programmer atmega32u2 start + diff --git a/etc/Makefile.test.template b/etc/Makefile.test.template new file mode 100644 index 0000000..9985704 --- /dev/null +++ b/etc/Makefile.test.template @@ -0,0 +1,30 @@ +# File: Makefile +# Author: M. P. Hayes, UCECE +# Date: 11 Sep 2010 +# Descr: Makefile for @PROJECT@ + +CC = gcc +CFLAGS = -Wall -Wstrict-prototypes -Wextra -g @INCLUDES@ + +DEL = rm + + +# Default target. +all: @PROJECT@ + + +# Compile: create object files from C source files. +@CCRULES@ + + +# Link: create executable file from object files. +@PROJECT@: @OBJ@ + $(CC) $(CFLAGS) $^ -o $@ -lrt + + +# Clean: delete derived files. +.PHONY: clean +clean: + -$(DEL) @PROJECT@ @OBJ@ + + diff --git a/etc/README b/etc/README new file mode 100644 index 0000000..087a55f --- /dev/null +++ b/etc/README @@ -0,0 +1,2 @@ +This is used for creating all the makefiles for compiling all the +applications. Beware all ye who enter here! diff --git a/etc/graphdeps.py b/etc/graphdeps.py new file mode 100755 index 0000000..475efe7 --- /dev/null +++ b/etc/graphdeps.py @@ -0,0 +1,213 @@ +#!/usr/bin/python +"""graphdeps V0.04 +Copyright (c) 2011 Michael P. Hayes, UC ECE, NZ + +Usage: graphdeps Makefile + +Options: + --outfile filename filename of .dot outputfile + +Examples: +""" + +# This is a quick and dirty program. It should be rewritten in +# an object-oriented manner. + +import sys +import os +import re +from optparse import OptionParser + + +def parse_rules (filename): + + infile = open (filename, 'r') + + text = infile.read () + infile.close () + + # Look for targets ignoring targets starting with dot (.PHONY, etc). + # This expects the command to be a single line. + matches = re.findall (r'^([a-z0-9._/\-]*):\s(.*)\n(.*)', text, re.MULTILINE) + + rules = [] + for match in matches: + # Target, dependencies, command. + rule = (match[0], match[1].strip().split (' '), match[2].strip ()) + rules.append (rule) + + return rules + + +def node_output (dotfile, name, options): + + # Should check if creating a duplicate although graphviz will + # weed them out. + + (file, ext) = os.path.splitext (name) + + cmap = {'.c' : 'green', + '.h' : 'skyblue', + '.o' : 'orange', + '.out' : 'red'} + + colour = 'purple' + if ext in cmap.keys (): + colour = cmap[ext] + + shape = 'ellipse' + if options.modules: + # Maybe magenta + colour = 'orange' + shape = 'rectangle' + + dotfile.write ('\t"' + name + '"\t [style=filled,shape=' + shape + ',color=' + colour + '];\n') + + return name + + +def op_output (dotfile, op, name): + + dotfile.write ('\t"' + op + '"\t[shape=rectangle,label="' + name + '"];\n') + + +def edge_output (dotfile, target, dep): + +# dotfile.write ('\t"' + target + '" ->\t"' + dep + '"\t[dir=back];\n') + dotfile.write ('\t"' + target + '" ->\t"' + dep + '";\n') + + +def dep_output (dotfile, target, dep, modules, options): + + if not options.fullpaths: + target = os.path.basename (target) + dep = os.path.basename (dep) + + target = node_output (dotfile, target, options) + + if dep == '': + return + + dep = node_output (dotfile, dep, options) + + (file, ext) = os.path.splitext (target) + + if not options.showops: + edge_output (dotfile, target, dep) + else: + if ext == '.o': + op = 'Compiler' + elif ext == '.out': + op = 'Linker' + elif ext == '.hex': + op = 'Objcopy' + else: + op = '' + + if op == '': + edge_output (dotfile, target, dep) + else: + op_output (dotfile, op + target, op) + edge_output (dotfile, target, op + target) + edge_output (dotfile, op + target, dep) + + +def target_output (dotfile, target, targets, modules, options, seen = {}): + + if not target or seen.has_key (target): + return + + deps = targets[target] + + for dep in deps: + if targets.has_key (dep): + target_output (dotfile, dep, targets, modules, options, seen) + + for dep in deps: + dep_output (dotfile, target, dep, modules, options) + + + +def main(argv = None): + if argv is None: + argv = sys.argv + + version = __doc__.split ('\n')[0] + + parser = OptionParser (usage = '%prog', version = version, + description = __doc__) + + parser.add_option('--showops', action = 'store_true', + dest = 'showops', default = False, + help = 'show operations') + + parser.add_option('--modules', action = 'store_true', + dest = 'modules', default = False, + help = 'show module dependencies') + + parser.add_option('--fullpaths', action = 'store_true', + dest = 'fullpaths', default = False, + help = 'show full paths') + + parser.add_option('--outfile', dest = 'outfilename', + default = 'graphdeps.dot', + help = 'output filename') + + parser.add_option('--target', dest = 'target', + default = None, + help = 'target to start from; if unspecified all targets are displayed') + + (options, args) = parser.parse_args () + + rules = parse_rules (args[0]) + + cfiles = [] + hfiles = [] + for rule in rules: + target = rule[0] + (file, ext) = os.path.splitext (os.path.basename (target)) + if ext == '.o': + cfiles.append (file + '.c') + for dep in rule[1]: + (file, ext) = os.path.splitext (os.path.basename (dep)) + if ext == '.h': + hfiles.append (file + '.h') + + modules = [] + for cfile in cfiles: + (modname, ext) = os.path.splitext (cfile) + hfile = modname + '.h' + if hfile in hfiles and modname not in modules: + modules.append (modname) + + targets = {} + wantedtargets = [] + for rule in rules: + target = rule[0] + targets[target] = rule[1] + if options.target == None or target == options.target: + wantedtargets.append (target) + + dopdf = options.outfilename[-4:] == '.pdf' + dotfilename = options.outfilename + if dopdf: + dotfilename = '/tmp/tmp.dot' + + dotfile = open (dotfilename, 'w') + dotfile.write ('strict digraph {\n\tgraph [rankdir=LR];\n') + + for target in wantedtargets: + target_output (dotfile, target, targets, modules, options) + + dotfile.write ('}\n') + dotfile.close () + + print ('Creating outfile ' + options.outfilename) + + if dopdf: + os.system ('dot -T pdf -o ' + options.outfilename + ' ' + dotfilename) + os.system ('rm ' + dotfilename) + + +if __name__ == '__main__': + main() diff --git a/etc/makemake.py b/etc/makemake.py new file mode 100755 index 0000000..1e10a87 --- /dev/null +++ b/etc/makemake.py @@ -0,0 +1,481 @@ +#!/usr/bin/env python +"""makemake V0.03 +Copyright (c) 2010 Michael P. Hayes, UC ECE, NZ + +This program tries to make a Makefile from a template. Given a C file +(or a directory which searched to find a C file containing a main +function) the included header files are recursively searched. The for +each header file, a search is made for a similarly named C file. The +header files for each C file are recursively searched and addded to +the list of found header files. Again, similarly named C files are +searched until all the C files required for the program are found. + +Usage: makemake --template template cfile search-dirs +or makemake --template template directory search-dirs +or makemake --builddir builddir --objext objext --template template directory search-dirs +or makemake --builddir builddir --modules --relpath directory search-dirs +or makemake --builddir builddir --files --relpath directory search-dirs + +By default makemake will create a rule like foo.o: foo.c bar.h +and this will require a VPATH if the dependencies are another directory. +Alternatively, use the --relpath option to makemake to explicitly add the +relative path to the dependencies. + +Note, this will die if there are circular dependencies. FIXME! +The --modules option also needs fixing. FIXME! + +There are special strings that are replaced in the template file: + @PROJECT@ Project name + @VPATH@ List of source directories + @INCLUDES@ List of include directories each prefixed by -I + @SRC@ List of source files + @OBJ@ List of object files + @CCRULES@ Rules to build object files from C files + +""" + +# See http://www.amk.ca/python/howto/regex/ for regular expressions in python. +# See also sre.py. +# + one or more, ? 0 or 1, * 0 or more + +# http://www.cs.umd.edu/~nspring/software/style-check-readme.html + +import sys +import getopt +import re +import os +import subprocess +from os import pathsep +import os.path + + +def unique (list): + + dict = {} + for item in list: + dict[item] = True; + + return dict.keys () + + +def file_search (filename, search_path, debug): + """Given a search path, find file + """ + + file_found = False + paths = search_path.split (pathsep) + for path in paths: + if os.path.exists (os.path.join (path, filename)): + file_found = True + break + + if file_found: + return os.path.abspath (os.path.join (path, filename)) + + # FIXME, if have :: at end of search path then need to search subdirs. + + return None + + +def hfiles_get (cfile, filedeps, mopts): + + deps = filedeps[cfile] + + if cfile in deps: + print >> sys.stderr, 'Circular dependency for', cfile + + + hfilelist = [] + + for hfile in filedeps[cfile]: + if hfile[-2:] == '.h': + if mopts['relpath']: + hfile = os.path.relpath (hfile) + hfilelist.append (hfile) + + for hfile in filedeps[cfile]: + hfilelist.extend (hfiles_get (hfile, filedeps, mopts)) + + return unique (hfilelist) + + +def cfiles_get (filedeps): + + cfilelist = [] + for target in filedeps: + if target[-2:] == '.c': + cfilelist.append (target) + + return unique (cfilelist) + + +def file_parse (pathname, indent, debug): + + if debug: + print >> sys.stderr, indent, 'Parsing file', pathname + + file = open (pathname, 'r') + text = file.read () + file.close () + + prog = re.compile (r'^#include[ ].*["<]([a-zA-Z_.0-9].*)[">]', re.MULTILINE) + + hfilelist = prog.findall (text, 0) + + if debug: + print >> sys.stderr, indent, 'Found hfiles', hfilelist, 'in', pathname + return hfilelist + + +def makefile_print (mopts, template, maincfilename, filedeps, + search_list, debug): + + basecfilelist = [] + cfilelist = [] + for target in filedeps: + if target[-2:] == '.c': + cfilelist.append (target) + basecfilelist.append (os.path.basename (target)) + basecfilelist.sort () + cfilelist.sort () + + project = os.path.splitext (os.path.basename (maincfilename)) + project = project[0] + + file = open (template, 'r') + + text = file.read () + file.close () + + vpath = ' '.join (search_list) + includes = '-I' + ' -I'.join (search_list) + src = ' '.join (basecfilelist) + obj = src + + if mopts['builddir'] != '': + objfilelist = [os.path.join (mopts['builddir'], obj1) for obj1 in basecfilelist] + objfilelist.sort () + obj = ' '.join (objfilelist) + project = os.path.join (mopts['builddir'], project) + + obj = re.sub (r'([a-zA-Z0-9/.-_]*)[.]c', r'\1' + mopts['objext'], obj) + + text = re.sub (r'@PROJECT@', project, text) + text = re.sub (r'@VPATH@', vpath, text) + text = re.sub (r'@INCLUDES@', includes, text) + text = re.sub (r'@SRC@', src, text) + + text = re.sub (r'@OBJ@', obj, text) + + if re.search (r'@CCRULES@', text) != None: + + search_path = pathsep.join (search_list) + + rules = '' + for cfile in cfilelist: + cfilebase = os.path.basename (cfile) + + if mopts['relpath']: + cfile1 = os.path.relpath (cfile) + else: + cfile1 = cfilebase + + if mopts['builddir'] != '': + rules = rules + os.path.join (mopts['builddir'], '') + + rules = rules + re.sub ('([a-zA-Z0-9/.-_]*)[.]c', r'\1' + mopts['objext'], cfilebase) + ': ' + cfile1 + + + hfilelist = hfiles_get (cfile, filedeps, mopts) + hfilelist.sort () + + if debug: + print >> sys.stderr, 'Need hfiles', hfilelist, 'for', cfile + + for hfile in hfilelist: + rules = rules + ' ' + hfile + + rules = rules + '\n' + rules = rules + '\t$(CC) -c $(CFLAGS) $< -o $@\n\n' + + text = re.sub (r'@CCRULES@', rules, text) + + + print text + + +def maincfilename_find (dirname): + + p = subprocess.Popen (['grep -l "main[ ]*(" ' + dirname + '/*.c'], + shell = True, stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + close_fds = True) + + (child_stdout, child_stdin) = (p.stdout, p.stdin) + child_stdin.close () + files = child_stdout.read () + child_stdout.close () + + filelist = files.strip ().split (' ') + if not filelist: + return None + + # What if there are multiple files with main? For now, select the + # first one. + return filelist[0] + + +def functions_find (gcc, filepath, functiondeps = {}): + + command = gcc + ' -c ' + filepath + ' -fdump-tree-cfg-raw > /dev/null' + # print >> sys.stderr, command + os.system (command) + + rtlfilename = os.path.abspath (os.path.basename (filepath)) + '.012t.cfg' + # print >> sys.stderr, rtlfilename + + file = open (rtlfilename, 'r') + text = file.readlines () + file.close () + + func_def = None + for line in text: + #print >> sys.stderr, line + matches = re.findall (r'^(.*)\s[(][)]', line) + if matches: + function = matches[0] + functiondeps[function] = [] + # print >> sys.stderr, 'DEF', function + matches = re.findall (r'.*gimple_call [<]([\w]*)[,]', line) + if matches: + # print >> sys.stderr, 'USE', matches[0] + functiondeps[function].append (matches[0]) + + command = 'rm ' + rtlfilename + # print >> sys.stderr, command + os.system (command) + + +def files_find (gcc, filepath, search_path, filedeps, moduledeps, functiondeps, indent, debug): + + # filedeps is a cache of all known included files + + if filedeps.has_key (filepath): + return + + # Find included header files + includes = file_parse (filepath, indent + ' ', debug) + includes2 = [] + + for hfile in includes: + hpath = file_search (hfile, search_path, debug) + if not hpath: + continue + includes2.append (hpath) + + # Guess modules from header files + modules = [] + for hpath in includes2: + cpath = re.sub (r'([a-zA-Z._0-9/.-_].*)[.]h', r'\1.c', hpath); + if (not os.path.exists (cpath)) or (cpath == filepath): + continue + # Have found a module + modules.append (cpath) + + base, ext = os.path.splitext (os.path.basename (filepath)) + if ext == '.c': + + functions_find (gcc, filepath, functiondeps) + + moduledeps[base] = [] + for module in modules: + modbase, ext = os.path.splitext (os.path.basename (module)) + moduledeps[base].append (modbase) + + filedeps[filepath] = includes2 + + # Search recursively each new included file + for file in includes2: + files_find (gcc, file, search_path, filedeps, moduledeps, functiondeps, indent + ' ', debug) + + # Search the modules + for file in modules: + files_find (gcc, file, search_path, filedeps, moduledeps, functiondeps, indent + ' ', debug) + + +def alldeps_print (depsdir, mopts): + + for target in depsdir.keys (): + + targetbase = os.path.basename (target) + if targetbase in mopts['exclude']: + continue + + deps = depsdir[target] + deps = [dep for dep in deps if os.path.basename (dep) not in mopts['exclude']] + if mopts['relpath']: + deps = [os.path.relpath (dep) for dep in deps] + + print os.path.relpath (target) + ': ' + ' '.join (deps) + '\n' + + +def deps_print (target, depsdir, mopts, record = {}): + + if record.has_key (target): + return + + deps = depsdir[target] + deps = [dep for dep in deps if os.path.basename (dep) not in mopts['exclude']] + for dep in deps: + deps_print (dep, depsdir, mopts, record) + + if mopts['relpath']: + deps = [os.path.relpath (dep) for dep in deps] + + record[target] = True + + print os.path.relpath (target) + ': ' + ' '.join (deps) + '\n' + + + +class Usage (Exception): + def __init__(self, msg): + self.msg = msg + + +def main(argv = None): + if argv is None: + argv = sys.argv + try: + try: + opts, args = getopt.gnu_getopt (argv[1:], "?h", \ + ["help", "builddir=", "objext=", + "exeext=", + "relpath", "debug", "template=", + "files", "modules", "exclude=", + "calls"]) + except getopt.error, msg: + raise Usage (msg) + + if not opts and not args: + print __doc__ + sys.exit (0) + + if len (args) < 1: + print __doc__ + sys.exit (0) + + mopts = {} + mopts['builddir'] = '' + mopts['objext'] = '.o' + mopts['exeext'] = '.out' + mopts['relpath'] = False + mopts['template'] = None + mopts['files'] = False + mopts['modules'] = False + mopts['calls'] = False + mopts['exclude'] = [] + debug = False + + # Process options + for o, a in opts: + if o in ("-?", "-h", "--help"): + print __doc__ + sys.exit (0) + elif o == "--builddir": + mopts['builddir'] = a + elif o == "--objext": + mopts['objext'] = a + elif o == "--exeext": + mopts['exeext'] = a + elif o == "--relpath": + mopts['relpath'] = True + elif o == "--debug": + debug = True + elif o == "--template": + mopts['template'] = a + elif o == "--files": + mopts['files'] = True + elif o == "--modules": + mopts['modules'] = True + elif o == "--calls": + mopts['calls'] = True + elif o == "--exclude": + mopts['exclude'] = a.split (' ') + + maincfilename = args[0] + + search_list = [] + if len (args) > 1: + search_list.extend (args[1:len (args)]) + + if debug: + print >> sys.stderr, search_list + search_path = pathsep.join (search_list) + if debug: + print >> sys.stderr, 'template', mopts['template'] + print >> sys.stderr, 'cfile', maincfilename + print >> sys.stderr, 'search_path', search_path + print >> sys.stderr, 'CWD = ', os.getcwd() + + if os.path.isdir (maincfilename): + if debug: + print >> sys.stderr, 'Searching ' + maincfilename + maincfilename = maincfilename_find (maincfilename) + if not maincfilename: + sys.exit (1) + + if debug: + print >> sys.stderr, 'Found C file ' + maincfilename + + includes = '-I' + ' -I'.join (search_list) + cflags = '-mmcu=atmega32u2' + opts = '-Os' + gcc = 'avr-gcc' + ' ' + cflags + ' ' + opts + ' ' + includes + + # Search main c file looking for header files included with #include + # and any header files included by the header files + + filedeps = {} + moduledeps = {} + functiondeps = {} + files_find (gcc, maincfilename, search_path, filedeps, moduledeps, functiondeps, '', debug) + + cfilelist = cfiles_get (filedeps) + ofilelist = [cfile[:-2] + mopts['objext'] for cfile in cfilelist] + outfile = maincfilename[:-2] + mopts['exeext'] + filedeps[outfile] = ofilelist + for ofile in ofilelist: + deps = [] + deps.append (ofile[:-2] + '.c') + filedeps[ofile] = deps + + # print >> sys.stderr, moduledeps + # print >> sys.stderr, filedeps + + if mopts['calls']: + deps_print ('main', functiondeps, mopts) + + if mopts['files']: + deps_print (outfile, filedeps, mopts) + + if mopts['modules']: + target, ext = os.path.splitext (os.path.basename (maincfilename)) + deps_print (target, moduledeps, mopts) + + if mopts['template']: + makefile_print (mopts, mopts['template'], maincfilename, filedeps, + search_list, debug) + + return 0 + + + except Usage, err: + print >> sys.stderr, err.msg + print >> sys.stderr, "for help use --help" + return 2 + + +if __name__ == "__main__": + sys.exit (main()) + diff --git a/fonts/Makefile b/fonts/Makefile new file mode 100644 index 0000000..901cf11 --- /dev/null +++ b/fonts/Makefile @@ -0,0 +1,18 @@ +CC = gcc +CFLAGS = -O2 -Wall -W -g + +FONTDEFS = $(wildcard *.txt) +FONTS = $(FONTDEFS:.txt=.h) + + +all: fontgen $(FONTS) + + +%.h: %.txt fontgen + ./fontgen $(@:.h=) < $^ > $@ + +fontgen: fontgen.c + $(CC) $(CFLAGS) $< -o $@ + +clean: + -rm *.o fontgen diff --git a/fonts/README b/fonts/README new file mode 100644 index 0000000..61b3933 --- /dev/null +++ b/fonts/README @@ -0,0 +1,123 @@ +To create a new font, edit a font definition file with text editor. +Then run make. This will run fontgen to create a corresponding .h +file that can be included in a program. + +Here's an example format for a font definition file. The first line +is a comment, the second line defines the width of each font +character, and the third light defines the height of each font +character. The following lines describe the font characters. The +. characters indicate an off pixel and other characters (such as @ or +#) indicate an on pixel. The first character is the corresponding +ASCII symbol. + +The font does not need to be contiguous. Empty entries will be +displayed as blanks. However, they will take up memory. + +# This is a demo font with only 3 characters. +width=5 +height=7 + +..... +..... +..... +..... +..... +..... +..... +! +.@@.. +.@@.. +.@@.. +.@@.. +.@@.. +..... +.@@.. +" +.@.@. +.@.@. +.@.@. +..... +..... +..... +..... + + +The font entries with indices below 32 can be specified using \nnn +where nnn is the octal representation for the desired index. For +example, here are three faces assigned to indices 1, 2, 3. Note, +index 0 cannot be used since this marks the end of a string. + +# Here's a demo font of three faces. +width=5 +height=7 +\001 +.@.@. +.@.@. +.@.@. +..... +..... +@...@ +.@@@. +\002 +..... +@@.@@ +@@.@@ +..... +..@.. +@...@ +.@@@. +\003 +..... +@@.@@ +@@.@@ +..... +..@.. +.@@@. +@...@ + +The tricky bit is representing these codes in C. You have to do +something like tinygl_text ("\001\002\003"); An alternative, is to +assign these to printable characters. For example, + +# Here's a demo font of three faces. +width=5 +height=7 +A +.@.@. +.@.@. +.@.@. +..... +..... +@...@ +.@@@. +B +..... +@@.@@ +@@.@@ +..... +..@.. +@...@ +.@@@. +C +..... +@@.@@ +@@.@@ +..... +..@.. +.@@@. +@...@ + +Then you can display them with tinygl_text ("ABC"); Unfortunately, the +font cannot display ABC as well. One solution is to load multiple +fonts and switch between them. + +#include "faces5x7.h" +#include "font5x7_1.h" + +tinygl_font_set (&faces5x7); +tinygl_text ("ABC"); + +tinygl_font_set (&font5x7_1); +tinygl_text ("ABC"); + +However, only one font can be active at a time. diff --git a/fonts/font3x5_1.h b/fonts/font3x5_1.h new file mode 100644 index 0000000..f2bc59a --- /dev/null +++ b/fonts/font3x5_1.h @@ -0,0 +1,162 @@ +/** @file font3x5_1.h + @author fontgen + @date 28 Sep 2010 + + @defgroup font3x5_1 This is a tiny 3x5 font with uppercase, digits, and symbols only. + M.P. Hayes 2 Sep 2010 + +*/ + +#ifndef FONT3X5_1_H_ +#define FONT3X5_1_H_ + +#define FONT3X5_1_WIDTH 3 +#define FONT3X5_1_HEIGHT 5 +#define FONT3X5_1_OFFSET 32 +#define FONT3X5_1_SIZE 59 + +#ifndef FONT_WIDTH +#define FONT_WIDTH 3 +#endif +#ifndef FONT_HEIGHT +#define FONT_HEIGHT 5 +#endif +#ifndef FONT_OFFSET +#define FONT_OFFSET 32 +#endif +#ifndef FONT_SIZE_MAX +#define FONT_SIZE_MAX 59 +#endif +#include "font.h" + +static font_t font3x5_1 = +{ + .flags = 1, /* (packed) */ + .width = 3, + .height = 5, + .offset = 32, + .size = 59, + .bytes = 2, + .data = + { + /* */ + 0x00, 0x00, + /* ! */ + 0x92, 0x20, + /* " */ + 0xeb, 0x73, + /* # */ + 0x7d, 0x5f, + /* $ */ + 0xde, 0x3d, + /* % */ + 0xa5, 0x52, + /* & */ + 0xea, 0x7a, + /* ' */ + 0x14, 0x00, + /* ( */ + 0x4a, 0x22, + /* ) */ + 0x22, 0x29, + /* * */ + 0xba, 0x2e, + /* + */ + 0xd0, 0x05, + /* , */ + 0x00, 0x14, + /* - */ + 0xc0, 0x01, + /* . */ + 0x00, 0x20, + /* / */ + 0xa4, 0x12, + /* 0 */ + 0x6a, 0x2b, + /* 1 */ + 0x9a, 0x74, + /* 2 */ + 0xa3, 0x72, + /* 3 */ + 0xa3, 0x38, + /* 4 */ + 0xed, 0x49, + /* 5 */ + 0x8f, 0x38, + /* 6 */ + 0xce, 0x2a, + /* 7 */ + 0xa7, 0x12, + /* 8 */ + 0xaa, 0x2a, + /* 9 */ + 0xae, 0x49, + /* : */ + 0x10, 0x04, + /* ; */ + 0x10, 0x14, + /* < */ + 0x54, 0x44, + /* = */ + 0x38, 0x0e, + /* > */ + 0x11, 0x15, + /* ? */ + 0x2a, 0x25, + /* @ */ + 0x00, 0x00, + /* A */ + 0xea, 0x5b, + /* B */ + 0xeb, 0x3a, + /* C */ + 0x4f, 0x72, + /* D */ + 0x6b, 0x3b, + /* E */ + 0xcf, 0x73, + /* F */ + 0xcf, 0x13, + /* G */ + 0x4f, 0x7a, + /* H */ + 0xed, 0x5b, + /* I */ + 0x97, 0x74, + /* J */ + 0x24, 0x7b, + /* K */ + 0x5d, 0x56, + /* L */ + 0x49, 0x72, + /* M */ + 0xfd, 0x5b, + /* N */ + 0x6f, 0x5b, + /* O */ + 0x6f, 0x7b, + /* P */ + 0xef, 0x13, + /* Q */ + 0x6f, 0x7f, + /* R */ + 0xef, 0x57, + /* S */ + 0xce, 0x38, + /* T */ + 0x97, 0x24, + /* U */ + 0x6d, 0x7b, + /* V */ + 0x6d, 0x2b, + /* W */ + 0xed, 0x5f, + /* X */ + 0xad, 0x5a, + /* Y */ + 0xad, 0x24, + /* Z */ + 0xa7, 0x72, + } +}; +#endif /* FONT3X5_1_H_ */ diff --git a/fonts/font3x5_1.txt b/fonts/font3x5_1.txt new file mode 100644 index 0000000..b58aa06 --- /dev/null +++ b/fonts/font3x5_1.txt @@ -0,0 +1,358 @@ +# This is a tiny 3x5 font with uppercase, digits, and symbols only. +# M.P. Hayes 2 Sep 2010 +width=3 +height=5 + +... +... +... +... +... +! +.@. +.@. +.@. +... +.@. +" +@@. +@.@ +@@@ +@.. +@@@ +# +@.@ +@@@ +@.@ +@@@ +@.@ +$ +.@@ +@@. +@@@ +.@@ +@@. +% +@.@ +..@ +.@. +@.. +@.@ +& +.@. +@.@ +@@. +@.@ +@@@ +' +..@ +.@. +... +... +... +( +.@. +@.. +@.. +@.. +.@. +) +.@. +..@ +..@ +..@ +.@. +* +.@. +@@@ +.@. +@@@ +.@. ++ +... +.@. +@@@ +.@. +... +, +... +... +... +.@. +@.. +- +... +... +@@@ +... +... +. +... +... +... +... +.@. +/ +..@ +..@ +.@. +@.. +@.. +0 +.@. +@.@ +@.@ +@.@ +.@. +1 +.@. +@@. +.@. +.@. +@@@ +2 +@@. +..@ +.@. +@.. +@@@ +3 +@@. +..@ +.@. +..@ +@@. +4 +@.@ +@.@ +@@@ +..@ +..@ +5 +@@@ +@.. +.@. +..@ +@@. +6 +.@@ +@.. +@@. +@.@ +.@. +7 +@@@ +..@ +.@. +@.. +@.. +8 +.@. +@.@ +.@. +@.@ +.@. +9 +.@@ +@.@ +.@@ +..@ +..@ +: +... +.@. +... +.@. +... +; +... +.@. +... +.@. +@.. +< +..@ +.@. +@.. +.@. +..@ += +... +@@@ +... +@@@ +... +> +@.. +.@. +..@ +.@. +@.. +? +.@. +@.@ +..@ +.@. +.@. +. +... +... +... +... +.@. +A +.@. +@.@ +@@@ +@.@ +@.@ +B +@@. +@.@ +@@. +@.@ +@@. +C +@@@ +@.. +@.. +@.. +@@@ +D +@@. +@.@ +@.@ +@.@ +@@. +E +@@@ +@.. +@@@ +@.. +@@@ +F +@@@ +@.. +@@@ +@.. +@.. +G +@@@ +@.. +@.. +@.@ +@@@ +H +@.@ +@.@ +@@@ +@.@ +@.@ +I +@@@ +.@. +.@. +.@. +@@@ +J +..@ +..@ +..@ +@.@ +@@@ +K +@.@ +@@. +@.. +@@. +@.@ +L +@.. +@.. +@.. +@.. +@@@ +M +@.@ +@@@ +@@@ +@.@ +@.@ +N +@@@ +@.@ +@.@ +@.@ +@.@ +O +@@@ +@.@ +@.@ +@.@ +@@@ +P +@@@ +@.@ +@@@ +@.. +@.. +Q +@@@ +@.@ +@.@ +@@@ +@@@ +R +@@@ +@.@ +@@@ +@@. +@.@ +S +.@@ +@.. +@@. +..@ +@@. +T +@@@ +.@. +.@. +.@. +.@. +U +@.@ +@.@ +@.@ +@.@ +@@@ +V +@.@ +@.@ +@.@ +@.@ +.@. +W +@.@ +@.@ +@@@ +@@@ +@.@ +X +@.@ +@.@ +.@. +@.@ +@.@ +Y +@.@ +@.@ +.@. +.@. +.@. +Z +@@@ +..@ +.@. +@.. +@@@ diff --git a/fonts/font5x7_1.h b/fonts/font5x7_1.h new file mode 100644 index 0000000..5c5ef55 --- /dev/null +++ b/fonts/font5x7_1.h @@ -0,0 +1,234 @@ +/** @file font5x7_1.h + @author fontgen + @date 28 Sep 2010 + + @defgroup font5x7_1 This is a 5x7 font with uppercase, lowercase, digits, and symbols. + M.P. Hayes 2 Sep 2010 + +*/ + +#ifndef FONT5X7_1_H_ +#define FONT5X7_1_H_ + +#define FONT5X7_1_WIDTH 5 +#define FONT5X7_1_HEIGHT 7 +#define FONT5X7_1_OFFSET 32 +#define FONT5X7_1_SIZE 95 + +#ifndef FONT_WIDTH +#define FONT_WIDTH 5 +#endif +#ifndef FONT_HEIGHT +#define FONT_HEIGHT 7 +#endif +#ifndef FONT_OFFSET +#define FONT_OFFSET 32 +#endif +#ifndef FONT_SIZE_MAX +#define FONT_SIZE_MAX 95 +#endif +#include "font.h" + +static const font_t font5x7_1 = +{ + .flags = 1, /* (packed) */ + .width = 5, + .height = 7, + .offset = 32, + .size = 95, + .bytes = 5, + .data = + { + /* */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* ! */ + 0xc6, 0x18, 0x63, 0x80, 0x01, + /* " */ + 0x4a, 0x29, 0x00, 0x00, 0x00, + /* # */ + 0x4a, 0x7d, 0xf5, 0x95, 0x02, + /* $ */ + 0xc4, 0x17, 0x47, 0x1f, 0x01, + /* % */ + 0x63, 0x22, 0x22, 0x32, 0x06, + /* & */ + 0x26, 0x15, 0x51, 0x93, 0x05, + /* ' */ + 0x86, 0x08, 0x00, 0x00, 0x00, + /* ( */ + 0x88, 0x08, 0x21, 0x08, 0x02, + /* ) */ + 0x82, 0x20, 0x84, 0x88, 0x00, + /* * */ + 0x80, 0x54, 0x57, 0x09, 0x00, + /* + */ + 0x80, 0x90, 0x4f, 0x08, 0x00, + /* , */ + 0x00, 0x00, 0x60, 0x88, 0x00, + /* - */ + 0x00, 0x80, 0x0f, 0x00, 0x00, + /* . */ + 0x00, 0x00, 0x00, 0x8c, 0x01, + /* / */ + 0x00, 0x22, 0x22, 0x02, 0x00, + /* 0 */ + 0x2e, 0xe6, 0x3a, 0xa3, 0x03, + /* 1 */ + 0xc4, 0x10, 0x42, 0x88, 0x03, + /* 2 */ + 0x2e, 0x42, 0x44, 0xc4, 0x07, + /* 3 */ + 0x2e, 0x42, 0x07, 0xa3, 0x03, + /* 4 */ + 0x88, 0xa9, 0xf4, 0x11, 0x02, + /* 5 */ + 0x3f, 0x84, 0x07, 0xa3, 0x03, + /* 6 */ + 0x2e, 0x86, 0x17, 0xa3, 0x03, + /* 7 */ + 0x3f, 0x42, 0x44, 0x08, 0x01, + /* 8 */ + 0x2e, 0x46, 0x17, 0xa3, 0x03, + /* 9 */ + 0x2e, 0x46, 0x0f, 0xa3, 0x03, + /* : */ + 0xc0, 0x18, 0x60, 0x0c, 0x00, + /* ; */ + 0xc0, 0x18, 0x60, 0x88, 0x00, + /* < */ + 0x88, 0x88, 0x20, 0x08, 0x02, + /* = */ + 0x00, 0x7c, 0xf0, 0x01, 0x00, + /* > */ + 0x82, 0x20, 0x88, 0x88, 0x00, + /* ? */ + 0x2e, 0x42, 0x44, 0x00, 0x01, + /* @ */ + 0x2e, 0x42, 0x5b, 0xab, 0x03, + /* A */ + 0x44, 0xc5, 0x1f, 0x63, 0x04, + /* B */ + 0x2f, 0xc6, 0x17, 0xe3, 0x03, + /* C */ + 0x2e, 0x86, 0x10, 0xa2, 0x03, + /* D */ + 0x2f, 0xc6, 0x18, 0xe3, 0x03, + /* E */ + 0x3f, 0x84, 0x17, 0xc2, 0x07, + /* F */ + 0x3f, 0x84, 0x17, 0x42, 0x00, + /* G */ + 0x2e, 0x86, 0x1e, 0xa3, 0x07, + /* H */ + 0x31, 0xc6, 0x1f, 0x63, 0x04, + /* I */ + 0x8e, 0x10, 0x42, 0x88, 0x03, + /* J */ + 0x10, 0x42, 0x18, 0xa3, 0x03, + /* K */ + 0x31, 0x95, 0x51, 0x52, 0x04, + /* L */ + 0x21, 0x84, 0x10, 0xc2, 0x07, + /* M */ + 0x71, 0xd7, 0x1a, 0x63, 0x04, + /* N */ + 0x71, 0xd6, 0x1c, 0x63, 0x04, + /* O */ + 0x2e, 0xc6, 0x18, 0xa3, 0x03, + /* P */ + 0x2f, 0xc6, 0x17, 0x42, 0x00, + /* Q */ + 0x2e, 0xc6, 0x58, 0x93, 0x05, + /* R */ + 0x2f, 0xc6, 0x17, 0x63, 0x04, + /* S */ + 0x2e, 0x06, 0x07, 0xa3, 0x03, + /* T */ + 0x9f, 0x10, 0x42, 0x08, 0x01, + /* U */ + 0x31, 0xc6, 0x18, 0xa3, 0x03, + /* V */ + 0x31, 0xc6, 0x18, 0x15, 0x01, + /* W */ + 0x31, 0xc6, 0x5a, 0xab, 0x02, + /* X */ + 0x31, 0x2a, 0xa2, 0x62, 0x04, + /* Y */ + 0x31, 0x2a, 0x42, 0x08, 0x01, + /* Z */ + 0x1f, 0x22, 0x22, 0xc2, 0x07, + /* [ */ + 0x27, 0x84, 0x10, 0xc2, 0x01, + /* \ */ + 0x20, 0x08, 0x82, 0x20, 0x00, + /* ] */ + 0x0e, 0x21, 0x84, 0x90, 0x03, + /* ^ */ + 0x44, 0x45, 0x00, 0x00, 0x00, + /* _ */ + 0x00, 0x00, 0x00, 0xc0, 0x07, + /* ` */ + 0x82, 0x20, 0x00, 0x00, 0x00, + /* a */ + 0x00, 0x38, 0xe8, 0xa3, 0x07, + /* b */ + 0x21, 0xb4, 0x19, 0xe3, 0x03, + /* c */ + 0x00, 0xb8, 0x10, 0xa2, 0x03, + /* d */ + 0x10, 0xda, 0x1c, 0xa3, 0x07, + /* e */ + 0x00, 0xb8, 0xf8, 0x83, 0x03, + /* f */ + 0x4c, 0x8a, 0x23, 0x84, 0x00, + /* g */ + 0xc0, 0xc7, 0xe8, 0xa1, 0x03, + /* h */ + 0x21, 0xb4, 0x19, 0x63, 0x04, + /* i */ + 0x04, 0x18, 0x42, 0x88, 0x03, + /* j */ + 0x08, 0x30, 0x84, 0x92, 0x01, + /* k */ + 0x21, 0xa4, 0x32, 0x4a, 0x02, + /* l */ + 0x86, 0x10, 0x42, 0x88, 0x03, + /* m */ + 0x00, 0xac, 0x5a, 0x63, 0x04, + /* n */ + 0x00, 0xb4, 0x19, 0x63, 0x04, + /* o */ + 0x00, 0xb8, 0x18, 0xa3, 0x03, + /* p */ + 0x00, 0xbc, 0xf8, 0x42, 0x00, + /* q */ + 0x00, 0xd8, 0xec, 0x21, 0x04, + /* r */ + 0x00, 0xb4, 0x19, 0x42, 0x00, + /* s */ + 0x00, 0xb8, 0xe0, 0xe0, 0x03, + /* t */ + 0x42, 0x1c, 0x21, 0x24, 0x03, + /* u */ + 0x00, 0xc4, 0x18, 0xb3, 0x05, + /* v */ + 0x00, 0xc4, 0x18, 0x15, 0x01, + /* w */ + 0x00, 0xc4, 0x5a, 0xab, 0x02, + /* x */ + 0x00, 0x44, 0x45, 0x54, 0x04, + /* y */ + 0x00, 0xc4, 0xe8, 0xa1, 0x03, + /* z */ + 0x00, 0x7c, 0x44, 0xc4, 0x07, + /* { */ + 0x88, 0x10, 0x41, 0x08, 0x02, + /* | */ + 0x84, 0x10, 0x42, 0x08, 0x01, + /* } */ + 0x82, 0x10, 0x44, 0x88, 0x00, + /* ~ */ + 0x00, 0x88, 0x8a, 0x00, 0x00, + } +}; +#endif /* FONT5X7_1_H_ */ diff --git a/fonts/font5x7_1.txt b/fonts/font5x7_1.txt new file mode 100644 index 0000000..6709f79 --- /dev/null +++ b/fonts/font5x7_1.txt @@ -0,0 +1,764 @@ +# This is a 5x7 font with uppercase, lowercase, digits, and symbols. +# M.P. Hayes 2 Sep 2010 +width=5 +height=7 + +..... +..... +..... +..... +..... +..... +..... +! +.@@.. +.@@.. +.@@.. +.@@.. +.@@.. +..... +.@@.. +" +.@.@. +.@.@. +.@.@. +..... +..... +..... +..... +# +.@.@. +.@.@. +@@@@@ +.@.@. +@@@@@ +.@.@. +.@.@. +$ +..@.. +.@@@@ +@.@.. +.@@@. +..@.@ +@@@@. +..@.. +% +@@... +@@..@ +...@. +..@.. +.@... +@..@@ +...@@ +& +.@@.. +@..@. +@.@.. +.@... +@.@.@ +@..@. +.@@.@ +' +.@@.. +..@.. +.@... +..... +..... +..... +..... +( +...@. +..@.. +.@... +.@... +.@... +..@.. +...@. +) +.@... +..@.. +...@. +...@. +...@. +..@.. +.@... +* +..... +..@.. +@.@.@ +.@@@. +@.@.@ +..@.. +..... ++ +..... +..@.. +..@.. +@@@@@ +..@.. +..@.. +..... +, +..... +..... +..... +..... +.@@.. +..@.. +.@... +- +..... +..... +..... +@@@@@ +..... +..... +..... +. +..... +..... +..... +..... +..... +.@@.. +.@@.. +/ +..... +....@ +...@. +..@.. +.@... +@.... +..... +0 +.@@@. +@...@ +@..@@ +@.@.@ +@@..@ +@...@ +.@@@. +1 +..@.. +.@@.. +..@.. +..@.. +..@.. +..@.. +.@@@. +2 +.@@@. +@...@ +....@ +...@. +..@.. +.@... +@@@@@ +3 +.@@@. +@...@ +....@ +.@@@. +....@ +@...@ +.@@@. +4 +...@. +..@@. +.@.@. +@..@. +@@@@@ +...@. +...@. +5 +@@@@@ +@.... +@.... +@@@@. +....@ +@...@ +.@@@. +6 +.@@@. +@...@ +@.... +@@@@. +@...@ +@...@ +.@@@. +7 +@@@@@ +@...@ +....@ +...@. +..@.. +..@.. +..@.. +8 +.@@@. +@...@ +@...@ +.@@@. +@...@ +@...@ +.@@@. +9 +.@@@. +@...@ +@...@ +.@@@@ +....@ +@...@ +.@@@. +: +..... +.@@.. +.@@.. +..... +.@@.. +.@@.. +..... +; +..... +.@@.. +.@@.. +..... +.@@.. +..@.. +.@... +< +...@. +..@.. +.@... +@.... +.@... +..@.. +...@. += +..... +..... +@@@@@ +..... +@@@@@ +..... +..... +> +.@... +..@.. +...@. +....@ +...@. +..@.. +.@... +? +.@@@. +@...@ +....@ +...@. +..@.. +..... +..@.. +@ +.@@@. +@...@ +....@ +.@@.@ +@.@.@ +@.@.@ +.@@@. +A +..@.. +.@.@. +@...@ +@@@@@ +@...@ +@...@ +@...@ +B +@@@@. +@...@ +@...@ +@@@@. +@...@ +@...@ +@@@@. +C +.@@@. +@...@ +@.... +@.... +@.... +@...@ +.@@@. +D +@@@@. +@...@ +@...@ +@...@ +@...@ +@...@ +@@@@. +E +@@@@@ +@.... +@.... +@@@@. +@.... +@.... +@@@@@ +F +@@@@@ +@.... +@.... +@@@@. +@.... +@.... +@.... +G +.@@@. +@...@ +@.... +@.@@@ +@...@ +@...@ +.@@@@ +H +@...@ +@...@ +@...@ +@@@@@ +@...@ +@...@ +@...@ +I +.@@@. +..@.. +..@.. +..@.. +..@.. +..@.. +.@@@. +J +....@ +....@ +....@ +....@ +@...@ +@...@ +.@@@. +K +@...@ +@..@. +@.@.. +@@... +@.@.. +@..@. +@...@ +L +@.... +@.... +@.... +@.... +@.... +@.... +@@@@@ +M +@...@ +@@.@@ +@.@.@ +@.@.@ +@...@ +@...@ +@...@ +N +@...@ +@@..@ +@.@.@ +@..@@ +@...@ +@...@ +@...@ +O +.@@@. +@...@ +@...@ +@...@ +@...@ +@...@ +.@@@. +P +@@@@. +@...@ +@...@ +@@@@. +@.... +@.... +@.... +Q +.@@@. +@...@ +@...@ +@...@ +@.@.@ +@..@. +.@@.@ +R +@@@@. +@...@ +@...@ +@@@@. +@...@ +@...@ +@...@ +S +.@@@. +@...@ +@.... +.@@@. +....@ +@...@ +.@@@. +T +@@@@@ +..@.. +..@.. +..@.. +..@.. +..@.. +..@.. +U +@...@ +@...@ +@...@ +@...@ +@...@ +@...@ +.@@@. +V +@...@ +@...@ +@...@ +@...@ +@...@ +.@.@. +..@.. +W +@...@ +@...@ +@...@ +@.@.@ +@.@.@ +@.@.@ +.@.@. +X +@...@ +@...@ +.@.@. +..@.. +.@.@. +@...@ +@...@ +Y +@...@ +@...@ +.@.@. +..@.. +..@.. +..@.. +..@.. +Z +@@@@@ +....@ +...@. +..@.. +.@... +@.... +@@@@@ +[ +@@@.. +@.... +@.... +@.... +@.... +@.... +@@@.. +\ +..... +@.... +.@... +..@.. +...@. +....@ +..... +] +.@@@. +...@. +...@. +...@. +...@. +...@. +.@@@. +^ +..@.. +.@.@. +@...@ +..... +..... +..... +..... +_ +..... +..... +..... +..... +..... +..... +@@@@@ +` +.@... +..@.. +...@. +..... +..... +..... +..... +a +..... +..... +.@@@. +....@ +.@@@@ +@...@ +.@@@@ +b +@.... +@.... +@.@@. +@@..@ +@...@ +@...@ +@@@@. +c +..... +..... +.@@@. +@.... +@.... +@...@ +.@@@. +d +....@ +....@ +.@@.@ +@..@@ +@...@ +@...@ +.@@@@ +e +..... +..... +.@@@. +@...@ +@@@@@ +@.... +.@@@. +f +..@@. +.@..@ +.@... +@@@.. +.@... +.@... +.@... +g +..... +.@@@@ +@...@ +@...@ +.@@@@ +....@ +.@@@. +h +@.... +@.... +@.@@. +@@..@ +@...@ +@...@ +@...@ +i +..@.. +..... +.@@.. +..@.. +..@.. +..@.. +.@@@. +j +...@. +..... +..@@. +...@. +...@. +@..@. +.@@.. +k +@.... +@.... +@..@. +@.@.. +@@... +@.@.. +@..@. +l +.@@.. +..@.. +..@.. +..@.. +..@.. +..@.. +.@@@. +m +..... +..... +@@.@. +@.@.@ +@.@.@ +@...@ +@...@ +n +..... +..... +@.@@. +@@..@ +@...@ +@...@ +@...@ +o +..... +..... +.@@@. +@...@ +@...@ +@...@ +.@@@. +p +..... +..... +@@@@. +@...@ +@@@@. +@.... +@.... +q +..... +..... +.@@.@ +@..@@ +.@@@@ +....@ +....@ +r +..... +..... +@.@@. +@@..@ +@.... +@.... +@.... +s +..... +..... +.@@@. +@.... +.@@@. +....@ +@@@@. +t +.@... +.@... +@@@.. +.@... +.@... +.@..@ +..@@. +u +..... +..... +@...@ +@...@ +@...@ +@..@@ +.@@.@ +v +..... +..... +@...@ +@...@ +@...@ +.@.@. +..@.. +w +..... +..... +@...@ +@.@.@ +@.@.@ +@.@.@ +.@.@. +x +..... +..... +@...@ +.@.@. +..@.. +.@.@. +@...@ +y +..... +..... +@...@ +@...@ +.@@@@ +....@ +.@@@. +z +..... +..... +@@@@@ +...@. +..@.. +.@... +@@@@@ +{ +...@. +..@.. +..@.. +.@... +..@.. +..@.. +...@. +| +..@.. +..@.. +..@.. +..@.. +..@.. +..@.. +..@.. +} +.@... +..@.. +..@.. +...@. +..@.. +..@.. +.@... +~ +..... +..... +.@... +@.@.@ +...@. +..... +..... diff --git a/fonts/font5x7_2.h b/fonts/font5x7_2.h new file mode 100644 index 0000000..7af0cdc --- /dev/null +++ b/fonts/font5x7_2.h @@ -0,0 +1,186 @@ +/** @file font5x7_2.h + @author fontgen + @date 28 Sep 2010 + + @defgroup font5x7_2 This is a 5x7 font with uppercase, digits, and some symbols. + M.P. Hayes 2 Sep 2010 + +*/ + +#ifndef FONT5X7_2_H_ +#define FONT5X7_2_H_ + +#define FONT5X7_2_WIDTH 5 +#define FONT5X7_2_HEIGHT 7 +#define FONT5X7_2_OFFSET 32 +#define FONT5X7_2_SIZE 71 + +#ifndef FONT_WIDTH +#define FONT_WIDTH 5 +#endif +#ifndef FONT_HEIGHT +#define FONT_HEIGHT 7 +#endif +#ifndef FONT_OFFSET +#define FONT_OFFSET 32 +#endif +#ifndef FONT_SIZE_MAX +#define FONT_SIZE_MAX 71 +#endif +#include "font.h" + +static font_t font5x7_2 = +{ + .flags = 1, /* (packed) */ + .width = 5, + .height = 7, + .offset = 32, + .size = 71, + .bytes = 5, + .data = + { + /* */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* ! */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* " */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* # */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* $ */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* % */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* & */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* ' */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* ( */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* ) */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* * */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* + */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* , */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* - */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* . */ + 0x00, 0x00, 0x02, 0x00, 0x00, + /* / */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0 */ + 0x2e, 0xe6, 0x3a, 0xa3, 0x03, + /* 1 */ + 0xc4, 0x10, 0x42, 0x88, 0x03, + /* 2 */ + 0x2e, 0x42, 0x44, 0xc4, 0x07, + /* 3 */ + 0x1f, 0x11, 0x04, 0xa3, 0x03, + /* 4 */ + 0x88, 0xa9, 0xf4, 0x11, 0x02, + /* 5 */ + 0x3f, 0x3c, 0x08, 0xa3, 0x03, + /* 6 */ + 0x4c, 0x84, 0x17, 0xa3, 0x03, + /* 7 */ + 0x1f, 0x22, 0x42, 0x08, 0x01, + /* 8 */ + 0x2f, 0x46, 0x17, 0xa3, 0x03, + /* 9 */ + 0x2e, 0x46, 0x0f, 0x91, 0x01, + /* : */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* ; */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* < */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* = */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* > */ + 0x41, 0x10, 0x44, 0x44, 0x00, + /* ? */ + 0x2e, 0x42, 0x44, 0x00, 0x01, + /* @ */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* A */ + 0x2e, 0xc6, 0xf8, 0x63, 0x04, + /* B */ + 0x4f, 0x4a, 0x27, 0xe5, 0x03, + /* C */ + 0x2e, 0x86, 0x10, 0xa2, 0x03, + /* D */ + 0x4f, 0x4a, 0x29, 0xe5, 0x03, + /* E */ + 0x3f, 0x84, 0x1f, 0xc2, 0x07, + /* F */ + 0x3f, 0x84, 0x17, 0x42, 0x00, + /* G */ + 0x2e, 0x86, 0x1c, 0xa3, 0x07, + /* H */ + 0x31, 0xc6, 0x1f, 0x63, 0x04, + /* I */ + 0x8e, 0x10, 0x42, 0x88, 0x03, + /* J */ + 0x1c, 0x21, 0x84, 0x92, 0x01, + /* K */ + 0x31, 0x95, 0x51, 0x52, 0x04, + /* L */ + 0x21, 0x84, 0x10, 0xc2, 0x07, + /* M */ + 0x71, 0xd7, 0x1a, 0x63, 0x04, + /* N */ + 0x71, 0xce, 0x9a, 0x73, 0x04, + /* O */ + 0x2e, 0xc6, 0x18, 0xa3, 0x03, + /* P */ + 0x2f, 0xc6, 0x17, 0x42, 0x00, + /* Q */ + 0x2e, 0xc6, 0x58, 0xd3, 0x05, + /* R */ + 0x2f, 0xc6, 0x57, 0x52, 0x04, + /* S */ + 0x2e, 0x06, 0x07, 0xa3, 0x03, + /* T */ + 0x9f, 0x10, 0x42, 0x08, 0x01, + /* U */ + 0x31, 0xc6, 0x18, 0xa3, 0x03, + /* V */ + 0x31, 0xc6, 0x18, 0x15, 0x01, + /* W */ + 0x31, 0xc6, 0x5a, 0x77, 0x04, + /* X */ + 0x31, 0x2a, 0xa2, 0x62, 0x04, + /* Y */ + 0x31, 0x46, 0x45, 0x08, 0x01, + /* Z */ + 0x1f, 0x22, 0x22, 0xc2, 0x07, + /* [ */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* \ */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* ] */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* ^ */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* _ */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* ` */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* a */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* b */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* c */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* d */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* e */ + 0x00, 0x00, 0x00, 0x00, 0x00, + /* f */ + 0x2e, 0x86, 0xf0, 0x43, 0x00, + } +}; +#endif /* FONT5X7_2_H_ */ diff --git a/fonts/font5x7_2.txt b/fonts/font5x7_2.txt new file mode 100644 index 0000000..c797492 --- /dev/null +++ b/fonts/font5x7_2.txt @@ -0,0 +1,332 @@ +# This is a 5x7 font with uppercase, digits, and some symbols. +# M.P. Hayes 2 Sep 2010 +width=5 +height=7 + +..... +..... +..... +..... +..... +..... +..... +. +..... +..... +..... +..@.. +..... +..... +..... +> +@.... +.@... +..@.. +...@. +..@.. +.@... +@.... +? +.@@@. +@...@ +....@ +...@. +..@.. +..... +..@.. +0 +.@@@. +@...@ +@..@@ +@.@.@ +@@..@ +@...@ +.@@@. +1 +..@.. +.@@.. +..@.. +..@.. +..@.. +..@.. +.@@@. +2 +.@@@. +@...@ +....@ +...@. +..@.. +.@... +@@@@@ +3 +@@@@@ +...@. +..@.. +...@. +....@ +@...@ +.@@@. +4 +...@. +..@@. +.@.@. +@..@. +@@@@@ +...@. +...@. +5 +@@@@@ +@.... +@@@@. +....@ +....@ +@...@ +.@@@. +6 +..@@. +.@... +@.... +@@@@. +@...@ +@...@ +.@@@. +7 +@@@@@ +....@ +...@. +..@.. +..@.. +..@.. +..@.. +8 +@@@@. +@...@ +@...@ +.@@@. +@...@ +@...@ +.@@@. +9 +.@@@. +@...@ +@...@ +.@@@@ +....@ +...@. +.@@.. +A +.@@@. +@...@ +@...@ +@...@ +@@@@@ +@...@ +@...@ +B +@@@@. +.@..@ +.@..@ +.@@@. +.@..@ +.@..@ +@@@@. +C +.@@@. +@...@ +@.... +@.... +@.... +@...@ +.@@@. +D +@@@@. +.@..@ +.@..@ +.@..@ +.@..@ +.@..@ +@@@@. +E +@@@@@ +@.... +@.... +@@@@@ +@.... +@.... +@@@@@ +F +@@@@@ +@.... +@.... +@@@@. +@.... +@.... +@.... +f +.@@@. +@...@ +@.... +@.... +@@@@@ +@.... +@.... +G +.@@@. +@...@ +@.... +@..@@ +@...@ +@...@ +.@@@@ +H +@...@ +@...@ +@...@ +@@@@@ +@...@ +@...@ +@...@ +I +.@@@. +..@.. +..@.. +..@.. +..@.. +..@.. +.@@@. +J +..@@@ +...@. +...@. +...@. +...@. +@..@. +.@@.. +K +@...@ +@..@. +@.@.. +@@... +@.@.. +@..@. +@...@ +L +@.... +@.... +@.... +@.... +@.... +@.... +@@@@@ +M +@...@ +@@.@@ +@.@.@ +@.@.@ +@...@ +@...@ +@...@ +N +@...@ +@@..@ +@@..@ +@.@.@ +@..@@ +@..@@ +@...@ +O +.@@@. +@...@ +@...@ +@...@ +@...@ +@...@ +.@@@. +P +@@@@. +@...@ +@...@ +@@@@. +@.... +@.... +@.... +Q +.@@@. +@...@ +@...@ +@...@ +@.@.@ +@..@. +@@@.@ +R +@@@@. +@...@ +@...@ +@@@@. +@.@.. +@..@. +@...@ +S +.@@@. +@...@ +@.... +.@@@. +....@ +@...@ +.@@@. +T +@@@@@ +..@.. +..@.. +..@.. +..@.. +..@.. +..@.. +U +@...@ +@...@ +@...@ +@...@ +@...@ +@...@ +.@@@. +V +@...@ +@...@ +@...@ +@...@ +@...@ +.@.@. +..@.. +W +@...@ +@...@ +@...@ +@.@.@ +@.@.@ +@@.@@ +@...@ +X +@...@ +@...@ +.@.@. +..@.. +.@.@. +@...@ +@...@ +Y +@...@ +@...@ +@...@ +.@.@. +..@.. +..@.. +..@.. +Z +@@@@@ +....@ +...@. +..@.. +.@... +@.... +@@@@@ diff --git a/fonts/fontgen.c b/fonts/fontgen.c new file mode 100644 index 0000000..a604c5d --- /dev/null +++ b/fonts/fontgen.c @@ -0,0 +1,462 @@ +/** @file fontgen.c + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief +*/ +#include +#include +#include +#include +#include +#include +#include + +/* FIXME. This is not robust. */ + + +typedef struct +{ + char name[64]; + char *comment; + uint8_t width; + uint8_t height; + uint8_t offset; + uint8_t size; + uint8_t bytes; + uint8_t interlaced; + uint8_t *data; +} font_t; + + +#define FONT_SIZE_MAX 255 + +int font_zoom (font_t *font, font_t *zoom_font, int zoom_x, int zoom_y) +{ + int i; + uint8_t *image; + uint8_t *zoom_image; + int bits; + + *zoom_font = *font; + zoom_font->width = font->width * zoom_x; + zoom_font->height = font->height * zoom_y; + zoom_font->bytes = font->bytes * zoom_x * zoom_y; + zoom_font->data = calloc (FONT_SIZE_MAX, zoom_font->bytes); + + bits = zoom_font->width * zoom_font->height; + if (zoom_font->interlaced) + bits = (bits + 1) / 2; + zoom_font->bytes = (bits + CHAR_BIT - 1) / CHAR_BIT; + + image = malloc (font->width * font->height); + zoom_image = malloc (font->width * zoom_x * font->height * zoom_y); + + for (i = font->offset; i < font->size + font->offset; i++) + { + int k; + int x; + int y; + uint8_t font_byte; + uint8_t *font_elt; + + font_elt = &font->data[i * font->bytes]; + + k = 0; + font_byte = *font_elt++; + + for (y = 0; y < font->height; y++) + { + for (x = 0; x < font->width; x++) + { + image[y * font->width + x] = (font_byte & 1) * 255; + font_byte >>= 1; + k++; + if (k >= CHAR_BIT) + { + k = 0; + font_byte = *font_elt++; + } + } + } + + for (y = 0; y < zoom_font->height; y++) + { + for (x = 0; x < zoom_font->width; x++) + zoom_image[y * zoom_font->width + x] + = image[(y / zoom_y) * font->width + (x / zoom_x)]; + } + + + font_elt = &zoom_font->data[i * zoom_font->bytes]; + k = 0; + *font_elt = 0; + for (y = 0; y < zoom_font->height; y++) + { + for (x = 0; x < zoom_font->width; x++) + { + if (zoom_image[y * zoom_font->width + x]) + *font_elt |= (1 << k); + k++; + if (k >= CHAR_BIT) + { + k = 0; + font_elt++; + *font_elt = 0; + } + } + } + } + + free (image); + free (zoom_image); + return 1; +} + + +int font_scan (font_t *font) +{ + char buffer[132]; + int line; + int symcnt; + int symline; + uint8_t symbol; + uint8_t symbol_min; + uint8_t symbol_max; + int tmp; + int bits; + unsigned int i; + + font->comment = 0; + + /* FIXME, use more robust key-value pair parsing. */ + while (1) + { + int c; + + c = fgetc (stdin); + if (c == EOF) + return 0; + + ungetc (c, stdin); + + if (!strchr ("#whi", c)) + break; + + if (!fgets (buffer, sizeof (buffer), stdin)) + return 0; + + if (c == 'w') + { + if (sscanf (buffer, "width=%d", &tmp) != 1) + fprintf (stderr, "Width parse error: %s\n", buffer); + font->width = tmp; + } + else if (c == 'h') + { + if (sscanf (buffer, "height=%d", &tmp) != 1) + fprintf (stderr, "Height parse error: %s\n", buffer); + font->height = tmp; + } + else if (c == 'i') + { + if (sscanf (buffer, "interlaced=%d", &tmp) != 1) + fprintf (stderr, "Interlaced parse error: %s\n", buffer); + font->interlaced = tmp; + } + else if (c == '#') + { + if (!font->comment) + font->comment = calloc (1, strlen (buffer) + 1); + else + font->comment = realloc (font->comment, + strlen (font->comment) + + strlen (buffer) + 1); + + strcat (font->comment, buffer + 1); + } + + } + + if (!font->width) + { + fprintf (stderr, "Missing width specifier\n"); + return 1; + } + + if (!font->height) + { + fprintf (stderr, "Missing height specifier\n"); + return 1; + } + + bits = font->width * font->height; + if (font->interlaced) + bits = (bits + 1) / 2; + font->bytes = (bits + CHAR_BIT - 1) / CHAR_BIT; + + font->offset = 0; + font->size = ~0; + + symbol_min = ~0; + symbol_max = 0; + + font->data = calloc (FONT_SIZE_MAX, font->bytes); + + symbol = 0; + + line = 4; + for (symcnt = 1; ; symcnt++) + { + int bit = 0; + + for (symline = 1; symline <= font->height + 1; symline++) + { + unsigned int num; + + if (!fgets (buffer, sizeof (buffer), stdin)) + { + /* Have reached end of file. */ + font->offset = symbol_min; + font->size = symbol_max - symbol_min + 1; + + return 0; + } + + /* Remove newline. */ + buffer[strlen (buffer) - 1] = '\0'; + + if (symline != 1) + { + char *p; + + if (strlen (buffer) != font->width) + { + fprintf (stderr, "Parse error at line %d: %s\n", line, buffer); + return 1; + } + + p = buffer; + if (font->interlaced) + { + num = symline & 1 ? font->width / 2 : (font->width + 1) / 2; + if (symline & 1) + p++; + } + else + num = font->width; + + for (i = 0; i < num; i++) + { + if (*p != '.') + { + font->data[symbol * font->bytes + bit / CHAR_BIT] + |= (1 << (bit % CHAR_BIT)); + } + else if (*p != '.') + { + fprintf (stderr, "Parse error at line %d: %c\n", line, *p); + return 1; + } + bit++; + if (font->interlaced) + p += 2; + else + p++; + } + } + else + { + if (buffer[0] == '\\' && buffer[1] != '\0') + { + if (sscanf (buffer + 1, "%o", &tmp) != 1) + { + fprintf (stderr, "Parse error (octal num) at line %d: %s\n", line, buffer); + return 2; + } + symbol = tmp; + } + else + { + char sym; + + if (sscanf (buffer, "%c", &sym) != 1) + { + fprintf (stderr, "Parse error (char) at line %d: %s\n", line, buffer); + return 2; + } + symbol = sym; + } + if (symbol < symbol_min) + symbol_min = symbol; + if (symbol > symbol_max) + symbol_max = symbol; + } + + line++; + } + } +} + + +static void font_print_1 (font_t *font, uint8_t symbol) +{ + int i; + + for (i = 0; i < font->bytes; i++) + printf ("0x%02x, ", font->data[symbol * font->bytes + i]); + fputs ("\n", stdout); +} + + +static void font_print (font_t *font) +{ + int i; + uint8_t bytes_per_char; + time_t now; + char name_upper[128]; + char timestr[32]; + + bytes_per_char = (font->width * font->height + 8 - 1) >> 3; + + for (i= 0; font->name[i]; i++) + name_upper[i] = toupper (font->name[i]); + name_upper[i] = '\0'; + + time (&now); + strftime (timestr, sizeof (timestr), "%d %b %Y", localtime (&now)); + + printf ("/** @file %s.h\n", font->name); + printf (" @author fontgen\n"); + printf (" @date %s\n\n", timestr); + printf (" @defgroup %s %s\n", font->name, font->comment); + printf ("*/\n\n"); + printf ("#ifndef %s_H_\n", name_upper); + printf ("#define %s_H_\n\n", name_upper); + printf ("#define %s_WIDTH %d\n", name_upper, font->width); + printf ("#define %s_HEIGHT %d\n", name_upper, font->height); + printf ("#define %s_OFFSET %d\n", name_upper, font->offset); + printf ("#define %s_SIZE %d\n\n", name_upper, font->size); + + printf ("#ifndef FONT_WIDTH\n#define FONT_WIDTH %d\n#endif\n", + font->width); + printf ("#ifndef FONT_HEIGHT\n#define FONT_HEIGHT %d\n#endif\n", + font->height); + printf ("#ifndef FONT_OFFSET\n#define FONT_OFFSET %d\n#endif\n", + font->offset); + printf ("#ifndef FONT_SIZE_MAX\n#define FONT_SIZE_MAX %d\n#endif\n", + font->size); + + printf ("#include \"font.h\"\n\n"); + printf ("static font_t %s =\n{\n", font->name); + + printf (" .flags = 1, /* (packed) */\n"); + printf (" .width = %d,\n", font->width); + printf (" .height = %d,\n", font->height); + printf (" .offset = %d,\n", font->offset); + printf (" .size = %d,\n", font->size); + printf (" .bytes = %d,\n", bytes_per_char); + printf (" .data =\n {\n"); + + for (i = font->offset; i < font->size + font->offset; i++) + { + if (i < 32) + printf (" /* \\%o */\n ", i); + else + printf (" /* %c */\n ", i); + + font_print_1 (font, i); + } + printf (" }\n};\n#endif /* %s_H_ */\n", name_upper); +} + + +static void font_draw_1 (font_t *font, uint8_t symbol) +{ + int i; + int j; + int bit; + char ch; + uint8_t *data; + + data = &font->data[symbol * font->bytes]; + bit = 0; + ch = *data++; + for (i = 0; i < font->height; i++) + { + for (j = 0; j < font->width; j++) + { + if (font->interlaced && ((i & 1) ^ (j & 1))) + { + fputc (' ', stdout); + continue; + } + fputc (ch & 1 ? '@' : '.', stdout); + ch >>= 1; + bit++; + if ((bit % CHAR_BIT) == 0) + ch = *data++; + } + fputc ('\n', stdout); + } +} + + +static void font_draw (font_t *font) +{ + int i; + + printf ("width=%d\n", font->width); + printf ("height=%d\n", font->height); + printf ("interlaced=%d\n", font->interlaced); + + for (i = 0; i < 32; i++) + { + printf ("\\%o\n", i); + font_draw_1 (font, i); + } + + for (; i < FONT_SIZE_MAX; i++) + { + printf ("%c\n", i); + font_draw_1 (font, i); + } +} + + +int main (int argc, char **argv) +{ + int ret; + font_t font; + + ret = font_scan (&font); + if (ret) + return ret; + + if (argc < 2) + { + fprintf (stderr, "Usage: fontgen fontname\n"); + return 1; + } + strcpy (font.name, argv[1]); + argc--; + argv++; + + if (argc > 2 && !strcmp (argv[1], "-zoom")) + { + int zoom; + font_t zoom_font; + + zoom = atoi (argv[2]); + argc -= 2; + argv += 2; + + font_zoom (&font, &zoom_font, zoom, zoom); + font = zoom_font; + } + + if (argc > 1 && !strcmp (argv[1], "-ascii")) + font_draw (&font); + else + font_print (&font); + return 0; +} diff --git a/utils/README b/utils/README new file mode 100644 index 0000000..a01e8c4 --- /dev/null +++ b/utils/README @@ -0,0 +1,4 @@ +This is a collection of common modules, such as utility functions, +etc. Note, that these modules do not directly access the hardware but +instead use the device driver modules. + diff --git a/utils/boing.c b/utils/boing.c new file mode 100644 index 0000000..1ecd1fe --- /dev/null +++ b/utils/boing.c @@ -0,0 +1,72 @@ +/** @file boing.c + @author M. P. Hayes, UCECE + @date 24 August 2010 + @brief Bouncing ball module +*/ +#include "system.h" +#include "boing.h" + +/** Update the state of a ball bouncing off the edges of the display + @param state current state + @return new state. */ +boing_state_t boing_update (boing_state_t state) +{ + tinygl_point_t hops[] = {{0, 1}, {1, 1}, {1, 0}, {1, -1}, + {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}}; + + state.pos.x += hops[state.dir].x; + state.pos.y += hops[state.dir].y; + + if (state.pos.x > TINYGL_WIDTH - 1 || state.pos.x < 0) + { + boing_dir_t newdir[] = {DIR_N, DIR_NW, DIR_W, DIR_SW, + DIR_S, DIR_SE, DIR_E, DIR_NE}; + state.pos.x -= 2 * hops[state.dir].x; + state.dir = newdir[state.dir]; + } + + if (state.pos.y > TINYGL_HEIGHT - 1 || state.pos.y < 0) + { + boing_dir_t newdir[] = {DIR_S, DIR_SE, DIR_E, DIR_NE, + DIR_N, DIR_NW, DIR_W, DIR_SW}; + state.pos.y -= 2 * hops[state.dir].y; + state.dir = newdir[state.dir]; + } + + return state; +} + + +/** Reverse the direction of a ball + @param state current state + @return new state with direction reversed. */ +boing_state_t boing_reverse (boing_state_t state) +{ + boing_dir_t newdir[] = {DIR_S, DIR_SW, DIR_W, DIR_NW, + DIR_N, DIR_NE, DIR_W, DIR_SE}; + + state.dir = newdir[state.dir]; + return state; +} + + +/** Initialise the state of a ball + @param xstart x coordinate to start at + @param ystart y coordinate to start at + @param dir initial direction + @return state. */ +boing_state_t boing_init (uint8_t xstart, uint8_t ystart, boing_dir_t dir) +{ + boing_state_t state; + + if (xstart > TINYGL_WIDTH) + xstart = TINYGL_WIDTH - 1; + if (ystart > TINYGL_HEIGHT) + ystart = TINYGL_HEIGHT - 1; + + state.pos.x = xstart; + state.pos.y = ystart; + state.dir = dir; + + return state; +} diff --git a/utils/boing.h b/utils/boing.h new file mode 100644 index 0000000..a2e586a --- /dev/null +++ b/utils/boing.h @@ -0,0 +1,42 @@ +/** @file boing.h + @author M. P. Hayes, UCECE + @date 24 August 2010 + @brief Bouncing ball module + + @defgroup boing Bouncing ball module +*/ +#ifndef BOING_H +#define BOING_H + +#include "tinygl.h" + +/** Compass direction of ball. */ +typedef enum dir {DIR_N, DIR_NE, DIR_E, DIR_SE, + DIR_S, DIR_SW, DIR_W, DIR_NW} boing_dir_t; + + +/** Structure defining state of ball. */ +typedef struct state {tinygl_point_t pos; boing_dir_t dir;} boing_state_t; + + +/** Update the state of a ball bouncing off the edges of the display + @param state current state + @return new state. */ +boing_state_t boing_update (boing_state_t state); + + +/** Reverse the direction of a ball + @param state current state + @return new state with direction reversed. */ +boing_state_t boing_reverse (boing_state_t state); + + +/** Initialise the state of a ball + @param xstart x coordinate to start at + @param ystart y coordinate to start at + @param dir initial direction + @return state. */ +boing_state_t boing_init (uint8_t xstart, uint8_t ystart, boing_dir_t dir); + + +#endif diff --git a/utils/font.h b/utils/font.h new file mode 100644 index 0000000..4e2714e --- /dev/null +++ b/utils/font.h @@ -0,0 +1,41 @@ +/** @file font.h + @author M. P. Hayes, UCECE + @date 1 March 2007 + @brief Font definition. +*/ + +#ifndef FONT_H +#define FONT_H + +#include "system.h" + +typedef const uint8_t font_data_t; + +/** Font structure. */ +typedef const struct +{ + /** Flags for future options. */ + uint8_t flags; + /** Width of font element. */ + uint8_t width; + /** Height of font element. */ + uint8_t height; + /** Index of first entry in font. */ + uint8_t offset; + /** Number of font entries in table. */ + uint8_t size; + /** Number of bytes per font entry. */ + uint8_t bytes; + /** Font element data. */ + font_data_t data[]; +} font_t; + + +/** Return the first character in the font. */ +#define FONT_FIRST(font) ((font)->offset) + +/** Return the last character in the font. */ +#define FONT_LAST(font) ((font)->offset + (font)->size - 1) + + +#endif diff --git a/utils/pacer.c b/utils/pacer.c new file mode 100644 index 0000000..c0ca5bc --- /dev/null +++ b/utils/pacer.c @@ -0,0 +1,30 @@ +/** @file pacer.c + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief Paced loop module +*/ +#include "system.h" +#include "timer.h" +#include "pacer.h" + +static timer_tick_t pacer_period; + + +/** Initialise pacer: + @param pacer_rate rate in Hz. */ +void pacer_init (pacer_rate_t pacer_rate) +{ + timer_init (); + + pacer_period = TIMER_RATE / pacer_rate; +} + + +/** Wait until next pacer tick. */ +void pacer_wait (void) +{ + static timer_tick_t when = 0; + + timer_wait_until (when); + when += pacer_period; +} diff --git a/utils/pacer.h b/utils/pacer.h new file mode 100644 index 0000000..42f9b58 --- /dev/null +++ b/utils/pacer.h @@ -0,0 +1,46 @@ +/** @file pacer.h + @author M. P. Hayes, UCECE + @date 23 August 2010 + @brief Paced loop support. + + @defgroup pacer Paced loop module + + This module provides support for paced loops by abstracting a + hardware timer. + + Here's a simple example for a paced loop that operates at 1 kHz. + + @code + #include "pacer.h" + + void main (void) + { + pacer_init (1000); + + while (1) + { + pacer_wait (); + + // Do something. + } + } + @endcode +*/ +#ifndef PACER_H +#define PACER_H + +#include "system.h" + +/** Define size of pacer rates. */ +typedef uint16_t pacer_rate_t; + + +/** Wait for the next pacer tick. */ +extern void pacer_wait (void); + + +/** Initialise pacer: + @param pacer_rate rate in Hz. */ +extern void pacer_init (pacer_rate_t pacer_rate); + +#endif /* PACER_H */ diff --git a/utils/task.c b/utils/task.c new file mode 100644 index 0000000..4e75354 --- /dev/null +++ b/utils/task.c @@ -0,0 +1,76 @@ +/** @file task.c + @author M. P. Hayes, UCECE + @date 17 August 2011 + @brief Simple task scheduler. +*/ +#include "system.h" +#include "task.h" +#include "timer.h" + + +/** With 16-bit times the maximum value is 32768. */ +#define TASK_OVERRUN_MAX 32767 + + +/** Schedule tasks + @param tasks pointer to array of tasks (the highest priority + task comes first) + @param num_tasks number of tasks to schedule + @return this never returns. +*/ +void task_schedule (task_t *tasks, uint8_t num_tasks) +{ + uint8_t i; + timer_tick_t now; + task_t *next_task; + + timer_init (); + now = timer_get (); + + /* Start by scheduling the first task. */ + next_task = tasks; + + while (1) + { + timer_tick_t sleep_min; + + /* Wait until the next task is ready to run. */ + timer_wait_until (next_task->reschedule); + + /* Schedule the task. */ + next_task->func (next_task->data); + + /* Update the reschedule time. */ + next_task->reschedule += next_task->period; + + sleep_min = ~0; + now = timer_get (); + + /* Search array of tasks. Schedule the first task (highest priority) + that needs to run otherwise wait until first task ready. */ + for (i = 0; i < num_tasks; i++) + { + task_t * task = tasks + i; + timer_tick_t overrun; + + overrun = now - task->reschedule; + if (overrun < TASK_OVERRUN_MAX) + { + /* Have found a task that can run immediately. */ + next_task = task; + break; + } + else + { + timer_tick_t sleep; + + sleep = -overrun; + if (sleep < sleep_min) + { + sleep_min = sleep; + next_task = task; + } + } + } + } +} diff --git a/utils/task.h b/utils/task.h new file mode 100644 index 0000000..187f866 --- /dev/null +++ b/utils/task.h @@ -0,0 +1,137 @@ +/** @file task.h + @author M. P. Hayes, UCECE + @date 17 August 2011 + @brief Simple task scheduler. + + @defgroup task Simple task scheduler + + This scheduler periodically calls functions specified in an array + of tasks to schedule. There is no pre-emption; a task can hog the CPU. + The tasks at the start of the array have a higher priority. + + Each task must maintain its own state; either using static local + variables, file variables, or dynamically using the generic pointer + passed when the task is called. + + Here's an example of two LED flashing tasks that run at 2 and 3 Hz. + + @code + #include "task.h" + #include "led.h" + #include "system.h" + + static void led1_task (void *data) + { + static uint8_t state = 0; + + led_set (LED1, state); + state = !state; + } + + + static void led2_task (void *data) + { + static uint8_t state = 0; + + led_set (LED2, state); + state = !state; + } + + + int main (void) + { + task_t tasks[] = + { + {.func = led1_task, .period = TASK_RATE / 2, .data = 0}, + {.func = led2_task, .period = TASK_RATE / 3, .data = 0}, + }; + + system_init (); + led_init (); + + task_schedule (tasks, 2); + return 0; + } +@endcode + + Here's another similar example. This time there are two instances + of the same task but with different state. + +@code + #include "task.h" + #include "led.h" + #include "system.h" + + typedef struct state_struct + { + uint8_t led; + uint8_t on; + } state_t; + + + static void led_task (void *data) + { + state_t *pstate = data; + + led_set (pstate->led, pstate->on); + pstate->on = !pstate->on; + } + + + int main (void) + { + state_t led1 = {.led = LED1, .on = 0}; + state_t led2 = {.led = LED2, .on = 0}; + task_t tasks[] = + { + {.func = led_task, .period = TASK_RATE / 2, .data = &led1}, + {.func = led_task, .period = TASK_RATE / 3, .data = &led2}, + }; + + system_init (); + led_init (); + + task_schedule (tasks, 2); + return 0; + } +@endcode +*/ + +#ifndef TASK_H +#define TASK_H + +#include "system.h" +#include "timer.h" + + +#define TASK_RATE TIMER_RATE + + +/** Task function prototype. */ +typedef void (* task_func_t)(void *data); + + +/** Task structure. */ +typedef struct task_struct +{ + /** Function to call. */ + task_func_t func; + /** Generic pointer to pass to function. */ + void *data; + /** How many ticks between scheduling events. */ + timer_tick_t period; + /** When to reschedule. */ + timer_tick_t reschedule; +} task_t; + + +/** Schedule tasks + @param tasks pointer to array of tasks (the highest priority + task comes first) + @param num_tasks number of tasks to schedule + @return this never returns. +*/ +void task_schedule (task_t *tasks, uint8_t num_tasks); + +#endif + diff --git a/utils/tinygl.c b/utils/tinygl.c new file mode 100644 index 0000000..1a59108 --- /dev/null +++ b/utils/tinygl.c @@ -0,0 +1,278 @@ +/** @file tinygl.c + @author M. P. Hayes, UCECE + @date 24 August 2010 + @brief Tiny graphics library. +*/ +#include +#include "system.h" +#include "tinygl.h" +#include "display.h" + +enum {TINYGL_SPEED_DEFAULT = 20}; + +static uint16_t update_rate; +static uint16_t text_advance_period; +static font_t *font; +static tinygl_text_mode_t text_mode = TINYGL_TEXT_MODE_STEP; +static uint8_t message_index; +static uint8_t scroll_pos = 0; +static char message1[32] = ""; + + +/** Draw point. + @param point coordinates of point + @param pixel_value pixel value for line. */ +void tinygl_draw_point (tinygl_point_t point, tinygl_pixel_value_t pixel_value) +{ + display_pixel_set (point.x, point.y, pixel_value); +} + + +/** Draw line. + @param point1 coordinates of start of line + @param point2 coordinates of end of line + @param pixel_value pixel value for line. */ +void tinygl_draw_line (tinygl_point_t point1, tinygl_point_t point2, + tinygl_pixel_value_t pixel_value) +{ + int dx; + int dy; + int x_inc; + int y_inc; + + /* Draw a line using Bresenham's algorithm. */ + + dx = point2.x - point1.x; + dy = point2.y - point1.y; + + x_inc = dx >= 0 ? 1 : -1; + y_inc = dy >= 0 ? 1 : -1; + if (dx < 0) + dx = -dx; + if (dy < 0) + dy = -dy; + + if (dx >= dy) + { + int error; + + error = dx / 2; + + for (; point1.x != point2.x; point1.x += x_inc) + { + tinygl_draw_point (point1, pixel_value); + + error -= dy; + if (error < 0) + { + error += dx; + point1.y += y_inc; + } + } + } + else + { + int error; + + error = dy / 2; + for (; point1.y != point2.y; point1.y += y_inc) + { + tinygl_draw_point (point1, pixel_value); + + error -= dx; + if (error < 0) + { + error += dy; + point1.x += x_inc; + } + } + } + tinygl_draw_point (point1, pixel_value); +} + + +/** Draw box. + @param tl coordinates of top left corner of box + @param br coordinates of bottom right of box + @param pixel_value pixel value for box. */ +void tinygl_draw_box (tinygl_point_t tl, tinygl_point_t br, + tinygl_pixel_value_t pixel_value) +{ + tinygl_draw_line (tl, tinygl_point (br.x, tl.y), pixel_value); + tinygl_draw_line (tl, tinygl_point (tl.x, br.y), pixel_value); + tinygl_draw_line (tinygl_point (br.x, tl.y), br, pixel_value); + tinygl_draw_line (tinygl_point (tl.x, br.y), br, pixel_value); +} + + +/** Clear display. */ +void tinygl_clear (void) +{ + display_clear (); + + /* Stop message display. */ + message1[0] = 0; + message_index = 0; +} + + +/** Determine if pixel on or off. + @param ch character to display + @param col column of font element + @param row row of font element + @return 1 if pixel on; if pixel out of bounds return 0. */ +static bool tinygl_font_pixel_get (char ch, uint8_t col, uint8_t row) +{ + int8_t index; + font_data_t *char_data; + uint8_t offset; + + if (!font || col >= font->width || row >= font->height) + return 0; + + /* Find index of font entry. */ + index = ch - font->offset; + if (index < 0 || index >= font->size) + return 0; + + /* Get start of font entry data for ch. */ + char_data = &font->data[index * font->bytes]; + + /* Extract whether pixel should be on or off. */ + offset = row * font->width + col; + return (char_data[offset / 8] & BIT (offset % 8)) != 0; +} + + +/** Display a character. + @param ch character to display + @return 1 if character fully displayed. */ +static bool tinygl_display_char (char ch) +{ + uint8_t x; + uint8_t y; + + if (!font) + return 0; + + switch (text_mode) + { + case TINYGL_TEXT_MODE_SCROLL_LEFT: + display_scroll_left (); + + for (y = 0; y < font->height; y++) + { + tinygl_draw_point (tinygl_point (TINYGL_WIDTH - 1, y), + tinygl_font_pixel_get (ch, scroll_pos, y)); + } + break; + + case TINYGL_TEXT_MODE_STEP: + if (scroll_pos != 0) + break; + + for (x = 0; x < font->width; x++) + { + for (y = 0; y < font->height; y++) + { + tinygl_draw_point (tinygl_point (x, y), + tinygl_font_pixel_get (ch, x, y)); + } + } + break; + + case TINYGL_TEXT_MODE_ROTATE_SCROLL_DOWN: + display_scroll_down (); + + for (x = 0; x < font->height; x++) + { + tinygl_draw_point (tinygl_point (x, 0), + tinygl_font_pixel_get (ch, scroll_pos, x)); + } + break; + } + + scroll_pos++; + if (scroll_pos > font->width) + scroll_pos = 0; + return scroll_pos == 0; +} + + +/** Advance message. */ +static void tinygl_text_advance (void) +{ + if (!message1[message_index]) + message_index = 0; + + if (message1[message_index]) + { + if (tinygl_display_char (message1[message_index])) + message_index++; + } +} + + +/** Display a message repeatedly. + @param string null terminated message to display. */ +void tinygl_text (const char *string) +{ + message_index = 0; + scroll_pos = 0; + strncpy (message1, string, sizeof (message1)); +} + + +/** Set the message update speed. + @param speed text advance speed (characters per 10 s). */ +void tinygl_text_speed_set (uint8_t speed) +{ + text_advance_period = update_rate / (speed * TINYGL_WIDTH) * 10; +} + + +/** Set the message display mode. + @param mode display mode. */ +void tinygl_text_mode_set (tinygl_text_mode_t mode) +{ + text_mode = mode; +} + + +/** Set the font to use for text. + @param pfont pointer to font description. */ +void tinygl_font_set (font_t *pfont) +{ + font = pfont; +} + + +/** Update display (refresh display and update message). */ +void tinygl_update (void) +{ + static uint16_t tick = 0; + + tick++; + if (tick >= text_advance_period) + { + tick = 0; + + tinygl_text_advance (); + } + + display_update (); +} + + +/** Initialise things. + @param rate rate in Hz that tinygl_update called. */ +void tinygl_init (uint16_t rate) +{ + update_rate = rate; + + display_init (); + + tinygl_text_speed_set (TINYGL_SPEED_DEFAULT); + + tinygl_clear (); +} diff --git a/utils/tinygl.h b/utils/tinygl.h new file mode 100644 index 0000000..e6cd64a --- /dev/null +++ b/utils/tinygl.h @@ -0,0 +1,182 @@ +/** @file tinygl.h + @author M. P. Hayes, UCECE + @date 24 August 2010 + @brief Tiny graphics library. + + @defgroup tinygl Tiny graphics library + +

Introduction to Tinygl

+ + Tinygl is a tiny graphics library for a simple dot matrix + display. This file declares the data types and graphics + primitives provided by tinygl. The basic graphics primitive is + a point. This specifies a pair of (x, y) coordinates on the + display. Each point has an associated pixel value. Currently, + this can be 0 for off and 1 for on. + + +

A simple Tinygl application

+ + Here's a simple application that draws a line from (1, 2) to + (3, 5). The paced loop runs at 1 kHz and this sets the display + refresh period to 200 Hz since there are 5 columns. + + @code + #include "pacer.h" + #include "tinygl.h" + + void main (void) + { + pacer_init (1000); + tinygl_init (1000); + + tinygl_draw_line (tinygl_point (1, 2), tinygl_point (3, 5)); + + while (1) + { + pacer_wait (); + tinygl_update (); + } + } + @endcode + +

A Tinygl text application

+ + Here's another simple application that displays a message. + + @code + #include "pacer.h" + #include "tinygl.h" + #include "../fonts/font5x7_1.h" + + void main (void) + { + pacer_init (1000); + tinygl_init (1000); + + tinygl_font_set (&font5x7_1); + tinygl_text_speed_set (10); + tinygl_text_mode_set (TINYGL_TEXT_MODE_SCROLL_LEFT); + tinygl_text ("HELLO WORLD "); + + while (1) + { + pacer_wait (); + tinygl_update (); + } + } + @endcode + +*/ + +#ifndef TINYGL_H +#define TINYGL_H + +#include "system.h" +#include "display.h" +#include "font.h" + +/** Define size of display. */ +#define TINYGL_WIDTH DISPLAY_WIDTH +#define TINYGL_HEIGHT DISPLAY_HEIGHT + + +/** Define a pixel value. Currently the only supported values are 0 + for off and 1 for on. */ +typedef uint8_t tinygl_pixel_value_t; + + +/** Define a display coordinate. Note these are signed to allow + relative coordinates. */ +typedef int8_t tinygl_coord_t; + + +/** Define a point as a pair of coordinates. The display origin is the + top left corner. */ +typedef struct tinygl_point +{ + tinygl_coord_t x; + tinygl_coord_t y; +} tinygl_point_t; + + +/** Define text display modes. */ +typedef enum +{ + /* Stepping text. */ + TINYGL_TEXT_MODE_STEP, + /** Scrolling text. */ + TINYGL_TEXT_MODE_SCROLL_LEFT, + /** Rotated scrolling text. */ + TINYGL_TEXT_MODE_ROTATE_SCROLL_DOWN +} tinygl_text_mode_t; + + +/** Construct a point from a pair of coordinates. + @param x x coordinate + @param y y coordinate + @return point. */ +static inline tinygl_point_t tinygl_point (tinygl_coord_t x, tinygl_coord_t y) +{ + tinygl_point_t point = {x, y}; + + return point; +} + + +/** Display a message repeatedly. + @param string null terminated message to display. */ +void tinygl_text (const char *string); + + +/** Set the message update speed. + @param speed text advance speed (characters per 10 s). */ +void tinygl_text_speed_set (uint8_t speed); + + +/** Set the message display mode. + @param mode display mode. */ +void tinygl_text_mode_set (tinygl_text_mode_t mode); + + +/** Set the font to use for text. + @param pfont pointer to font description. */ +void tinygl_font_set (font_t *pfont); + + +/** Draw point. + @param point coordinates of point + @param pixel_value pixel value to draw point. */ +void tinygl_draw_point (tinygl_point_t point, tinygl_pixel_value_t pixel_value); + + +/** Draw line. + @param point1 coordinates of start of line + @param point2 coordinates of end of line + @param pixel_value pixel value to draw line. */ +void tinygl_draw_line (tinygl_point_t point1, tinygl_point_t point2, + tinygl_pixel_value_t pixel_value); + + +/** Draw box. + @param tl coordinates of top left corner of box + @param br coordinates of bottom right of box + @param pixel_value pixel value to draw box. */ +void tinygl_draw_box (tinygl_point_t tl, tinygl_point_t br, + tinygl_pixel_value_t pixel_value); + + +/** Clear display. */ +void tinygl_clear (void); + + +/** Update display and advance message. */ +void tinygl_update (void); + + +/** Initialise things. + @param rate rate in Hz that tinygl_update called. */ +void tinygl_init (uint16_t rate); + + +#endif /* TINYGL_H */ diff --git a/utils/uint8toa.c b/utils/uint8toa.c new file mode 100644 index 0000000..2faaca2 --- /dev/null +++ b/utils/uint8toa.c @@ -0,0 +1,34 @@ +/** @file uint8toa.c + @author M. P. Hayes, UCECE + @date 15 May 2007 + @brief +*/ +#include "system.h" + + +/** Convert 8 bit unsigned number to decimal ASCII string. This uses + much less memory than sprintf. + @param num number to convert + @param str pointer to array of at least 4 chars to hold string + @param leading_zeroes non-zero to pad with leading zeroes. */ +void +uint8toa (uint8_t num, char *str, bool leading_zeroes) +{ + uint8_t d; + uint8_t i; + uint8_t const powers[] = {100, 10, 1, 0}; + + for (i = 0; (d = powers[i]); i++) + { + uint8_t q; + + q = num / d; + if (leading_zeroes || q || d == 1) + { + *str++ = '0' + q; + num -= q * d; + leading_zeroes = 1; + } + } + *str = '\0'; +} diff --git a/utils/uint8toa.h b/utils/uint8toa.h new file mode 100644 index 0000000..67af57d --- /dev/null +++ b/utils/uint8toa.h @@ -0,0 +1,22 @@ +/** @file uint8toa.h + @author M. P. Hayes, UCECE + @date 21 Nov 2006 + @brief 8 bit unsigned int to ASCII conversion. + + @defgroup uint8toa 8 bit unsigned int to ASCII conversion +*/ + +#ifndef UINT8TOA_H +#define UINT8TOA_H + +#include "system.h" + + +/** Convert 8 bit unsigned number to decimal ASCII string. This uses + much less memory than sprintf. + @param num number to convert + @param str pointer to array of at least 4 chars to hold string + @param leading_zeroes non-zero to pad with leading zeroes. */ +void uint8toa (uint8_t num, char *str, bool leading_zeroes); + +#endif