diff --git a/recipes/arduinouno/README.md b/recipes/arduinouno/README.md new file mode 100644 index 0000000..7181168 --- /dev/null +++ b/recipes/arduinouno/README.md @@ -0,0 +1,7 @@ +# Arduino Uno + +The Arduino Uno is a very popular platform based on the ATMega328p. While +Collapse OS doesn't run on AVR MCUs (yet?), the Arduino can be a handy tool, +which is why we have recipes for it here. + +* [Writing to a AT28 EEPROM from a modern environment](at28/README.md) diff --git a/recipes/arduinouno/at28/Makefile b/recipes/arduinouno/at28/Makefile new file mode 100644 index 0000000..7ea1322 --- /dev/null +++ b/recipes/arduinouno/at28/Makefile @@ -0,0 +1,23 @@ +PROGNAME = at28wr +USBDEV = /dev/cuaU0 +BAUDS = 115200 +AVRDUDEARGS = -F -V -c arduino -P $(USBDEV) -b $(BAUDS) +AVRDUDEMCU = atmega328p +TARGET = $(PROGNAME).hex +AVRA = avra + +# Rules + +.PHONY: send all clean + +all: $(TARGET) + @echo Done! + +send: $(PROGNAME).hex + avrdude $(AVRDUDEARGS) -p $(AVRDUDEMCU) -U flash:w:$< + +$(TARGET): at28wr.asm + $(AVRA) -o $@ at28wr.asm + +clean: + rm -f $(TARGET) *.S.eep.hex *.S.obj diff --git a/recipes/arduinouno/at28/README.md b/recipes/arduinouno/at28/README.md new file mode 100644 index 0000000..25090ef --- /dev/null +++ b/recipes/arduinouno/at28/README.md @@ -0,0 +1,40 @@ +# Writing to a AT28 EEPROM from a modern environment + +In this recipe, we'll build ourselves an ad-hoc EEPROM holder which is designed +to be driven from an Arduino Uno. + +## Gathering parts + +* An Arduino Uno +* A AT28C64B +* 2 '164 shift registers +* Sockets, header pins, proto board, etc. +* [avra][avra] (will soon rewrite to Collapse OS' ASM) +* avrdude to send program to Arduino + +## Schema + +(there will soon be an image here, but I have yet to scan my schema) + +This is a rather simple circuit which uses 2 chained '164 shift register to +drive the AT28 address pins and connects CE, WE, OE and the data pins directly +to the Arduino. Pins have been chosen so that the protoboard can plug directly +on the Arduino's right side (except for VCC, which needs to be wired). + +PD0 and PD1 are not used because they're used for the UART. + +## Software + +The software in at28wr.asm listens to the UART and writes every byte it receives +to the AT28, starting at address 0. It expects tty-escaped content (see +`/tools/ttysafe`). + +After having written the byte, it re-reads it from the EEPROM and spits it back +to the UART, tty-escaped. + +## Usage + +After you've build and sent your binary to the Arduino with `make send`, you +can send your (tty-safe!) content to your EEPROM using `/tools/pingpong`. + +[avra]: http://avra.sourceforge.net/ diff --git a/recipes/arduinouno/at28/at28wr.asm b/recipes/arduinouno/at28/at28wr.asm new file mode 100644 index 0000000..1b657af --- /dev/null +++ b/recipes/arduinouno/at28/at28wr.asm @@ -0,0 +1,213 @@ +; *** EEPROM write *** +; Listen to UART expecting tty-escaped "pingpong" (from tools/) communication. +; +; Each of those received bytes is written to the EEPROM, starting at addr 0. +; that byte is then re-read and sent back to the UART, tty-escaped. +; +; Addr selection is done through 2 chained '164, data in/out is done directly +; with PD7:2 for bits 7:2 and PB1:0 for bits 1:0 (PD1 and PD0 are used for +; UART). +; +; *** Register Usage *** +; +; r0: holds whether last received char was tty-escaped (0 = no, 1=yes) +; r16: generic tmp +; r17: generic tmp +; r20: Byte to send to the "data" SR. Wired to D0-D7. +; r21: "high" byte, to send to the "addr" SR. Wired to A8-15 +; r22: "low" byte, to send to the "addr" SR. Wired to A0-7 +; r23: tmp value to use for sending to the "addr" SR + +.include "m328Pdef.inc" + +; *** Pins *** +.equ SRCP = PORTB2 +.equ SRDS = PORTB1 +.equ FLWE = PORTB3 +.equ FLOE = PORTB4 +.equ FLCE = PORTB5 ; WARNING: same as LED + +; *** Consts *** +.equ BAUD_PRESCALE = 103 ; 9600 bauds at 16mhz + +rjmp main + +; *** Code *** +; Waits until a char is read, then put it in R20 +; Perform TTY-escape transparently. +uartrd: + lds r16, UCSR0A + sbrs r16, RXC0 ; RXC0 is set? skip rjmp and fetch char. + rjmp uartrd + lds r20, UDR0 + ; is this the escape char? + cpi r20, 0x20 + brne uartrd_0 + ; escape char + ; We "pong" the escape right away. + rcall uartwr + inc r0 + rjmp uartrd +uartrd_0: + ; should we escape? + tst r0 + breq uartrd_1 + ; yes + andi r20, 0x7f +uartrd_1: + ret + +; Sends char in r20 to UART +; Perform TTY-escape transparently. +uartwr: + lds r16, UCSR0A + sbrs r16, UDRE0 ; wait until send buffer is empty + rjmp uartwr + ; should we escape? + tst r0 + breq uartwr_0 + ; we need to escape + ori r20, 0x80 + clr r0 +uartwr_0: + sts UDR0, r20 + ret + +; send r23 to addr shift register. +; We send highest bits first so that Q7 is the MSB and Q0 is the LSB +sendaddr: + ldi r16, 8 ; we will loop 8 times + cbi PORTB, SRDS + sbrc r23, 7 ; if latest bit isn't cleared, set SER_DP high + sbi PORTB, SRDS + ; toggle SRCP, not waiting between pulses. The CD74AC164 at 5V has a + ; 5.9ns CP min pulse width. We can't match that at 16mhz. No need to + ; wait. + cbi PORTB, SRCP + sbi PORTB, SRCP + lsl r23 ; shift our data left + dec r16 + brne sendaddr+1 ; not zero yet? loop! (+1 to avoid reset) + ret + +; send r20 to EEPROM's I/O7:0 through PD7:2 and PB1:0 +writedata: + ; send bits 7:2 + mov r16, r20 + andi r16, 0xfc + in r17, PORTD + andi r17, 0x03 + or r16, r17 + out PORTD, r16 + ; send bits 1:0 + mov r16, r20 + andi r16, 0x03 + in r17, PORTB + andi r17, 0xfc + or r16, r17 + out PORTB, r16 + ret + +; push r20 to the rom and increase the memory counter +pushdata: + ; first, set up addr + mov r23, r21 + rcall sendaddr + mov r23, r22 + rcall sendaddr + inc r22 + brne pushdata_0 ; no overflow? skip + inc r21 + +pushdata_0: + ; addr is latched on WE falling edge + cbi PORTB, FLWE + + ; now, lets set up data. Plenty enough instructions to ensure a 100ns + ; minimum delay. + rcall writedata + + ; data is latched on rising edge + sbi PORTB, FLWE + + ret + +; wait until I/O7 stops toggling +waitio7: + cbi PORTB, FLOE + in r16, PIND + sbi PORTB, FLOE + andi r16, 0xfc + cbi PORTB, FLOE + in r17, PIND + sbi PORTB, FLOE + andi r17, 0xfc + cp r16, r17 + brne waitio7 + ret + +; read EEPROM's I/O7:0 through PD7:2 and PB1:0 and put result in r20. +readdata: + cbi PORTB, FLOE + ; read bits 7:2 + in r20, PIND + andi r20, 0xfc + ; read bits 1:0 + in r16, PINB + andi r16, 0x03 + or r20, r16 + sbi PORTB, FLOE + ret + +; Set PD7:2 and PB1:0 to output +ioout: + ldi r16, 0xfc ; PD7:2 + out DDRD, r16 + ldi r16, 0x3f ; PB5:0 (WE, OE and CE too) + out DDRB, r16 + ret + +; Set PD7:2 and PB1:0 to input +ioin: + ldi r16, 0x03 ; PD7:2 + out DDRD, r16 + ldi r16, 0x3c ; PB1:0 + out DDRB, r16 + ret + +main: + ldi r16, low(RAMEND) + out SPL, r16 + ldi r16, high(RAMEND) + out SPH, r16 + + ; We begin with WE and OE disabled (high), but CE stays enabled (low) + ; the whole time. + sbi PORTB, FLWE + sbi PORTB, FLOE + cbi PORTB, FLCE + + ; Clear counters and flags + clr r0 + clr r21 + clr r22 + + ; Setup UART + ldi R16, low(BAUD_PRESCALE) + sts UBRR0L, r16 + ldi r16, high(BAUD_PRESCALE) + sts UBRR0H, r16 + + ldi r16, (1< 0) { + close(fd); + } return returncode; }