Compare commits

...

2 commits

Author SHA1 Message Date
Stephan I. Böttcher
46809f6fac coding for 36h straight 2026-01-03 00:30:48 +01:00
Stephan I. Böttcher
bf73d2b586 src: dose.c 2026-01-02 07:04:46 +01:00
32 changed files with 1425 additions and 1338 deletions

3
.gitignore vendored
View file

@ -22,3 +22,6 @@ src/mul
*.eeprom
*.elf
src/data
BCH-Codes/
*.odt
*.userrow

View file

@ -1,19 +1,18 @@
PROJ=bate
PROJ=dose
PATH:=/usr/local/bin:$(PATH)
default: all
all: $(PROJ).hex
SN_bate = 1
MCU_bate = attiny424
C_FILES_bate = uart.c rtc.c spi.c adc.c calib.c mul.c cmd.c
S_FILES_bate = uart_tx.S
SN_dose = 1
MCU_dose = attiny424
C_FILES_dose = config.c cmd.c pwm.c uart.c base85.c bch4369.c rtc.c spi.c flash.c adc.c
S_FILES_dose = uart_tx.S base85a.S
MCU = $(MCU_$(PROJ))
# When flash gets tight, use `OPT=-Os`, or use more assembler :-)
OPT = -O2
OPT = -Os
CC=avr-gcc -Wall -Wno-parentheses -MMD -std=c99 $(OPT) \
-mmcu=$(MCU) \
@ -22,8 +21,7 @@ CC=avr-gcc -Wall -Wno-parentheses -MMD -std=c99 $(OPT) \
-fpack-struct \
-fshort-enums \
-mtiny-stack \
-mint8 \
-fverbose-asm
-mint8
SN = $(SN_$(PROJ))
CFLAGS = $($*_CFLAGS) $(DEBUG) -I. -DSN="$(SN)"
@ -51,41 +49,35 @@ LDFLAGS = -Teeprom.ld
OBJCOPY = avr-objcopy
%.hex: %.elf
$(OBJCOPY) -O ihex -R .eeprom -R .eemap $< $@
$(OBJCOPY) -O ihex -R .eeprom -R .eemap -R .userrow -R .uumap $< $@
%.eeprom: %.elf
$(OBJCOPY) -O ihex -j .eemap --change-section-lma .eemap=0 $< $@
%.userrow: %.elf
$(OBJCOPY) -O ihex -j .uumap --change-section-lma .uumap=0 $< $@
pMCU-attiny424 = t424
#
#avrdude> dump fuses
#>>> dump fuses 0x0 0x9
#
#Reading | ################################################## | 100% 0.13 s
#
#0000 00 00 7e ff ff f6 ff 00 00 |..~...... |
#
#avrdude>
pMCU-attiny824 = t824
# WDT
fuse0_bate= 0x00
fuse0_dose= 0x00
# BOD
fuse1_bate= 0x00
fuse1_dose= 0x00
# OSC, 20 MHz
fuse2_bate= 0x7e
fuse2_dose= 0x7e
# ???
fuse4_bate= 0xff
fuse4_dose= 0xff
# SYS0 (default 0xf6) RESET, EEPROM erase
fuse5_bate= 0xf7
fuse5_dose= 0xf7
# SYS1 startup time (64ms)
fuse6_bate= 0xff
fuse6_dose= 0xff
# APPEND
fuse7_bate= 0x00
fuse7_dose= 0x00
# BOOTEND
fuse8_bate= 0x00
fuses_bate =$(patsubst %, 0x%, 00 00 7e ff ff f7 ff 00 00)
fuse8_dose= 0x00
fuses_dose =$(patsubst %, 0x%, 00 00 7e ff ff f7 ff 00 00)
AVRDUDEPROG = avrdude
AVRDUDE = $(AVRDUDEPROG)
@ -94,7 +86,7 @@ AVRDUDE_PORT = /dev/ttyUSB1
AD = $(AVRDUDE) -p $(pMCU-$(MCU)) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
sig_bate = 0x1e 0x92 0x2c
sig_dose = 0x1e 0x92 0x2c
id: $(PROJ).id
%.id:
@ -104,12 +96,12 @@ ad: $(PROJ).ad
%.ad:
$(AD) -v -t
%.burn: %.hex
%.burn: %.hex %.id
$(AD) -U flash:v:$< || $(AD) -U flash:w:$<
%.verify: %.hex %.eeprom
%.verify: %.hex %.eeprom %.userrow
-$(AD) -qq -U fuses:v:"$(fuses_$*)":m
-$(AD) -qq -U userrow:v:"$(BATE_CONFIG)":m
-$(AD) -qq -U userrow:v:$(word 3, $^)
-$(AD) -qq -U eeprom:v:$(word 2, $^)
-$(AD) -qq -U flash:v:$<
@ -119,41 +111,10 @@ fuse: $(PROJ).fuse$F
echo "$*: fuse$F = $(fuse$F_$*)"
[ -n "$(fuse$F_$*)" ] && $(AD) -B 5 -U fuse$F:w:$(fuse$F_$*):m
# see bate.c: Configuration in USERROW
BC_MAGIC = 0xba
BC_VERS = 9
BC_TRIG = 0x29
BC_SEND = 0x59
BC_PWR = 0xfc
BC_TEST = 0
BC_SPI = 4
BC_MDEL = 2
BC_PER = 9
BC_CPER = 30
BC_CLK = 1
BC_MCLK = 0
BC_BAUD = 0 0
BC_UART = 0xd0
BC_PIT = 0xff
BC_IMM = 5
BC_PAD = 0 0 0 0 0 0 0
BC_PRE = 0xff 0xff 0xff 0xff 0xff 0x55 0x0a 0
BATE_CONFIG = $(BC_MAGIC) $(BC_VERS) \
$(BC_TRIG) $(BC_SEND) $(BC_PWR) $(BC_TEST) \
$(BC_SPI) $(BC_MDEL) $(BC_PER) $(BC_CPER) \
$(BC_CLK) $(BC_MCLK) $(BC_BAUD) $(BC_UART) \
$(BC_PIT) $(BC_IMM) $(BC_PAD) $(BC_PRE)
bate.config:
$(AD) -U userrow:v:"$(BATE_CONFIG)":m \
|| $(AD) -U userrow:w:"$(BATE_CONFIG)":m
clean:
rm -f *.hex *.o *.s *.map *.elf *.d
bate.hex: bate.eeprom
# $(PROJ).hex: $(PROJ).eeprom $(PROJ).userrow
.PHONY: eeprom.eeprom
eeprom.eeprom:
@ -161,7 +122,3 @@ eeprom.eeprom:
%.eeprom.burn: %.eeprom
$(AD) -U eeprom:v:$< || $(AD) -U eeprom:w:$<
calib: calib.c mul.c
gcc -DCALIB_DEBUG -o $@ $<
mul: mul.c
gcc -DMUL_TEST -o $@ $<

200
src/adc.c
View file

@ -3,9 +3,6 @@
//
#include "adc.h"
#include "bate.h"
#include "mul.h"
#include "eeprom.h"
#include <avr/interrupt.h>
enum adc_conf_parameter {
@ -14,64 +11,52 @@ enum adc_conf_parameter {
REF = 10 << ADC_TIMEBASE_gp,
MODE = ADC_MODE_BURST_SCALING_gc | ADC_START_IMMEDIATE_gc,
MODE_DIFF = ADC_MODE_BURST_SCALING_gc | ADC_START_IMMEDIATE_gc | ADC_DIFF_bm,
V_RFP = ADC_MUXPOS_AIN4_gc,
V_NTC = ADC_MUXPOS_AIN6_gc,
V_BAT = ADC_MUXPOS_AIN7_gc,
VD1 = ADC_MUXPOS_AIN7_gc,
VD2 = ADC_MUXPOS_AIN10_gc,
VDG = ADC_MUXPOS_AIN6_gc,
};
struct adc_conf adc_conf[N_ADC] ADC_CONF_ADDR = {
{ // Internal Temperature
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | ADC_MUXPOS_TEMPSENSE_gc,
.calib = 80 * 256,
.shifts = 1,
},
__attribute__((section(".eeprom1")))
struct adc_conf adc_conf[N_ADC] = {
{ // V_CC / 10
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | ADC_MUXPOS_VDDDIV10_gc,
.calib = 1024 * 10,
.shifts = 3, // V [×10]
},
{ // Batterie Voltage / 11
{ // Internal Temperature
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | V_BAT,
.calib = 1024 * 11,
.shifts = 3, // V [×11]
.inp = INP | ADC_MUXPOS_TEMPSENSE_gc,
},
{ // NTC biased by V_RF
.mode = MODE_DIFF,
{ // VD1
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | V_NTC,
.inn = INP | V_RFP,
.calib = 2048 * 10,
.shifts = 1, // mV
.inp = INP | VD1,
},
{ // NTC biased by V_RF
{ // VD2
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | VD2,
},
{ // VD1
.mode = MODE,
.ref = REF | ADC_REFSEL_2500MV_gc,
.inp = INP | V_NTC,
.calib = 2500 * 10,
.shifts = 1,
.inp = INP | VD1,
},
{ // NTC biased by V_RF
.mode = MODE,
.ref = REF | ADC_REFSEL_VDD_gc,
.inp = INP | V_NTC,
},
{ // Transmitter power
{ // VD2
.mode = MODE,
.ref = REF | ADC_REFSEL_2500MV_gc,
.inp = INP | V_RFP,
.calib = 2500 * 10,
.shifts = 1,
.inp = INP | VD2,
},
{ // Transmitter power
{ // VDG
.mode = MODE,
.ref = REF | ADC_REFSEL_2500MV_gc,
.inp = INP | VDG,
},
{ // VDG
.mode = MODE,
.ref = REF | ADC_REFSEL_VDD_gc,
.inp = INP | V_RFP,
.inp = INP | VDG,
},
};
@ -87,13 +72,17 @@ void start_conversion(const struct adc_conf *c)
ADC.COMMAND = c->mode;
}
#ifdef NO_EE_CONFIG
void init_adc()
{
PORTA.PIN4CTRL |= PORT_ISC_INPUT_DISABLE_gc;
PORTB.PIN1CTRL |= PORT_ISC_INPUT_DISABLE_gc;
PORTA.PIN6CTRL |= PORT_ISC_INPUT_DISABLE_gc;
PORTA.PIN7CTRL |= PORT_ISC_INPUT_DISABLE_gc;
}
#endif
void start_adc()
{
if (!adc_conf->mode)
@ -112,20 +101,18 @@ void start_adc()
#if 0
ISR(ADC0_RESRDY_vect)
{
DEBUG_COUNTER(adc_irqs);
uint8_t i = adc_current;
if (i >= N_ADC)
goto stop;
adc_readings[i] = *(volatile uint16_t *) &ADC.RESULT;
if (++i < N_ADC) {
while (++i < N_ADC) {
const struct adc_conf *c = adc_conf + i;
if (c->mode || c->mode == 0xff)
goto stop;
adc_current = i;
if (c->mode) {
start_conversion(c);
adc_current = i;
return;
}
}
stop:
ADC.CTRLA = 0;
adc_current = N_ADC;
@ -149,7 +136,7 @@ ISR(ADC0_RESRDY_vect, ISR_NAKED)
"push r31" "\n\t"
"lds r24, adc_current" "\n\t"
"cpi r24, %[NADC]" "\n\t"
"brsh 3f" "\n\t"
"brcc 3f" "\n\t"
"mov r30, r24" "\n\t"
"ldi r31, 0" "\n\t"
"lsl r30" "\n\t"
@ -158,22 +145,18 @@ ISR(ADC0_RESRDY_vect, ISR_NAKED)
"lds r25, %[RESULT]" "\n\t"
"st Z, r25" "\n\t"
"lds r25, %[RESULT]+1" "\n\t"
"std Z+1, r25" "\n\t"
"cpi r24, %[NADC]-1" "\n\t"
"brsh 3f" "\n\t"
"subi r24, 0xff" "\n\t"
"std Z+1, r25" "\n"
"1:" "\n\t"
"subi r24, -1" "\n\t"
"cpi r24, %[NADC]" "\n\t"
"brcc 3f" "\n\t"
"mov r30, r24" "\n\t"
"ldi r31, 0" "\n\t"
"lsl r30" "\n\t"
"lsl r30" "\n\t"
"lsl r30" "\n\t"
"lsl r30 ; NADC==8!" "\n\t"
"subi r30, lo8(-(adc_conf))" "\n\t"
"sbci r31, hi8(-(adc_conf))" "\n"
"1:" "\n\t"
"ld r25, Z" "\n\t"
"subi r25, 1" "\n\t"
"cpi r25, 0xfe" "\n\t"
"brsh 3f" "\n\t"
"sbci r31, hi8(-(adc_conf))" "\n\t"
"ldd r25, Z+1" "\n\t"
"sts %[CTRLC], r25" "\n\t"
"ldd r25, Z+3" "\n\t"
@ -181,6 +164,8 @@ ISR(ADC0_RESRDY_vect, ISR_NAKED)
"ldd r25, Z+2" "\n\t"
"sts %[MUXPOS], r25" "\n\t"
"ld r25, Z" "\n\t"
"tst r25" "\n\t"
"breq 1b" "\n\t"
"sts %[COMMAND], r25" "\n"
"2:" "\n\t"
"sts adc_current, r24" "\n\t"
@ -207,98 +192,3 @@ ISR(ADC0_RESRDY_vect, ISR_NAKED)
);
}
#endif
static inline
int16_t calib_temp(uint16_t a)
{
uint16_t calib = SIGROW.TEMPSENSE0;
int16_t offset = (int8_t) SIGROW.TEMPSENSE1;
a >>= 1;
a -= offset << 5;
a = mul16sun(calib, a, 8);
a -= 8741;
// [K × 32]
return a;
}
// saturating addition, signed + signed
static inline
int16_t saddss(int16_t a, int16_t o)
{
__asm__(
" add %A[a], %A[o] \n"
" adc %B[a], %B[o] \n"
" brvc 1f \n"
" ldi %A[a], 0xff \n"
" ldi %B[a], 0x7f \n"
" brmi 1f \n"
" adiw %[a], 1 \n"
"1: \n"
: [a] "+w" (a)
: [o] "r" (o)
);
return a;
}
// saturating addition, unsigned + signed
static inline
uint16_t saddus(uint16_t a, int16_t o)
{
__asm__(
" add %A[a], %A[o] \n"
" adc %B[a], %B[o] \n"
" tst %B[o] \n"
" brmi 1f \n"
" brcc 3f \n"
" rjmp 2f \n"
"1: \n"
" brcs 3f \n"
"2: \n"
" ldi %A[a], 0xff \n"
" ldi %B[a], 0xff \n"
" brcs 3f \n"
" adiw %[a], 1 \n"
"3: \n"
: [a] "+w" (a)
: [o] "r" (o)
);
return a;
}
void send_calib_adc(uint8_t i)
{
struct adc_conf *conf = adc_conf + i;
uint8_t sgnd = conf->mode & ADC_DIFF_bm;
int16_t offset = (int16_t)conf->offset;
uint16_t calib = conf->calib;
uint16_t a = adc_readings[i];
if (conf->inp == ADC_MUXPOS_TEMPSENSE_gc) {
a = calib_temp(a);
sgnd = 1;
}
if (sgnd)
a = saddss(a, offset);
else
a = saddus(a, offset);
if (!calib) {
send_str(" 0x");
send_hex_word(a);
return;
}
uint16_t c;
if (sgnd)
c = calib;
else {
c = a;
a = calib;
}
a = mul16sun(a, c, conf->shifts >> 4);
send_char(' ');
send_str(decimal_str(a, conf->shifts & 3));
}

View file

@ -10,12 +10,9 @@ struct adc_conf {
uint8_t ref;
uint8_t inp;
uint8_t inn;
int8_t offset;
uint8_t shifts;
uint16_t calib;
};
#define N_ADC (64/sizeof(struct adc_conf))
#define N_ADC 8
extern uint16_t adc_readings[N_ADC];
extern uint8_t adc_current;
@ -24,4 +21,3 @@ void start_adc();
void init_adc();
static inline uint8_t adc_busy() { return ADC.STATUS & 1; }
extern struct adc_conf adc_conf[N_ADC];
void send_calib_adc(uint8_t i);

43
src/base85.c Normal file
View file

@ -0,0 +1,43 @@
#include <stdint.h>
#include <base85.h>
uint32_t divmod85(uint32_t u, uint8_t *m);
uint32_t mul85(uint32_t u, uint8_t m);
uint8_t base85_error;
#ifdef BASE85C
int base85_encode(uint32_t u, uint8_t *s)
{
s += 5;
*s = 0;
u = divmod85(u, s);
u = divmod85(u, s);
u = divmod85(u, s);
u = divmod85(u, s);
*--s = (u>>24) + 33;
return 5;
}
const uint8_t *base85_decode(const uint8_t *s, uint32_t *u)
{
uint32_t uu = 0;
uint8_t c;
for (int i=0; i<5; i++) {
while (1) {
c = *s++ - 33;
if (c >= 85)
goto error;
uu = mul85(uu, c);
break;
}
}
c = 0;
error:
base85_error = c;
*u = uu;
return s;
}
#endif

4
src/base85.h Normal file
View file

@ -0,0 +1,4 @@
extern uint8_t base85_error;
int base85_encode(uint32_t u, uint8_t *s);
const uint8_t *base85_decode(const uint8_t *s, uint32_t *u);

148
src/base85a.S Normal file
View file

@ -0,0 +1,148 @@
; uint32_t divmod85(uint32_t u, uint8_t *m)
; r26/r27 = m
; r25 = A LSB, big endian
; r24 = B
; r23 = C
; r22 = D MSB
; r1 = undef
#ifdef BASE85C
.global divmod85
divmod85:
movw r26, r20
# endif
;; ! C-ABI: gobbles r1, m=X
_divmod85: ; r25,r24,r23,r22 = A,B,C,D
mov r18, r25
add r18, r24
clr r21
rol r21 ; r18,r21 = A+B
add r23, r18
clr r31
adc r31, r21 ; r23,r31 = A+B+C
add r21, r23
clr r18
adc r18, r31 ; r21,r18 = A+B>>8 + A+B+C
add r23, r22
clr r20
adc r31, r20 ; r23,r31 = A+B+C+D
add r18, r23
adc r21, r31 ; r18,r21 = A+B>>16 + A+B+C>>8 + A+B+C+D
sub r23, r25
sbc r31, r20 ; r23,r31 = B+C+D
add r21, r23
adc r20, r31 ; r18,r21,r20 = … + B+C+D<<8
sub r23, r24
clr r30
sbc r31, r30 ; r23,r31 = C+D
add r20, r23
adc r31, r22
rol r30 ; r18,r21,r20,r31,r30 = … + C+D<<16 + D<<24
mov r19, r18
movw r22, r20
movw r24, r30
lsl r19
rol r21
rol r20
rol r31
rol r30
add r18, r19
adc r25, r21
adc r24, r20
adc r23, r31
adc r22, r30
ldi r19, 85
mul r18, r19
lsl r0
ldi r18, 33
adc r1, r18
# ifdef BASE85C
st X, r1
clr r1
# else
st -X, r1
# endif
ret
#ifndef BASE85C
.global base85_encode
base85_encode:
movw r26, r20
adiw r26, 5
st X, r1
rcall _divmod85
rcall _divmod85
rcall _divmod85
rcall _divmod85
subi r25, -33
st -X, r25
ldi r24, 5
clr r1
ret
# endif
; uint32_t mul85(uint32_t u, uint8_t m);
#ifdef BASE85C
.global mul85
mul85:
# endif
_mul85:
ldi r21, 85
mul r25, r21
movw r18, r0
mul r24, r21
movw r24, r0
mul r23, r21
movw r30, r0
mul r22, r21
add r18, r20
adc r24, r19
adc r30, r25
adc r31, r0
mov r25, r18
mov r23, r30
mov r22, r31
# ifdef BASE85C
clr r1
# endif
ret
#ifndef BASE85C
.global base85_decode
base85_decode:
push r17
push r22
push r23
movw r26, r24
clr r25
clr r24
clr r23
clr r22
ldi r17, 5
1:
ld r20, Y+
cpi r20, 85
brcc 2f
rcall _mul85
subi r17, 1
brne 1b
clr r20
2:
sts base85_error, r20
pop r31
pop r30
st Z, r22
std Z+1, r23
std Z+2, r24
std Z+3, r25
movw r24, r26
pop r17
clr r1
ret
# endif

93
src/bch4369.c Normal file
View file

@ -0,0 +1,93 @@
/*********************************************************************
galois.BCH(4369, 4369-128, extension_field=galois.GF(2**16))
<BCH Code: [4369, 4241, 17] over GF(2)>
hex(bch.generator_poly)
'0x124a5eda5ec0cbce2104522c1782ed521'
A flash page is 512 + 16 bytes. Use the extended page size for parity
bits. A code size of n=4369 is achievable with m=16. There are n-k =
128 parity bits. The code has a Hamming distance of d=17. That allows
correction of up to 8 error bits. The message is padded with 145 bits
of zeros.
*********************************************************************/
#include "bch4369.h"
uint8_t bch_parity[16];
const
uint8_t bch_genpoly[16] = {
0x24, 0xa5, 0xed, 0xa5,
0xec, 0x0c, 0xbc, 0xe2,
0x10, 0x45, 0x22, 0xc1,
0x78, 0x2e, 0xd5, 0x21
};
#ifdef BCH_C
void bch4369(uint8_t d)
{
uint8_t *p = bch_parity;
uint8_t m = *p;
for (int i=0; i<8; i++) {
p += 16;
const uint8_t *pp = bch_genpoly + 16;
uint8_t b = d;
d <<= 1;
uint8_t mm = m;
for (int j=0; j<16; j++) {
uint8_t bb = *--p;
m = bb << 1 | b >> 7;
if (mm & 0x80)
m ^= *--pp;
*p = m;
b = bb;
}
}
}
#else
__attribute__ ((noinline, noclone))
void bch4369(uint8_t d)
{
__asm__ __volatile__(
""
"ldi r30, lo8(bch_parity)" "\n\t"
"ldi r31, hi8(bch_parity)" "\n\t"
"ld r25, Z" "\n\t"
"bst r25, 7" "\n\t" // copy MSB of parity → T-bit
"ldi r20, 8" "\n\t" // loop r20 = 8 → 1
"1:" "\n\t"
"adiw r30, 16" "\n\t"
"ldi r26, lo8(bch_genpoly+16)" "\n\t"
"ldi r27, hi8(bch_genpoly+16)" "\n\t"
"rol %[D]" "\n\t" // move next input bit → r25.7
"ror r25" "\n\t"
"2:" "\n\t"
"rol r25" "\n\t" // Move r25.7 → C-bit
"ld r25, -Z" "\n\t" // Rotage input bit into parity byte
"rol r25" "\n\t"
"brtc 3f" "\n\t" // if T-bit:
"ld r0, -X" "\n\t" // xor with gen_poly
"eor r25, r0" "\n\t"
"3:" "\n\t"
"st Z, r25" "\n\t"
"ror r25" "\n\t" // save C-bit → r25.7 and MSB → r25.6
"cpi r30, lo8(bch_parity)" "\n\t"
"brne 2b" "\n\t"
"bst r25, 6" "\n\t" // copy MSB r25.6 → T-bit
"subi r20, 1" "\n\t"
"brne 1b" "\n"
: [D] "+w" (d)
:
: "memory", "cc",
"r0", "r20", "r21", "r25",
"r26", "r27", "r30", "r31"
);
}
#endif

6
src/bch4369.h Normal file
View file

@ -0,0 +1,6 @@
#include <stdint.h>
#include <string.h>
extern uint8_t bch_parity[16];
void bch4369(uint8_t d);
static inline void bch4369_init() { memset(bch_parity, 0 , 16); }

View file

@ -1,98 +0,0 @@
//
// calib.c
//
// MS5534-CM.pdf
// https://media.digikey.com/pdf/Data%20Sheets/Measurement%20Specialties%20PDFs/MS5534-CM.pdf
#ifdef CALIB_DEBUG
# include <stdio.h>
# define TESTDATA_ADDR
#else
# include "eeprom.h"
#endif
#include "calib.h"
#include "mul.h"
void bate_calib(const union bate *bate, struct pressure *pt)
{
uint16_t C1 = bate->W1 >> 1;
uint16_t C5 = (bate->W1 & 1) << 13 | (bate->W2 & 0xffc0) >> 3;
uint8_t C6 = bate->W2 & 0x3f;
uint16_t C4 = bate->W3 >> 6;
uint16_t C3 = bate->W4 & 0xffc0;
uint16_t C2 = (bate->W3 & 0x3f) << 6 | bate->W4 & 0x3f;
uint16_t D1 = bate->D1;
uint16_t D2 = bate->D2;
uint16_t UT1 = C5 + 20224;
uint16_t dT = D2 - UT1;
uint16_t TEMPSENS = C6 + 50;
int16_t TEMP = mul16sun(dT, TEMPSENS, 6) + 200;
int16_t TCO = C4 - 512;
uint16_t OFFT1 = C2 << 2;
// fails when TCO < 0
uint16_t OFF = OFFT1 + mul16sun(dT, TCO, 4);
uint16_t SENST1 = C1 + 24576;
uint16_t TCS = C3;
uint16_t SENS = SENST1 + mul16sun(dT, TCS, 0);
int16_t X = mul16sun(D1 - 7168, SENS, 2) - OFF;
int16_t P = mul16sun(X, 80, 8) + 2500;
pt->T = TEMP + 2732;
pt->p = P;
#ifdef CALIB_DEBUG
printf("W1 0x%04x\n", bate->W1);
printf("W2 0x%04x\n", bate->W2);
printf("W3 0x%04x\n", bate->W3);
printf("W4 0x%04x\n", bate->W4);
printf("D1 0x%04x %6d\n", D1, D1 - 7168);
printf("D2 0x%04x\n", D2);
printf("C1 %5u\n", C1);
printf("C2 %5u\n", C2);
printf("C3 %5d %5d\n", C3>>6, C3);
printf("C4 %5d\n", C4);
printf("C5 %5u %5u\n", C5>>3, C5);
printf("C6 %5u\n", C6);
printf("UT1 %5u\n", UT1);
printf("dT %5d\n", dT);
printf("TEMPSENS %5u\n", TEMPSENS);
printf("TEMP %5d\n", TEMP);
printf("TCO %5d\n", TCO);
printf("OFFT1 %5u\n", OFFT1);
printf("OFF %5u\n", OFF);
printf("SENST1 %5u\n", SENST1);
printf("TCS %5u %5u\n", TCS>>6, TCS);
printf("SENS %5u\n", SENS);
printf("X %5d\n", X);
printf("P %5d\n", P);
printf(" %.1f °C %.1f mbar\n\n", TEMP/10., P/10.);
}
#define MUL_DEBUG
#include "mul.c"
int main()
{
struct pressure pt;
for (int i=0; i<5; i++)
bate_calib(testdata+i, &pt);
return 0;
#endif
}
const union bate testdata[] TESTDATA_ADDR = {
{ .w = { 0xA691, 0x0A97, 0x989F, 0xAF28, 0x4896, 0x71F4 }, },
{ .W = { 0xabaf, 0x3c99, 0xa31a, 0xb589}, .D = { 0x470c, 0x773f }, }, // 21.2 °C, 1021.7 mbar
{ .W = { 0xabaf, 0x3c99, 0xa31a, 0xb589}, .D = { 0x1a51, 0x6fed }, }, // 7.5 °C, 5.4 mbar
{ .W = { 0xaa3d, 0x35d9, 0xcbe5, 0xb736}, .D = { 0x4bb7, 0x7487 }, }, // 17.7 °C, 1023.0 mbar
{ .W = { 0xaa3d, 0x35d9, 0xcbe5, 0xb736}, .D = { 0x1e25, 0x650a }, }, // -11.3 °C, 2.4 mbar
};

View file

@ -1,29 +0,0 @@
#include <stdint.h>
// !!! int = int8_t
union bate {
uint8_t b[12];
uint16_t w[6];
struct {
uint16_t W[4];
uint16_t D[2];
};
struct {
uint16_t W1;
uint16_t W2;
uint16_t W3;
uint16_t W4;
uint16_t D1;
uint16_t D2;
};
};
struct pressure {
uint16_t T;
uint16_t p;
};
#define N_TESTDATA (64/sizeof(union bate))
void bate_calib(const union bate *bate, struct pressure *pt);
extern const union bate testdata[N_TESTDATA];

301
src/cmd.c
View file

@ -2,215 +2,118 @@
// cmd.c
//
#include "bate.h"
#include "rtc.h"
#include <string.h>
#include "cmd.h"
#include "adc.h"
#include "flash.h"
#include "bch4369.h"
#include "base85.h"
#include "uart.h"
#ifdef NOCMD
// no space for this
void parse_command(uint8_t *s, uint8_t n) {}
#else
void base85_send_buffer(const uint8_t *buf);
const uint8_t *base85_fill_buffer(const uint8_t *s);
const uint8_t *skip_space(const uint8_t *s);
uint8_t cmd_buffer[16];
uint8_t uart_stream_source;
uint8_t rx_params[8];
#if 0
__attribute__ ((noinline, noclone))
uint8_t parse_hex_nibble(uint8_t c)
void parse_command(const uint8_t *s, uint8_t n)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 0x0a;
return 0xf0;
}
static inline
uint8_t parse_rx_params(uint8_t *s, uint8_t n)
{
uint8_t p;
p = 0;
while (n) {
while (n) {
if (*s != ' ')
break;
n--;
s++;
}
if (!n || p >= sizeof(rx_params))
break;
uint8_t d = parse_hex_nibble(*s);
if (d==0xf0)
break;
n--;
s++;
if (n) {
uint8_t h = 0;
h = parse_hex_nibble(*s);
if (h != 0xf0) {
n--;
s++;
d <<= 4;
d |= h;
}
}
rx_params[p++] = d;
DEBUG_POKE(rxhex, d);
}
if (!n || *s != '\n')
p = 0;
DEBUG_POKE(rxpar, p);
return p;
}
#else
static inline
uint8_t parse_rx_params(uint8_t *s, uint8_t n)
{
uint8_t p;
__asm__("\n"
"1: \n"
" clr %[p] \n"
" tst %[n] \n"
" breq 8f \n"
" ldi r26, lo8(rx_params) \n"
" ldi r27, hi8(rx_params) \n"
"2: \n"
" ld r21, Z+ \n"
" cpi r21, ' ' \n"
" breq 6f \n"
" cpi r21, '\\n' \n"
" breq 8f \n"
" rcall nibble \n"
" brcc 7f \n"
" mov r25, r21 \n"
" subi %[n], 1 \n"
" breq 7f \n"
" ld r21, Z \n"
" rcall nibble \n"
" brcc 5f \n"
" subi %[n], 1 \n"
" breq 7f \n"
" adiw r30, 1 \n"
" swap r25 \n"
" or r25, r21 \n"
"5: \n"
" cpi %[p], 8 ; sz rx_params \n"
" brcc 7f \n"
" subi %[p], -1 \n"
" st X+, r25 \n"
#ifdef DEBUG
" sts debug_data + 6, r25 \n"
" sts debug_data + 9, %[p] \n"
#endif
" rjmp 2b \n"
" \n"
"nibble: \n"
" subi r21, '0' \n"
" cpi r21, 10 \n"
" brcs 9f \n"
" subi r21, 'a'-'0' \n"
" cpi r21, 6 \n"
" brcc 9f \n"
" subi r21, -10 \n"
" sec \n"
"9: ret \n"
" \n"
"6: \n"
" subi %[n], 1 \n"
" brne 2b \n"
"7: \n"
" clr %[p] \n"
"8: \n"
: [p] "=a" (p), // r24 (r16…)
[s] "+z" (s), // r30,r31
[n] "+a" (n) // r22 (r16…)
: : "r21", "r25", "r26", "r27"
);
DEBUG_POKE(rxpar, p);
return p;
}
#endif
void parse_command(uint8_t *s, uint8_t n)
{
if (!n || n >= 15)
goto fail;
uint8_t cmd = *s++;
uint8_t p = parse_rx_params(s, n-1);
uint8_t *pp = rx_params;
uint8_t *a = (uint8_t *)(((uint16_t)pp[0] << 8) | pp[1]);
if (cmd == 'R' && p==1)
__asm__("\n"
" ldi r18, 1 \n"
" out %[ccp], %[p] \n"
" sts %[swrr], r18 \n"
:
: [p] "r" (pp[0]),
[ccp] "n" (_SFR_IO_ADDR(CCP)),
[swrr] "n" (&RSTCTRL.SWRR)
: "r18"
);
if (p >= 3 && p < 8 && (
cmd == 'C' && pp[0]==0xba
|| cmd == 'E'
|| cmd == 'U')) {
uint8_t *e = (uint8_t*) &config;
if (cmd=='E')
e = (uint8_t*) EEPROM_START;
if (cmd=='U')
e = (uint8_t*) &USERROW;
e += pp[1];
pp[p] = *e;
memcpy(e, pp+2, p-2);
if (cmd != 'C')
__asm__("\n"
" ldi r24, %[erwp] \n"
" out %[ccp], %[p] \n"
" sts %[ctrl], r24 \n"
:
: [p] "r" (pp[0]),
[ccp] "n" (_SFR_IO_ADDR(CCP)),
[ctrl] "n" (&NVMCTRL.CTRLA),
[erwp] "n" (NVMCTRL_CMD_PAGEERASEWRITE_gc)
: "r24"
);
if (*s =='|')
return;
s = skip_space(s);
uint8_t flag = *s++;
uint8_t r = 0;
send_char(cmd);
send_char('|');
if (cmd == 'A') {
r = adc_current;
if (r >= N_ADC) {
if (flag == '>') {
base85_send_buffer((void*)adc_readings);
flag = *s++;
}
else if (cmd == 'T' && p==1) {
immediate += pp[0];
pp[1] = immediate;
if (flag == '+')
start_adc();
}
else if (cmd == 'M' && p==2) {
pp[2] = *a;
}
else if (cmd == 'W' && p==3) {
pp[3] = *a;
*a = pp[2];
if (cmd == 'W') {
s = base85_fill_buffer(s);
r = base85_error;
}
else if (cmd == 'K' && p==4) {
cli();
pp[4] = clock_tick;
clock = *(uint32_t*)pp;
sei();
else if (cmd == 'B') {
if (flag == '<') {
s = base85_fill_buffer(s);
flag = *s - '0';
if (flag < 4) {
s++;
if (!(uart_stream_source & 0xe0)) {
memcpy(flash_buffer+16*flag, cmd_buffer, 16);
uart_stream_source = flag;
}
else if (cmd == 'D' && p==1) {
pp[1] = test_calib;
test_calib = pp[0];
r = uart_stream_source;
}
flag = *s++;
}
r = base85_error;
if (!r && flag == '%') {
if (*s=='>')
base85_send_buffer(bch_parity);
else if (*s=='<')
bch4369_feed_buffer(*++s);
r = bch4369_feed_n;
}
else if (flag == '>') {
uint8_t *b = cmd_buffer;
flag = *s - '0';
if (flag < 4)
b = flash_buffer + 16*flag;
base85_send_buffer(b);
}
}
else if (cmd=='F') {
base85_fill_buffer(s);
r = base85_error;
if (!r)
r = flash_submit_command(cmd_buffer);
}
else
goto fail;
send_char('R');
send_char('!');
send_hex(cmd, pp, p+1, 1);
return;
fail:
send_str("R?\n");
send_char('?');
send_hex_byte_eol(r);
}
#endif
const uint8_t *skip_space(const uint8_t *s) {
while (*s == ' ')
s++;
return s;
}
const uint8_t *base85_fill_buffer(const uint8_t *s) {
base85_error = 0;
s = skip_space(s);
for (int i=0; !base85_error && i<FB_SIZE; i+=4)
s = base85_decode(s, (uint32_t *)(cmd_buffer + i));
s = skip_space(s);
return s;
}
void base85_send_buffer(const uint8_t *buf)
{
uint8_t s[6];
for(int i=0; i<16; i++) {
base85_encode(buf[i], s);
send_str((const char *)s);
}
}
uint8_t bch4369_feed_n;
void bch4369_feed_buffer(uint8_t flag)
{
if (flag=='0') {
bch4369_feed_n = 0;
memset(bch_parity, 0, 16);
}
for (int i=0; i<16; i++)
bch4369(cmd_buffer[i]);
bch4369_feed_n++;
}

8
src/cmd.h Normal file
View file

@ -0,0 +1,8 @@
#include <stdint.h>
extern uint8_t base85_error;
extern uint8_t bch4369_feed_n;
void parse_command(const uint8_t *s, uint8_t n);
void bch4369_feed_buffer(uint8_t flag);
extern uint8_t cmd_buffer[16];

57
src/config.c Normal file
View file

@ -0,0 +1,57 @@
#include "config.h"
struct_ioconf(port_config) = {
conf_prefix(PORTA),
conf_io(PORTA.PIN6CTRL, PORT_ISC_INPUT_DISABLE_gc), // AIN
conf_io(PORTA.PIN7CTRL, PORT_ISC_INPUT_DISABLE_gc), // AIN
conf_io(PORTB.PIN1CTRL, PORT_ISC_INPUT_DISABLE_gc), // AIN
conf_io(PORTA.OUT, Bit(SSEL_PIN)),
conf_io(PORTA.DIR, Bit(SSEL_PIN) | Bit(DRAIN_PIN)
| Bit(MOSI_PIN) | Bit(SCK_PIN)),
conf_io(PORTB.OUT, 0),
conf_io(PORTB.DIR, Bit(PWM_PIN)),
};
#if 0
void apply_config(const struct io_config *c, uint8_t n)
{
uint8_t prefix = 0;
while (n--) {
const struct io_config *io = c++;
if (io->addr == 0xff)
prefix = io->val;
else {
uint8_t *p = (uint8_t*)((uint16_t)prefix << 8 | io->addr);
*p = io->val;
}
}
}
#else
__attribute__((naked))
void apply_config(const struct io_config *c, uint8_t n)
{
__asm__(
"movw r30,r24" "\n\t"
"clr r24" "\n"
"1:" "\n\t"
"mov r24, r27" "\n\t"
"subi r22,-1" "\n\t"
"brcs 9f" "\n"
"2:" "\n\t"
"ld r25, Z+" "\n\t"
"ld r24, Z+" "\n\t"
"cpi r25, 0xff" "\n\t"
"breq 1b" "\n\t"
"st X, r25" "\n\t"
"subi r22,1" "\n\t"
"brcc 2b" "\n\t"
"9:" "\n\t"
"ret" "\n\t"
);
}
#endif

43
src/config.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef _CONFIG_H
#define _CONFIG_H
#include <avr/io.h>
#include <stdint.h>
struct io_config {
uint8_t addr;
uint8_t val;
};
#define struct_ioconf(_n) struct io_config _n[] \
__attribute__((section(".eeprom9"), aligned(2)))
#define conf_prefix(_io) { 0xff, (uint16_t)&(_io) >> 8 }
#define conf_io(_io, _v) { (uint16_t)&(_io) & 0xff, (_v) }
#define conf_ioo(_io, _o, _v) { (uint16_t)&(_io) + _o & 0xff, (_v) }
#define conf_iow(_io, _v) conf_io(_io, (_v) & 0xff), conf_ioo(_io, 1, (_v) >> 8)
void apply_config(const struct io_config *c, uint8_t n);
extern struct io_config *ee9_start, *ee9_end;
#define IO_CONFIG ee9_start
#define IO_CONFIG_SIZE (ee9_end - ee9_start)
#define Bit(x) (1<<(x))
#define DRAIN_VPORT VPORTA
#define DRAIN_PORT PORTA
#define DRAIN_PIN 5
#define SSEL_VPORT VPORTA
#define SSEL_PORT PORTA
#define SSEL_PIN 4
#define PWM_VPORT VPORTB
#define PWM_PORT PORTB
#define PWM_PIN 0
#define SPI_VPORT VPORTA
#define SPI_PORT PORTA
#define MOSI_PIN 1
#define MISO_PIN 2
#define SCK_PIN 3
#endif // _CONFIG_H

62
src/dose.c Normal file
View file

@ -0,0 +1,62 @@
//
// dose.c
//
// !!! int = int8_t
#include <stdint.h>
#include <string.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include "config.h"
#include "dose.h"
#include "uart.h"
#include "rtc.h"
#include "spi.h"
#include "cmd.h"
#include "adc.h"
#include "pwm.h"
#include "flash.h"
////////////////////////////////////////////////////////////////////////////////
//
// Configuration in USERROW
__attribute__((section(".userrow")))
const struct config config = {
.magic = USE_USERROW,
.version = USE_VERSION,
.cpu_clk = CLKCTRL_PDIV_2X_gc | 1, // 10MHz (max @ 3V)
.flash_page_size = FM_528,
};
////////////////////////////////////////////////////////////////////////////////
//
// main()
int main()
{
while (CLKCTRL.MCLKCTRLB != config.cpu_clk) {
CCP = CCP_IOREG_gc;
CLKCTRL.MCLKCTRLB = config.cpu_clk;
}
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();
while (config.magic != USE_USERROW)
sleep_cpu();
apply_config(IO_CONFIG, IO_CONFIG_SIZE);
uint8_t reset_source = RSTCTRL.RSTFR;
RSTCTRL.RSTFR = reset_source;
send_str("\nV Turbo Dose V0.9");
send_hex_byte_eol(reset_source);
while (1) {
sleep_cpu();
command();
}
}

25
src/dose.h Normal file
View file

@ -0,0 +1,25 @@
//
// dose.h
//
////////////////////////////////////////////////////////////////////////////////
//
// Configuration
#include <stdint.h>
#include <avr/io.h>
struct config {
uint8_t magic;
uint8_t version;
uint8_t cpu_clk;
uint8_t flash_page_size;
};
enum magic_flags {
USE_USERROW = 0xD0,
USE_VERSION = 0x01,
};
extern const struct config config;

View file

@ -1,3 +0,0 @@
#define ADC_CONF_ADDR __attribute__((__section__(".eeprom")))
#define TESTDATA_ADDR __attribute__((__section__(".eeprom")))

View file

@ -6,12 +6,24 @@ MEMORY
{
eemap : ORIGIN = 0x1400, LENGTH = 0x80
eedef : ORIGIN = 0x810000, LENGTH = 0x80
uumap : ORIGIN = 0x1300, LENGTH = 0x20
uudef : ORIGIN = 0x850000, LENGTH = 0x20
}
SECTIONS
{
.eemap 0x1400:
{
ee1_start = .;
*(.eeprom1)
ee1_end = .;
*(.eeprom)
ee9_start = .;
*(.eeprom9)
ee9_end = .;
} >eemap AT >eedef
.uumap 0x1300:
{
*(.userrow)
} >uumap AT >uudef
}
INSERT BEFORE .eeprom

212
src/flash.c Normal file
View file

@ -0,0 +1,212 @@
/******************************************************
- opcode (hex)
- address
- pb page and byte address
- xb byte address
- px page address
- kx block address
- sx sector address
- xxx don't care
- padding
- 0 1 byte
- 00 2 bytes
- 0000 4 bytes
- data
- data bytes
- | no data bytes
read commands
- Main Memory Page Read D2 pb 0000
- Continuous Array Read (Low-Power Mode) 01 pb
- Continuous Array Read (Low-Frequency) 03 pb
- Continuous Array Read (High-Frequency) 0B pb 0
- Continuous Array Read (High-Frequency) 1B pb 00
- Continuous Array Read (Legacy Command) E8 pb 0000
- Buffer 1 Read (Low-Frequency) D1 xb
- Buffer 2 Read (Low-Frequency) D3 xb
- Buffer 1 Read (High-Frequency) D4 xb 0
- Buffer 2 Read (High-Frequency) D6 xb 0
write commands
- Buffer 1 Write 84 xb
- Buffer 2 Write 87 xb
- Buffer 1 Page Program with Erase 83 px |
- Buffer 2 Page Program with Erase 86 px |
- Buffer 1 Page Program w/o Erase 88 px |
- Buffer 2 Page Program w/o Erase 89 px |
- Page through Buffer 1 with Erase 82 pb
- Page through Buffer 2 with Erase 85 pb
- Byte/Page through Buffer 1 w/o Erase 02 pb
- Page Erase 81 px |
- Block Erase 50 kx |
- Sector Erase 7C sx |
- Chip Erase C7_94_80_9A |
- Program/Erase Suspend B0 |
- Program/Erase Resume D0 |
- Read-Modify-Write through Buffer 1 58 pbx
- Read-Modify-Write through Buffer 2 59 pbx
security commands
- Enable Sector Protection 3D_2A_7F_A9 |
- Disable Sector Protection 3D_2A_7F_9A |
- Erase Sector Protection Register 3D_2A_7F_CF |
- Program Sector Protection Register 3D_2A_7F_FC
- Read Sector Protection Register 32 xxx
- Sector Lockdown 3D_2A_7F_30
- Read Sector Lockdown Register 35 xxx
- Freeze Sector Lockdown 34_55_AA_40 |
- Program Security Register 9B_00_00_00
- Read Security Register 77 xxx
miscellanious
- Main Memory Page to Buffer 1 Transfer 53 px |
- Main Memory Page to Buffer 2 Transfer 55 px |
- Main Memory Page to Buffer 1 Compare 60 px |
- Main Memory Page to Buffer 2 Compare 61 px |
- Auto Page Rewrite through Buffer 1 58 px |
- Auto Page Rewrite through Buffer 2 59 px |
- Deep Power-Down B9 |
- Resume from Deep Power-Down AB |
- Ultra-Deep Power-Down 79 |
- Status Register Read D7
- Manufacturer and Device ID Read 9F
- Power of 2 (Binary) Page Size 3D_2A_80_A6 |
- Standard DataFlash Page Size 3D_2A_80_A7 |
- Software Reset F0_00_00_00 |
*/
#include "flash.h"
#include "cmd.h"
uint8_t flash_cmd_buffer[4];
uint8_t flash_status_bytes[2];
uint8_t flash_buffer[FB_SIZE];
uint8_t flash_cmd_na(uint16_t mode, uint16_t what)
{
return flash_cmd(mode, what, 0, 0);
}
uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte)
{
uint8_t spi_mode = SPI_FLASH | mode & FM_SPI;
uint8_t op = mode;
mode >>= 8;
uint8_t size = what;
what >>= 8;
uint8_t s = spi_select(spi_mode);
if (s)
return s;
uint8_t *b = flash_cmd_buffer;
*b++ = op ;
switch (mode & FM_ADDR) {
case FM_528:
page <<= 1;
case FM_512:
byte |= page << 9;
page >>= 7;
case FM_SEC:
*b++ = page;
*b++ = byte>>8;
*b++ = byte;
}
uint8_t csize = b - flash_cmd_buffer;
uint8_t pads = 0;
switch (mode & FM_PAD) {
case FM_PAD4: pads = 2;
case FM_PAD2: pads += 1;
case FM_PAD1: pads += 1;
}
spi.zsize = pads;
if (what + size <= 64)
b = flash_buffer + what;
else if (what+size <= 80)
b = cmd_buffer + (what-64);
else if (size <= 2)
b = flash_status_bytes;
else
return what;
switch (mode & FM_START) {
case FM_WRITE:
spi_start_write(csize, flash_cmd_buffer, size, b);
break;
case FM_READ:
spi_start_read(csize, flash_cmd_buffer, size, b);
break;
case FM_WAIT:
spi.mask = 0x80;
spi_start_read(csize, flash_cmd_buffer, 2, b);
break;
}
return 0;
}
uint16_t flash_page, flash_block, n_blocks;
uint8_t flash_stream_status;
enum {
FS_IDLE = 0,
FS_Rdy = 1,
FS_Bsy = 2,
FS_Read = 4,
FS_Write = 8,
FS_Erase = 12,
FS_Buff = 32,
FS_StBsy = 64,
FS_TxBCH = 128,
};
uint8_t flash_poll()
{
uint8_t r = spi_busy_p();
if (r)
// SPI is shifting something
return 0x80 | r;
r = flash_stream_status;
if (r & FS_StBsy) {
// status bytes arrived
if (flash_status_bytes[0] & 0x80)
// flash is still busy burning
goto rd_status;
// not busy any more, move Bsy → Rdy
if (r & FS_Bsy)
r |= FS_Rdy;
goto rdy;
}
if (!(r & FS_Bsy))
goto rdy;
if (r & FS_Write) {
rd_status:
// request status bytes for pending Tx od Er
r |= FS_StBsy;
flash_stream_status = r;
flash_cmd_na(0xd7, 0xff02);
return r & FS_Bsy;
}
// Rx is Rdy when SPI is idle
rdy:
// clear the Bsy bits
r &= ~(FS_Bsy | FS_StBsy);
// return the ready bits
flash_stream_status = r;
return r;
}
struct flash_cmd {
uint16_t mode, what, page, byte;
uint8_t buffer[8];
};
uint8_t flash_submit_command(uint8_t *cmd)
{
struct flash_cmd *c = (void*)cmd;
return flash_cmd(c->mode, c->what, c->page, c->byte);
}

24
src/flash.h Normal file
View file

@ -0,0 +1,24 @@
#include "spi.h"
enum flash_mode_bits {
FM_PAD1 = 0x01,
FM_PAD2 = 0x02,
FM_PAD4 = 0x03,
FM_PAD = 0x03,
FM_512 = 0x04,
FM_528 = 0x08,
FM_SEC = 0x0c,
FM_ADDR = 0x0c,
FM_WRITE = 0x10,
FM_READ = 0x20,
FM_WAIT = 0x30,
FM_START = 0x30,
FM_CONT = SPI_CONT,
FM_SPI = FM_CONT,
};
uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte);
#define FB_SIZE 64
extern uint8_t flash_buffer[FB_SIZE];
uint8_t flash_submit_command(uint8_t *cmd);

284
src/mul.c
View file

@ -1,284 +0,0 @@
//
// mul.c
//
// To save space in ATtinys.
// 16bit × 16bit → 16bit multiplication for AVR w/ `mul`, `muls`, `mulsu`,
// returning the high bits, not the low bits.
//
// Also provides a variant for printing decimal numbers.
#include "mul.h"
// To save space, omit what is not needed. Use -D when needed.
#ifndef MUL_NONE
# define MUL16SUN
# define MUL_DECIMAL_STR
# ifdef MUL_ALL
# define MUL16SU
# define MUL16SS
# define MUL16UU
# endif
#endif
#ifdef MUL_TEST
#define MUL_DEBUG
#include <stdio.h>
int main()
{
int16_t tests[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
89, 99, 100, 101, 102, 0xff, 0x100, 0x101,
999, 1000, 1001, 9999, 10000, 10001,
0x7ffe, 0x7fff
};
for (uint8_t dec = 0; dec < 5; dec++)
for (int i=0; i < sizeof(tests)/sizeof(*tests); i++)
for (int s=0; s<2; s++) {
int16_t n = tests[i];
if (s)
n = -n;
printf("%u 0x%04x %+d %s\n",
dec, n & 0xffff, n,
decimal_str(n, dec));
}
return 0;
}
#endif
#ifndef MUL_DEBUG
#ifdef MUL16SU
int16_t mul16su(int16_t s, uint16_t u)
{
__asm__(
"movw r18, %[s]" "\n\t"
"mul r18, %A[u]" "\n\t"
"mov r20, r1" "\n\t"
"mulsu r19, %B[u]" "\n\t"
"movw %[s], r0" "\n\t"
"mul r18, %B[u]" "\n\t"
"clr r18" "\n\t"
"add r20, r0" "\n\t"
"adc %A[s], r1" "\n\t"
"adc %B[s], r18" "\n\t"
"mulsu r19, %A[u]" "\n\t"
"sbc %B[s], r18" "\n\t"
"add r20, r0" "\n\t"
"adc %A[s], r1" "\n\t"
"adc %B[s], r18" "\n\t"
"clr r1" "\n\t"
: [s] "+r" (s)
: [u] "a" (u)
: "r0", "r1", "r18", "r19", "r20"
);
return s;
}
#endif
#ifdef MUL16SUN
int16_t mul16sun(int16_t s, uint16_t u, uint8_t n)
{
// n = 0 … 8
__asm__(
"movw r18, %[s]" "\n\t"
"mul r18, %A[u]" "\n\t"
"mov r21, r1" "\n\t"
"mulsu r19, %B[u]" "\n\t"
"movw %[s], r0" "\n\t"
"mul r18, %B[u]" "\n\t"
"clr r18" "\n\t"
"add r21, r0" "\n\t"
"adc %A[s], r1" "\n\t"
"adc %B[s], r18" "\n\t"
"mulsu r19, %A[u]" "\n\t"
"sbc %B[s], r18" "\n\t"
"add r21, r0" "\n\t"
"adc %A[s], r1" "\n\t"
"adc %B[s], r18" "\n\t"
"cpi %[n], 0" "\n\t"
"breq 3f" "\n\t"
"sbrs %[n], 3" "\n\t"
"rjmp 2f" "\n\t"
"mov %B[s], %A[s]" "\n\t"
"mov %A[s], r21" "\n\t"
"rjmp 3f" "\n"
"1:" "\t"
"lsl r21" "\n\t"
"rol %A[s]" "\n\t"
"rol %B[s]" "\n\t"
"2:" "\t"
"dec %[n]" "\n\t"
"brpl 1b" "\n"
"3:" "\t"
"clr r1" "\n\t"
: [s] "+r" (s)
: [u] "a" (u),
[n] "r" (n)
: "r0", "r1", "r18", "r19", "r21"
);
return s;
}
#endif
#ifdef MUL16SS
int16_t mul16ss(int16_t a, int16_t b)
{
// ((int32_t)a*b) >> 16
__asm__(
"movw r18, %[a]" "\n\t"
"mul r18, %A[b]" "\n\t"
"mov r20, r1" "\n\t"
"muls r19, %B[b]" "\n\t"
"movw %[a], r0" "\n\t"
"mulsu %B[b], r18" "\n\t"
"clr r18" "\n\t"
"sbc %B[a], r18" "\n\t"
"add r20, r0" "\n\t"
"adc %A[a], r1" "\n\t"
"adc %B[a], r18" "\n\t"
"mulsu r19, %A[b]" "\n\t"
"sbc %B[a], r18" "\n\t"
"add r20, r0" "\n\t"
"adc %A[a], r1" "\n\t"
"adc %B[a], r18" "\n\t"
"clr r1" "\n\t"
: [a] "+r" (a)
: [b] "a" (b)
: "r0", "r1", "r18", "r19", "r20"
);
return a;
}
#endif
#ifdef MUL16UU
int16_t mul16uu(uint16_t a, uint16_t b)
{
// ((uint32_t)a*(uint32_t)b) >> 16
__asm__(
"movw r18, %[a]" "\n\t"
"mul r18, %A[b]" "\n\t"
"mov r20, r1" "\n\t"
"mul r19, %B[b]" "\n\t"
"movw %[a], r0" "\n\t"
"mul %B[b], r18" "\n\t"
"clr r18" "\n\t"
"add r20, r0" "\n\t"
"adc %A[a], r1" "\n\t"
"adc %B[a], r18" "\n\t"
"mul r19, %A[b]" "\n\t"
"add r20, r0" "\n\t"
"adc %A[a], r1" "\n\t"
"adc %B[a], r18" "\n\t"
"clr r1" "\n\t"
: [a] "+r" (a)
: [b] "a" (b)
: "r0", "r1", "r18", "r19", "r20"
);
return a;
}
#endif
#if defined(MUL_DIVMOD10) || defined(MUL_DECIMAL_STR)
#ifndef MUL_DIVMOD10
static inline
#endif
uint16_t divmod10(uint16_t u, uint8_t *mod)
{
uint16_t r;
uint8_t d;
__asm__(
"ldi r18, lo8(6554)" "\n\t"
"ldi r19, hi8(6554)" "\n\t"
"mul r18, %A[u]" "\n\t"
"mov r20, r1" "\n\t"
"mul r19, %B[u]" "\n\t"
"movw %[r], r0" "\n\t"
"mul r19, %A[u]" "\n\t"
"clr r19" "\n\t"
"add r20, r0" "\n\t"
"adc %A[r], r1" "\n\t"
"adc %B[r], r19" "\n\t"
"mul r18, %B[u]" "\n\t"
"add r20, r0" "\n\t"
"adc %A[r], r1" "\n\t"
"adc %B[r], r19" "\n\t"
"ldi r19, 10" "\n\t"
"inc r20" "\n\t"
"mul r19, r20" "\n\t"
"mov %[d], r1" "\n\t"
"clr r1" "\n\t"
: [r] "=&r" (r),
[d] "=r" (d)
: [u] "d" (u)
: "r0", "r1", "r18", "r19", "r20"
);
*mod = d;
return r;
}
#endif
#else // MUL_DEBUG
// Models for what the assembly is supposed to do.
// Also usefull for testing in non-AVR hosts.
int16_t mul16su(int16_t s, uint16_t u)
{
uint32_t r = (int32_t)s * (int32_t)u;
return r >> 16;
}
int16_t mul16sun(int16_t s, uint16_t u, uint8_t n)
{
// n = 0 … 8
uint32_t r = (int32_t)s * (int32_t)u;
return r >> (16-n);
}
int16_t mul16ss(int16_t a, int16_t b)
{
uint32_t r = (int32_t)a * (int32_t)b;
return r >> 16;
}
int16_t mul16uu(uint16_t a, uint16_t b)
{
uint32_t r = (int32_t)a * (int32_t)b;
return r >> 16;
}
uint16_t divmod10(uint16_t u, uint8_t *mod)
{
uint32_t r = (uint32_t)u * (uint32_t)6554;
*mod = (((r & 0xff00)+0x100)*10) >> 16;
return r >> 16;
}
#endif // MUL_DEBUG
#ifdef MUL_DECIMAL_STR
static uint8_t decimals[8];
char *decimal_str(int16_t b, uint8_t dec)
{
uint8_t s = 0;
if (b<0) {
s = 1;
b = -b;
}
uint8_t *c = decimals+6;
uint8_t n = 0;
while ((b || n < dec) && n<7) {
b = divmod10(b, c);
*c-- += '0';
if (++n==dec) {
*c-- = '.';
n++;
}
}
if (!n)
*c = '0';
else if (s)
*c = '-';
else
*c = '+';
return (char *)c;
}
#endif

View file

@ -1,15 +0,0 @@
//
// mul.h
//
#include <stdint.h>
int16_t mul16su(int16_t s, uint16_t u);
int16_t mul16ss(int16_t a, int16_t b);
int16_t mul16uu(uint16_t a, uint16_t b);
int16_t mul16sun(int16_t s, uint16_t u, uint8_t n);
char *decimal_str(int16_t b, uint8_t dec);
#if defined(MUL_DIVMOD10) || defined(MUL_ALL)
uint16_t divmod10(uint16_t u, uint8_t *mod);
#endif

30
src/pwm.c Normal file
View file

@ -0,0 +1,30 @@
// pwm.c
#include "pwm.h"
#ifdef NO_EE_CONFIG
void init_pwm(uint16_t period)
{
PWM.CTRLA = 0;
PWM.CTRLB = TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
PWM.PER = period;
PWM.CMP0 = 0xffff;
PWM.CTRLA = 0x81;
D_PORT.OUT |= 1<<D_PIN;
D_PORT.DIR |= 1<<D_PIN;
}
#else
struct_ioconf(pwm_config) = {
conf_prefix(PWM),
conf_io(PWM.CTRLA, 0),
conf_io(PWM.CTRLB, TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc),
conf_iow(PWM.PER, 0xfff),
conf_iow(PWM.CMP0, 0xffff),
conf_io(PWM.CTRLA, 0x81),
};
#endif

32
src/pwm.h Normal file
View file

@ -0,0 +1,32 @@
#include "config.h"
#define PWM TCA0.SINGLE
void init_pwm(uint16_t period);
static inline
void pwm_set(uint16_t dc, uint8_t d)
{
PWM.CMP0 = dc;
if (d)
DRAIN_VPORT.OUT |= 1 << DRAIN_PIN;
else
DRAIN_VPORT.OUT &=~ (1 << DRAIN_PIN);
}
static inline
void pwm_bias()
{
pwm_set(0xffff, 0);
}
static inline
void pwm_step()
{
uint16_t c = PWM.CMP0+1;
if (c > PWM.PER+1)
pwm_bias();
else
pwm_set(c, 1);
}

136
src/rtc.c
View file

@ -4,69 +4,117 @@
// !!! int = int8_t
#include "bate.h"
#include "config.h"
#include "rtc.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#define Bit(x) (1<<(x))
volatile uint32_t clock;
volatile uint8_t clock_tick;
volatile uint16_t clock;
volatile uint8_t pit_tick;
volatile uint8_t rtc_tick;
void init_rtc(uint8_t p)
{
if (p>=16)
p = RTC_PERIOD_CYC1024_gc;
RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;
RTC.PITINTCTRL = 1;
RTC.PITCTRLA = p;
while (RTC.PITSTATUS & 1) ;
RTC.PITCTRLA = p | 1;
}
struct_ioconf(rtc_config) = {
conf_prefix(RTC),
conf_iow(RTC.CMP, 3600),
conf_io(RTC.CLKSEL, RTC_CLKSEL_INT1K_gc),
conf_io(RTC.PITINTCTRL, 1),
conf_io(RTC.PITCTRLA, RTC_PERIOD_CYC1024_gc | RTC_PITEN_bm),
conf_iow(RTC.PER, 0xffff),
conf_io(RTC.CTRLA, RTC_PRESCALER_DIV1024_gc | RTC_RUNSTDBY_bm | RTC_RTCEN_bm),
conf_io(RTC.INTCTRL, RTC_CMP_bm | RTC_OVF_bm),
};
#if 1
ISR(RTC_PIT_vect, ISR_NAKED)
{
__asm__ ("push r24" "\n\t"
"in r24, __SREG__" "\n\t"
__asm__ (
"push r24" "\n\t"
#ifdef DEBUG
"lds r24, debug_data+9" "\n\t"
"subi r24, -1" "\n\t"
"sts debug_data+9, r24" "\n\t"
#endif
"ldi r24, 1" "\n\t"
"sts %[flag], r24" "\n\t"
"sts %[tick], r24" "\n\t"
"lds r24, %[clock]" "\n\t"
"subi r24, -1" "\n\t"
"sts %[clock], r24" "\n\t"
"lds r24, %[clock]+1" "\n\t"
"sbci r24, -1" "\n\t"
"sts %[clock]+1, r24" "\n\t"
"lds r24, %[clock]+2" "\n\t"
"sbci r24, -1" "\n\t"
"sts %[clock]+2, r24" "\n\t"
"lds r24, %[clock]+3" "\n\t"
"sbci r24, -1" "\n\t"
"sts %[clock]+3, r24" "\n\t"
"pop r24" "\n\t"
"out __SREG__, r24" "\n\t"
"pop r24" "\n\t"
"sts pit_tick, r24" "\n\t"
"ldi r24, " "\n\t"
"sts %[flag], r24" "\n\t"
"reti" "\n"
:[tick] "+m" (clock_tick),
[clock] "+m" (clock)
:[flag] "n" (&RTC.PITINTFLAGS)
::[flag] "n" (&RTC.PITINTFLAGS)
);
}
ISR(RTC_CNT_vect, ISR_NAKED)
{
__asm__ (
"push r24" "\n\t"
"in r24, __SREG__" "\n\t"
"push r24" "\n\t"
"lds r24, %[flag]" "\n\t"
"sts %[flag], r24" "\n\t"
"sts rtc_tick, r24" "\n\t"
"sbrs r24, 1" "\n\t"
"rjmp 1f" "\n\t"
"push r24" "\n\t"
"push r25" "\n\t"
"push r26" "\n\t"
"lds r24, %[CMPL]" "\n\t"
"lds r25, %[CMPH]" "\n\t"
"lds r26, config+3" "\n\t"
"add r24, r26" "\n\t"
"sts %[CMPL], r24" "\n\t"
"lds r26, config+5" "\n\t"
"add r25, r26" "\n\t"
"sts %[CMPH], r25" "\n\t"
"pop r26" "\n\t"
"pop r25" "\n\t"
"pop r24" "\n"
"1:" "\n\t"
"sbrs r24, 0" "\n\t"
"rjmp 2f" "\n\t"
"lds r24, clock" "\n\t"
"subi r24, -1" "\n\t"
"sts clock, r24" "\n\t"
"lds r24, clock+1" "\n\t"
"sbci r24, -1" "\n\t"
"sts clock+1, r24" "\n\t"
"2:" "\n\t"
"pop r24" "\n\t"
"out __SREG__, r24" "\n"
"pop r24" "\n\t"
"reti" "\n"
::
[flag] "n" (&RTC.INTFLAGS),
[CMPL] "n" (&RTC.CMP),
[CMPH] "n" (&RTC.CMP+1)
);
}
#else
ISR(RTC_PIT_vect)
{
clock_tick = 1;
RTC.PITINTFLAGS = 1;
clock++;
}
ISR(RTC_CNT_vect)
{
uint8_t flag = RTC.INTFLAGS;
RTC.INTFLAGS = flag;
rtc_tick = flag;
if (flag & RTC_CNT_bm)
RTC.CMP += rtc_config[1].val | rtc_config[2].val << 8;
if (flag & RTC_OVF_bm)
clock += 1;
return;
}
#endif
#define NOINLINE_TICKS
#ifdef NOINLINE_TICKS
#undef time
#undef second_tick
#undef hour_tick
#undef day_tick
uint8_t time() { return timei(); }
uint8_t second_tick() { return second_ticki(); }
uint8_t hour_tick() { return hour_ticki(); }
uint8_t day_tick() { return day_ticki(); }
#endif

View file

@ -3,7 +3,53 @@
//
#include <stdint.h>
#include <avr/interrupt.h>
volatile extern uint32_t clock;
volatile extern uint8_t clock_tick;
volatile extern uint16_t clock;
volatile extern uint8_t pit_tick;
volatile extern uint8_t rtc_tick;
void init_rtc(uint8_t p);
static inline
uint32_t timei()
{
cli();
uint16_t c = RTC.CNT;
sei();
return c | (uint32_t)clock << 16;
}
static inline
uint8_t second_ticki()
{
cli();
uint8_t c = pit_tick;
pit_tick = 0;
sei();
return c;
}
static inline
uint8_t hour_ticki()
{
cli();
uint8_t c = rtc_tick;
rtc_tick = c & ~2;
sei();
return c & 2;
}
static inline
uint8_t day_ticki()
{
cli();
uint8_t c = rtc_tick;
rtc_tick = c & ~1;
sei();
return c & 1;
}
#define time timei
#define second_tick second_ticki
#define hour_tick hour_ticki
#define day_tick day_ticki

154
src/spi.c
View file

@ -2,67 +2,135 @@
// spi.c
//
// !!! int = int8_t
// ! int = int8_t
#include "spi.h"
#include "bate.h"
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <string.h>
volatile uint8_t spi_tick;
struct spi_job spi;
// The Pressure sensor samples on the rising edge, MODE = 0
static const uint8_t SPI_Mode_Write = SPI_SSD_bm | SPI_BUFEN_bm;
#ifdef NO_EE_CONFIG
// The Pressure sensor delivers on the rising edge, MODE = 1
static const uint8_t SPI_Mode_Read = SPI_SSD_bm | SPI_BUFEN_bm | SPI_MODE_0_bm;
void init_spi(uint8_t div)
void init_spi(uint8_t spi_div)
{
if (div & ~SPI_PRESC_gm)
div = SPI_PRESC_DIV64_gc;
SPI.CTRLB = SPI_Mode_Write;
SPI.CTRLA = SPI_MASTER_bm | SPI_ENABLE_bm | div;
SSEL_PORT.OUT |= 1 << SSEL_PIN;
SSEL_PORT.DIR |= 1 << SSEL_PIN;
SPI.CTRLB = SPI_SSD_bm | SPI_BUFEN_bm; // Mode 0
SPI.CTRLA = SPI_MASTER_bm | SPI_ENABLE_bm | spi_div;
SPI.DATA;
SPI.DATA;
SPI.INTFLAGS = 0xff;
SPI.INTCTRL = SPI_TXCIE_bm;
SPI.INTCTRL = 0;
}
uint16_t spi_frame(uint16_t d)
#else
struct_ioconf(spi_config) = {
conf_prefix(SPI),
conf_io(SPI.CTRLB, SPI_SSD_bm | SPI_BUFEN_bm),
conf_io(SPI.CTRLA, SPI_MASTER_bm | SPI_ENABLE_bm | SPI_SPEED),
};
#endif
ISR(SPI0_INT_vect)
{
if (d)
SPI.CTRLB = SPI_Mode_Write;
uint8_t ifg = SPI.INTFLAGS;
while (ifg & SPI_DREIF_bm) {
if (spi.csize) {
SPI.DATA = *spi.cmd++;
spi.csize--;
}
else if (spi.zsize) {
SPI.DATA = spi.zero;
spi.zsize--;
}
else if (spi.wsize) {
if (spi.wdata)
SPI.DATA = *spi.wdata++;
else
SPI.CTRLB = SPI_Mode_Read;
SPI.DATA;
SPI.DATA;
SPI.INTFLAGS = 0xff;
spi_tick = 0;
SPI.DATA = d >> 8;
SPI.DATA = d;
uint8_t timeout = 20; // 20×15µs = 300µs
sei();
while (!spi_tick) {
sleep_cpu();
if (!--timeout) {
DEBUG_COUNTER(spi_timeout);
SPI.DATA = spi.zero;
}
else {
if (ifg & SPI_TXCIF_bm) {
if (!(spi.mode & SPI_CONT))
SSEL_PORT.OUT |= 1 << SSEL_PIN;
spi.status |= SPI_IDLE;
SPI.INTCTRL = SPI_RXCIF_bm;
}
else {
SPI.INTCTRL = SPI_TXCIF_bm | SPI_RXCIF_bm;
}
break;
}
ifg = SPI.INTFLAGS;
}
while (ifg & SPI_RXCIF_bm) {
uint8_t d = SPI.DATA; // clears the IF flag
if (spi.isize)
spi.isize --;
else {
if (spi.mask) {
if ((d & spi.mask) == spi.wait) {
spi.zsize++;
SPI.INTCTRL = SPI_DREIF_bm | SPI_RXCIF_bm;
continue;
}
spi.mask = 0;
}
if (spi.rsize) {
*spi.rdata++ = d;
spi.rsize--;
}
else if (!spi.mask) {
SPI.INTCTRL = SPI_DREIF_bm;
}
}
ifg = SPI.INTFLAGS;
}
uint16_t b = SPI.DATA;
return (b<<8) | SPI.DATA;
}
ISR(SPI0_INT_vect, ISR_NAKED)
uint8_t spi_select(uint8_t mode)
{
__asm__ ("push r24" "\n\t"
"lds r24, %[flag]" "\n\t"
"sts %[flag], r24" "\n\t"
"sts spi_tick, r24" "\n\t"
"pop r24" "\n\t"
"reti" "\n"
: : [flag] "n" (&SPI.INTFLAGS)
);
uint8_t s = spi_busy_p();
if (s)
return s;
memset(&spi, 0, sizeof(spi));
spi.mode = mode;
if (mode & SPI_FLASH)
SSEL_PORT.OUT &=~ (1 << SSEL_PIN);
else
SSEL_PORT.OUT |= 1 << SSEL_PIN;
if (mode & SPI_CONFIG)
SPI.CTRLA |= SPI_DORD_bm;
else
SPI.CTRLA &=~ SPI_DORD_bm;
return 0;
}
void spi_start_cmd(uint8_t csize, uint8_t *cmd)
{
spi.cmd = cmd;
barrier();
spi.csize = csize;
spi_start();
}
void spi_start_write(uint8_t csize, uint8_t *cmd, uint8_t wsize, uint8_t *wdata)
{
spi.wdata = wdata;
barrier();
spi.wsize = wsize;
spi_start_cmd(csize, cmd);
}
void spi_start_read(uint8_t csize, uint8_t *cmd, uint8_t rsize, uint8_t *rdata)
{
spi.rdata = rdata;
spi.isize = csize + spi.zsize;
barrier();
spi.wsize = rsize; // more zeros
spi.rsize = rsize;
spi_start_cmd(csize, cmd);
}

View file

@ -2,12 +2,69 @@
// spi.h
//
#include <stdint.h>
#include <avr/io.h>
#ifndef _SPI_H
#define _SPI_H
#include "config.h"
#include <avr/interrupt.h>
#define SPI SPI0
#define SPI_SPEED (SPI_CLK2X_bm | SPI_PRESC_DIV4_gc)
extern volatile uint8_t spi_tick;
uint16_t spi_frame(uint16_t d);
void init_spi(uint8_t div);
static inline void spi_off() { SPI.INTCTRL = SPI.CTRLA = 0; }
extern
struct spi_job {
uint8_t mode;
uint8_t status;
uint8_t csize;
uint8_t zsize;
uint8_t isize;
uint8_t rsize;
uint8_t wsize;
uint8_t zero;
uint8_t wait;
uint8_t mask;
const uint8_t *cmd;
const uint8_t *wdata;
uint8_t *rdata;
} spi;
enum spi_mode_bits {
SPI_FLASH = 0x01,
SPI_CONFIG = 0x02,
SPI_CONT = 0x80,
};
enum spi_status_bits {
SPI_IDLE = 0x01,
};
static inline
uint8_t spi_abort()
{
cli();
uint8_t f = SPI.INTFLAGS;
SPI.INTFLAGS = 0;
sei();
return f;
}
static inline
uint8_t spi_busy_p()
{
return SPI.INTFLAGS;
}
static inline
void spi_start()
{
SPI.INTCTRL = SPI_DREIF_bm | SPI_RXCIF_bm;
}
static inline void barrier() { __asm__("":::"memory"); }
void init_spi(uint8_t spi_div);
uint8_t spi_select(uint8_t mode);
void spi_start_cmd(uint8_t csize, uint8_t *cmd);
void spi_start_write(uint8_t csize, uint8_t *cmd, uint8_t wsize, uint8_t *wdata);
void spi_start_read(uint8_t csize, uint8_t *cmd, uint8_t rsize, uint8_t *rdata);
#endif

View file

@ -4,99 +4,41 @@
// !!! int = int8_t
#include "bate.h"
#include "mul.h"
#include <avr/io.h>
#include "uart.h"
#include <avr/sleep.h>
#include <avr/interrupt.h>
#define Bit(x) (1<<(x))
// 10 MHz / 2400 / 16 * 64
#define UART_DIV 16667
__attribute__ ((noinline, noclone))
void init_uart(uint16_t div, uint8_t mode)
{
if (mode==0xff)
mode = USART_TXEN_bm;
mode |= USART_TXEN_bm;
if (div<64)
div = UART_DIV;
USART0.BAUD = div;
PORTB.DIRSET = Bit(2);
USART0.CTRLB = mode;
if (mode & USART_RXEN_bm)
USART0.CTRLA = USART_RXCIE_bm;
// `BOTHEDGES` should wake from power down sleep()
PORTB.PIN3CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
}
volatile uint8_t rx_tick;
ISR(PORTB_PORT_vect, ISR_NAKED)
{
__asm__ ("push r24" "\n\t"
"lds r24, %[stat]" "\n\t"
"sts %[stat], r24" "\n\t"
"sts rx_tick, r24" "\n\t"
"pop r24" "\n\t"
"reti" "\n"
: : [stat] "n" (&VPORTB.INTFLAGS)
);
}
__attribute__ ((noinline, noclone))
uint8_t uart_tick()
{
cli();
uint8_t r = rx_tick;
rx_tick = 0;
sei();
return r;
}
struct_ioconf(uart_config) = {
conf_prefix(USART0),
conf_iow(USART0.BAUD, UART_BAUD),
conf_io(USART0.CTRLB, UART_MODE & 0xff),
conf_io(USART0.CTRLC, UART_MODE >> 8),
conf_io(USART0.CTRLA,USART_RXCIE_bm),
};
// `uart_tx` buffer size must be a power of 2, max 256.
// Fix `tx()` and `put_char()` / `put_char:`
// when the size changes.
// For now, we can afford half of the available RAM,
// and still have 128 bytes for the stack
#ifdef UART_TX_SMALL
uint8_t uart_tx[128];
#else
uint8_t uart_tx[256];
#endif
uint8_t uart_tx[32];
#define uart_tx_m (sizeof(uart_tx) - 1)
volatile uint8_t uart_tx_w;
volatile uint8_t uart_tx_r;
volatile uint8_t uart_tx_busy;
uint8_t uart_cks;
#if 0
__attribute__ ((noinline, noclone))
void tx()
{
// interrupts must be disabled
if (USART0.STATUS & USART_TXCIF_bm) {
USART0.STATUS = USART_TXCIF_bm;
uart_tx_busy = 0;
}
// read volatile memory once, for speed
uint8_t r = uart_tx_r;
uint8_t w = uart_tx_w;
while (w - r) {
uart_tx_busy = w - r;
if (!(USART0.STATUS & USART_DREIF_bm)) {
USART0.CTRLA |= USART_DREIE_bm | USART_TXCIE_bm;
USART0.CTRLA |= USART_DREIE_bm;
uart_tx_r = r;
return;
}
USART0.TXDATAL = uart_tx[r++ & uart_tx_m];
// race? TXC while we were looping? Drop it.
USART0.STATUS = USART_TXCIF_bm;
USART0.STATUS |= USART_TXCIF_bm
}
uart_tx_r = r;
USART0.CTRLA &=~ USART_DREIE_bm;
@ -104,7 +46,6 @@ void tx()
ISR(USART0_DRE_vect)
{
DEBUG_COUNTER(tx_irqs);
tx();
}
@ -113,57 +54,43 @@ ISR(USART0_DRE_vect)
__attribute__ ((noinline, noclone))
void tx()
{
// This uses only six registers, to save stack in the ISR.
// r0 and r1 need not be saved
// Avoids unlikely branches.
// [STATUS] is prepared in advance, to avoid two branches and one memory load,
// at the cost of one register on the ISR stack (r25)
// This uses only three registers, to save stack in the ISR.
__asm__("\n"
" lds r21, uart_tx_r \n"
" lds r19, uart_tx_w \n"
" lds r25, %[CTRLA] \n"
" andi r25, ~0x20 ; clr DREIE \n"
" lds r30, %[STATUS] \n"
" ldi r24, 0x40 \n"
" and r30, r24 ; TXCIF \n"
" breq 2f \n"
" sts %[STATUS], r24 ; clr TXCIF \n"
" ldi r31, 0 \n"
" sts uart_tx_busy, r31 \n"
" andi r25, ~(1<<%[DRE]) \n"
" rjmp 2f \n"
" \n"
"1: \n"
" mov r30, r21 ; \n"
" subi r21, 0xff ; r++ & uart_tx_m \n"
#ifdef UART_TX_SMALL
" andi r30, 0x7f ; \n"
#endif
" ldi r31, 0 \n"
" mov r31, r30 ; \n"
" subi r31, 0xff ; r++ & uart_tx_m \n"
" sts uart_tx_r, r31 \n"
" andi r30, 0x1f ; \n"
" clr r31 \n"
" subi r30, lo8(-(uart_tx)) \n"
" sbci r31, hi8(-(uart_tx)) \n"
" ld r24, Z \n"
" sts %[TXDATA], r24 \n"
" ldi r24, 0x40 \n"
" sts %[STATUS], r24 ; clr TXCIF \n"
" ld r30, Z \n"
" sts %[TXDATA], r30 \n"
" ldi r30, 1<<%[TXC] \n"
" sts %[STATUS], r30 \n"
"2: \n"
" cp r21, r19 \n"
" lds r30, uart_tx_r \n"
" lds r31, uart_tx_w \n"
" cp r30, r31 \n"
" breq 3f \n"
" sts uart_tx_busy, r24 ; =0x40 \n"
" lds r24, %[STATUS] \n"
" sbrc r24, 5 ; DREIF \n"
" lds r30, %[STATUS] \n"
" sbrc r30, %[DRE] \n"
" rjmp 1b \n"
" ori r25, 0x60 ; set DREIE TXCIE \n"
" ori r25, 1<<%[DRE] \n"
"3: \n"
" sts %[CTRLA], r25 \n"
" sts uart_tx_r, r21 \n"
:
: [STATUS] "n" (&USART0.STATUS),
:
[TXC] "n" (USART_TXCIF_bp),
[DRE] "n" (USART_DREIF_bp),
[CTRLA] "n" (&USART0.CTRLA),
[STATUS] "n" (&USART0.STATUS),
[TXDATA] "n" (&USART0.TXDATAL)
: "r19", "r21",
"r24", "r25",
"r30", "r31",
"memory"
: "r25", "r30", "r31", "memory"
);
}
@ -174,30 +101,17 @@ ISR(USART0_DRE_vect, ISR_NAKED)
// OTOH, the C implementation of tx() is not bad either,
// if we are doing this asm, then we do it agressively.
__asm__("\n"
" push r24 \n"
" in r24, __SREG__ \n"
" push r24 \n"
" push r25 \n"
" push r19 \n"
" push r21 \n"
" in r25, __SREG__ \n"
" push r25 \n"
" push r30 \n"
" push r31 \n"
" \n"
#ifdef DEBUG
" lds r24, debug_data + 3 \n"
" subi r24, 0xff \n"
" sts debug_data + 3, r24 \n"
#endif
" rcall tx \n"
" \n"
" pop r31 \n"
" pop r30 \n"
" pop r21 \n"
" pop r19 \n"
" pop r25 \n"
" pop r24 \n"
" out __SREG__, r24 \n"
" pop r24 \n"
" out __SREG__, r25 \n"
" pop r25 \n"
" reti \n"
);
}
@ -206,26 +120,28 @@ ISR(USART0_DRE_vect, ISR_NAKED)
ISR(USART0_TXC_vect, ISR_ALIASOF(USART0_DRE_vect));
uint8_t uart_rx[16];
uint8_t uart_rx[32];
#define uart_rx_m (sizeof(uart_rx) - 1)
volatile uint8_t uart_rx_w;
volatile uint8_t uart_rx_s;
volatile uint8_t uart_rx_mes;
uint8_t uart_rx_err;
uint8_t uart_rx_errors;
#if 0
ISR(USART0_RXC_vect)
{
DEBUG_COUNTER(rx_irqs);
// s/while/if/ !
if (USART0.STATUS & USART_RXCIF_bm) {
uint8_t c = USART0.RXDATAL;
DEBUG_POKE(rx_char, c);
uart_rx_s |= USART0.RXDATAH;
uint8_t w = uart_rx_w;
uart_rx[w] = c;
if (w < uart_rx_m)
uart_rx_w = ++w;
if (w<=uart_rx_m) {
uint8_t c = USART0.RXDATAL;
uart_rx[w++] = c;
if (!uart_rx_mes && c=='\n')
uart_rx_mes = w;
uart_rx_w = w;
}
}
}
#else
@ -246,23 +162,19 @@ ISR(USART0_RXC_vect, ISR_NAKED)
" push r24 \n"
" push r30 \n"
" push r31 \n"
#ifdef DEBUG
" lds r24, debug_data + 4 \n"
" subi r24, -1 \n"
" sts debug_data + 4, r24 \n"
#endif
" lds r24, %[RXDATA] \n"
" lds r24, %[DH] \n"
" lds r30, uart_rx_s \n"
" and r24, r30 \n"
" sts uart_rx_s, r24 \n"
" lds r24, %[DL] \n"
" lds r30, uart_rx_w \n"
" cpi r30, 0x20 \n"
" brcc 1f \n"
" ldi r31, 0 \n"
" subi r30, lo8(-(uart_rx)) \n"
" sbci r31, hi8(-(uart_rx)) \n"
" st Z+, r24 \n"
#ifdef DEBUG
" sts debug_data + 5, r24 \n"
#endif
" subi r30, lo8(uart_rx) \n"
" sbrc r30, 4 ; log2(sizeof(uart_rx)) \n"
" subi r30, 1 \n"
" sts uart_rx_w, r30 \n"
" cpi r24, '\n' \n"
" brne 1f \n"
@ -277,145 +189,7 @@ ISR(USART0_RXC_vect, ISR_NAKED)
" out __SREG__, r24 \n"
" pop r24 \n"
" reti \n"
: : [RXDATA] "n" (&USART0.RXDATAL)
:: [DL] "n" (&USART0.RXDATAL), [DH] "n" (&USART0.RXDATAH)
);
}
#endif
#if 0
// These are implemented in `uart_tx.S`
__attribute__ ((noinline, noclone))
uint8_t uart_put_char(uint8_t c)
{
uint8_t r = uart_tx_r;
uint8_t w = uart_tx_w;
uint8_t ww = w + 1;
if ((ww & uart_tx_m) == (r & uart_tx_m))
return 1;
uart_tx[w & uart_tx_m] = c;
__asm__("" ::: "memory");
uart_tx_w = ww;
__asm__("" ::: "memory");
return 0;
}
__attribute__ ((noinline, noclone))
void send_char(uint8_t c)
{
uint8_t s = 0;
uint8_t b;
while (1) {
b = uart_put_char(c);
uart_busy();
if (!b)
break;
sleep_cpu();
s = 1;
}
if (s)
DEBUG_COUNTER(tx_sleep);
}
__attribute__ ((noinline, noclone))
void send_str(const char *s)
{
while (*s) {
uint8_t c = *s++;
if (!uart_put_char(c))
continue;
send_char(c);
}
uart_busy();
}
static inline
void rx_dismiss(uint8_t n)
{
cli();
uint8_t i = 0;
uint8_t m = 0;
uint8_t w = uart_rx_w;
if (w != uart_rx_m)
while (n < w) {
uint8_t c = uart_rx[n++];
uart_rx[i++] = c;
if (!m && c=='\n')
m = i;
}
uart_rx_mes = m;
uart_rx_w = i;
sei();
}
__attribute__ ((noinline, noclone))
uint8_t uart_busy()
{
cli();
tx();
sei();
return uart_tx_busy;
}
static __attribute__ ((noinline, noclone))
void send_hex_nibble(uint8_t b)
{
b += '0';
if (b>'9')
b += '@' - '9';
send_char(b);
}
__attribute__ ((noinline, noclone))
void send_hex_byte(uint8_t b)
{
send_hex_nibble(b >> 4);
send_hex_nibble(b & 0xf);
}
__attribute__ ((noinline, noclone))
void send_hex_word(uint16_t b)
{
send_hex_byte(b >> 8);
send_hex_byte(b);
}
__attribute__ ((noinline, noclone))
void send_hex(uint8_t header, const uint8_t *s, uint8_t n, uint8_t words)
{
// dump little endian words.
send_char(header);
while (n--) {
if (words & 1)
send_char(' ');
uint8_t l = *(s++);
if (words & 2 && n & 1) {
send_hex_byte(*(s++));
n--;
}
send_hex_byte(l);
}
send_char('\n');
}
void command(void)
{
uint8_t m = uart_rx_mes;
if (!m)
return;
uint8_t *s = uart_rx;
uint8_t i = 0;
uint8_t c;
send_str("R ");
while (i++ < m) {
c = *s++;
send_char(c);
}
if (c != '\n')
send_char('\n');
else
parse_command(uart_rx, m);
rx_dismiss(m);
}
#endif

View file

@ -2,32 +2,23 @@
// uart.h
//
#include <stdint.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include "config.h"
void init_uart(uint16_t div, uint8_t mode);
uint8_t uart_tick();
#define UART_MODE 0x03c0
#define UART_BAUD 5555 // 115200 baud
static inline
uint8_t uart_break_p()
{
return !(VPORTB.IN & 0x08);
}
void init_uart(uint16_t mode, uint16_t div);
void send_char(uint8_t c);
void send_eol();
void send_str(const char *s);
void send_hex_byte(uint8_t b);
void send_hex_byte_eol(uint8_t b);
void send_hex_word(uint16_t b);
void send_hex(uint8_t header, const uint8_t *s, uint8_t n, uint8_t words);
void send_hex(const void *s, uint8_t n);
uint8_t uart_busy();
void command(void);
void send_decimal(uint16_t b, uint8_t dec);
void parse_command(uint8_t *s, uint8_t n);
void parse_command(const uint8_t *s, uint8_t n);
static inline
void send_hex_long(uint32_t b)
@ -36,5 +27,5 @@ void send_hex_long(uint32_t b)
send_hex_word(b);
}
extern uint8_t uart_cks;
void send_cks();
extern uint8_t uart_rx_err;
extern uint8_t uart_rx_errors;

View file

@ -3,17 +3,18 @@
//
// avoid quite a few push and pops and jumps
#include <avr/io.h>
.global send_hex_word
.global send_hex_byte
.global send_hex_byte_eol
.global send_char
.global send_str
.global uart_busy
.global send_hex
.global send_cks
.global send_eol
.global command
// `tx()` and `put_char()` do not gobble r18, r20, r22, r23, r26, and r27.
// `tx()` and `put_char()` do not gobble r18, r20, r21, r22, r24, r26, and r27.
put_char:
// non-global, non-C
@ -23,60 +24,64 @@ put_char:
ldi r19, 1
add r19, r30
eor r23, r19
#ifdef UART_TX_SMALL
andi r23, 0x7f // uart_tx_m
breq 1f
andi r30, 0x7f // uart_tx_m
#else
breq 1f
#endif
andi r30, 0x1f // uart_tx_m
ldi r31, 0
subi r30, lo8(-(uart_tx))
sbci r31, hi8(-(uart_tx))
andi r23, 0x1f // uart_tx_m
breq 1f
st Z, r22
sts uart_tx_w, r19
lds r19, uart_cks
add r19, r22
sts uart_cks, r19
1:
// r22 preserved
// r23 full when zero
ret
// r18, r20, r26, and 27 must be preseved in the hex functions
// r22 and r23 must be preserved in `uart_busy()` and `put_char()'
// r18, r26, and 27 must be preseved in the hex functions
// , r20, r21, r24, must be preserved in `uart_busy()` and `put_char()'
send_hex_byte_eol:
ldi r22, ' '
rcall send_char22
rcall send_hex_byte
send_eol:
ldi r22, '\n'
rcall send_char22
uart_busy:
cli
rcall tx ; gobbles r25, r30, and r31
sei
lds r25, USART0_STATUS
andi r25, SPI_TXCIF_bm
9:
ret
send_hex_word:
push r24
mov r20, r24
mov r24, r25
rcall send_hex_byte
pop r24
mov r24, r20
send_hex_byte:
push r24
mov r21, r24
swap r24
rcall send_hex_nibble
pop r24
mov r24, r21
send_hex_nibble:
andi r24, 0x0f
subi r24, -'0'
cpi r24, '9'+1
brlo send_char
subi r24, '9'+1-'A'
send_char:
mov r22, r24
send_char22:
rcall put_char
rcall uart_busy
tst r23
brne 9f
rcall uart_busy
sleep
rjmp send_char22
2:
rcall put_char
tst r23
brne 1f
rcall send_char22
1:
movw r24, r26
@ -85,86 +90,53 @@ send_str:
ld r22, X+
tst r22
brne 2b
uart_busy:
cli
rcall tx ; gobbles only r19, r21, r24, r25, r30, and r31
sei
lds r24, uart_tx_busy
send_hex:
movw r26, r24
mov r18, r22
1:
ld r24, X+
rcall send_hex_byte
subi r18, 1
brcc 1b
9:
ret
; send_hex('Q', &uart_cks, 1, 0);
send_cks:
ldi r24, 'Q'
ldi r18, 0
ldi r20, 1
ldi r22, lo8(uart_cks)
ldi r23, hi8(uart_cks)
send_hex:
movw r26, r22
rcall send_char
rjmp 3f
1:
ldi r22, ' '
sbrc r18, 0
rcall send_char22
ld r24, X+
sbrs r18, 1
rjmp 2f
sbrs r20, 0
rjmp 2f
push r24
subi r20, 1
ld r24, X+
rcall send_hex_byte
pop r24
2:
rcall send_hex_byte
3:
subi r20, 1
brcc 1b
send_eol:
ldi r22, 10
rjmp send_char22
command:
lds r20, uart_rx_mes
tst r20
breq 9b
ldi r22, 'R'
rcall send_char22
ldi r22, '>'
rcall send_char22
ldi r26, lo8(uart_rx)
ldi r27, hi8(uart_rx)
mov r18, r20
1:
ld r22, X+
rcall send_char22
subi r18, 1
brne 1b
;; when any frame errors occured, dismiss the buffer
lds r22, uart_rx_s
andi r22, 0x46
brne uart_errs_p
;; when there is no message, check for overflow
lds r22, uart_rx_mes
tst r22
breq uart_full_p
;; when the buffer was dismissed for errors, skip this command
push r22
lds r24, uart_rx_err
sts uart_rx_err, r1
lds r25, uart_rx_errors
or r25, r24
sts uart_rx_errors, r25
tst r24
brne 2f
ldi r24, lo8(uart_rx)
ldi r25, hi8(uart_rx)
mov r22, r20
push r20
rcall parse_command
2:
pop r24
rx_dismiss:
cli
lds r18, uart_rx_w
clr r19
clr r20
cpi r18, 15
brcc 3f
sub r18, r24
breq 3f
brcs 3f // TCNH, n > w
brcs 3f
ldi r26, lo8(uart_rx)
ldi r27, hi8(uart_rx)
movw r30, r26
add r30, r24
adc r31, r1
adc r31, r20
1:
ld r25, Z+
st X+, r25
@ -175,7 +147,7 @@ rx_dismiss:
brne 2f
mov r20, r19
2:
cp r19, r18
subi r18, 1
brne 1b
3:
sts uart_rx_mes, r20
@ -183,3 +155,15 @@ rx_dismiss:
sei
9:
ret
uart_full_p:
lds r22, uart_rx_w
andi r22, 0x20
breq 9b
uart_errs_p:
lds r24, uart_rx_err
or r24, r22
rx_dismiss_buffer:
sts uart_rx_err, r24
lds r24, uart_rx_w
rjmp rx_dismiss