Add arduinouno/at28 recipe

This commit is contained in:
Virgil Dupras 2020-07-22 17:26:06 -04:00
parent e4a4a9800d
commit 3607eefa55
7 changed files with 304 additions and 15 deletions

View File

@ -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)

View File

@ -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

View File

@ -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/

View File

@ -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

View File

@ -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);
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)

View File

@ -1,6 +1,7 @@
void sendcmd(int fd, char *cmd);
void sendcmdp(int fd, char *cmd);
void mread(int fd, char *s, int count);
int mexpect(int fd, char ec);
void readprompt(int fd);
int set_interface_attribs(int fd, int speed, int parity);
void set_blocking(int fd, int should_block);

View File

@ -11,11 +11,17 @@
* to indicate EOF to the receiving end.
*
* 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)
{
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;
}
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]);
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;
// empty the recv buffer
while (read(fd, &c, 1) == 1) usleep(1000);
int returncode = 0;
while (fread(&c, 1, 1, fp)) {
putchar('.');
fflush(stdout);
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!
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;
break;
}
@ -50,6 +50,9 @@ int main(int argc, char **argv)
write(fd, &c, 1);
printf("Done!\n");
fclose(fp);
if (fd > 0) {
close(fd);
}
return returncode;
}