@@ -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) |
@@ -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 |
@@ -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/ |
@@ -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<<RXEN0) | (1<<TXEN0) | |||||
sts UCSR0B, r16 | |||||
loop: | |||||
rcall uartrd | |||||
rcall ioout | |||||
rcall pushdata | |||||
rcall ioin | |||||
rcall waitio7 | |||||
rcall readdata | |||||
rcall uartwr | |||||
rjmp loop | |||||
@@ -31,13 +31,15 @@ static void mempty(int fd) | |||||
} | } | ||||
} | } | ||||
static void mexpect(int fd, char ec) | |||||
int mexpect(int fd, unsigned char ec) | |||||
{ | { | ||||
char c; | |||||
unsigned char c; | |||||
mread(fd, &c, 1); | mread(fd, &c, 1); | ||||
if (c != ec) { | if (c != ec) { | ||||
fprintf(stderr, "Expected %d but got %d\n", ec, c); | |||||
fprintf(stderr, "Expected %x but got %x\n", ec, c); | |||||
return 0; | |||||
} | } | ||||
return 1; | |||||
} | } | ||||
void readprompt(int fd) | void readprompt(int fd) | ||||
@@ -1,6 +1,7 @@ | |||||
void sendcmd(int fd, char *cmd); | void sendcmd(int fd, char *cmd); | ||||
void sendcmdp(int fd, char *cmd); | void sendcmdp(int fd, char *cmd); | ||||
void mread(int fd, char *s, int count); | void mread(int fd, char *s, int count); | ||||
int mexpect(int fd, char ec); | |||||
void readprompt(int fd); | void readprompt(int fd); | ||||
int set_interface_attribs(int fd, int speed, int parity); | int set_interface_attribs(int fd, int speed, int parity); | ||||
void set_blocking(int fd, int should_block); | void set_blocking(int fd, int should_block); | ||||
@@ -11,11 +11,17 @@ | |||||
* to indicate EOF to the receiving end. | * to indicate EOF to the receiving end. | ||||
* | * | ||||
* It is recommended that you send contents that has gone through `ttysafe`. | * It is recommended that you send contents that has gone through `ttysafe`. | ||||
* | |||||
* If "delayus" is specified, this will be the delay we wait between the moment | |||||
* we write the "ping" and the moment where we fetch the "pong". | |||||
*/ | */ | ||||
int main(int argc, char **argv) | int main(int argc, char **argv) | ||||
{ | { | ||||
if (argc != 3) { | |||||
fprintf(stderr, "Usage: ./pingpong device fname\n"); | |||||
int delayus = 1000; | |||||
if (argc == 4) { | |||||
delayus = atoi(argv[3]); | |||||
} else if (argc != 3) { | |||||
fprintf(stderr, "Usage: ./pingpong device fname [delayus] \n"); | |||||
return 1; | return 1; | ||||
} | } | ||||
FILE *fp = fopen(argv[2], "r"); | FILE *fp = fopen(argv[2], "r"); | ||||
@@ -23,24 +29,18 @@ int main(int argc, char **argv) | |||||
fprintf(stderr, "Can't open %s.\n", argv[2]); | fprintf(stderr, "Can't open %s.\n", argv[2]); | ||||
return 1; | return 1; | ||||
} | } | ||||
int fd = open(argv[1], O_RDWR|O_NOCTTY|O_NONBLOCK); | |||||
printf("Press a key...\n"); | |||||
getchar(); | |||||
int fd = ttyopen(argv[1]); | |||||
unsigned char c; | unsigned char c; | ||||
// empty the recv buffer | |||||
while (read(fd, &c, 1) == 1) usleep(1000); | |||||
int returncode = 0; | int returncode = 0; | ||||
while (fread(&c, 1, 1, fp)) { | while (fread(&c, 1, 1, fp)) { | ||||
putchar('.'); | putchar('.'); | ||||
fflush(stdout); | fflush(stdout); | ||||
write(fd, &c, 1); | write(fd, &c, 1); | ||||
usleep(1000); // let it breathe | |||||
unsigned char c2; | |||||
while (read(fd, &c2, 1) != 1); // read echo | |||||
if (c != c2) { | |||||
usleep(delayus); // let it breathe | |||||
if (!mexpect(fd, c)) { | |||||
// mismatch! | // mismatch! | ||||
unsigned int pos = ftell(fp); | unsigned int pos = ftell(fp); | ||||
fprintf(stderr, "Mismatch at byte %d! %d != %d.\n", pos, c, c2); | |||||
fprintf(stderr, "Mismatch at byte %d!\n", pos); | |||||
returncode = 1; | returncode = 1; | ||||
break; | break; | ||||
} | } | ||||
@@ -50,6 +50,9 @@ int main(int argc, char **argv) | |||||
write(fd, &c, 1); | write(fd, &c, 1); | ||||
printf("Done!\n"); | printf("Done!\n"); | ||||
fclose(fp); | fclose(fp); | ||||
if (fd > 0) { | |||||
close(fd); | |||||
} | |||||
return returncode; | return returncode; | ||||
} | } | ||||