Add arduinouno/at28 recipe
This commit is contained in:
parent
e4a4a9800d
commit
3607eefa55
7
recipes/arduinouno/README.md
Normal file
7
recipes/arduinouno/README.md
Normal 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)
|
23
recipes/arduinouno/at28/Makefile
Normal file
23
recipes/arduinouno/at28/Makefile
Normal 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
|
40
recipes/arduinouno/at28/README.md
Normal file
40
recipes/arduinouno/at28/README.md
Normal 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/
|
213
recipes/arduinouno/at28/at28wr.asm
Normal file
213
recipes/arduinouno/at28/at28wr.asm
Normal 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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user