mirror of
https://codeberg.org/SiB64/turbo_weather.git
synced 2026-05-01 15:14:22 +02:00
Compare commits
2 commits
429a0bf530
...
46809f6fac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46809f6fac | ||
|
|
bf73d2b586 |
32 changed files with 1425 additions and 1338 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -22,3 +22,6 @@ src/mul
|
|||
*.eeprom
|
||||
*.elf
|
||||
src/data
|
||||
BCH-Codes/
|
||||
*.odt
|
||||
*.userrow
|
||||
|
|
|
|||
95
src/Makefile
95
src/Makefile
|
|
@ -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 $@ $<
|
||||
|
|
|
|||
204
src/adc.c
204
src/adc.c
|
|
@ -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,19 +101,17 @@ 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;
|
||||
start_conversion(c);
|
||||
return;
|
||||
if (c->mode) {
|
||||
start_conversion(c);
|
||||
adc_current = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
stop:
|
||||
ADC.CTRLA = 0;
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
43
src/base85.c
Normal 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
4
src/base85.h
Normal 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
148
src/base85a.S
Normal 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
93
src/bch4369.c
Normal 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
6
src/bch4369.h
Normal 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); }
|
||||
98
src/calib.c
98
src/calib.c
|
|
@ -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
|
||||
};
|
||||
29
src/calib.h
29
src/calib.h
|
|
@ -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];
|
||||
305
src/cmd.c
305
src/cmd.c
|
|
@ -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++;
|
||||
}
|
||||
if (flag == '+')
|
||||
start_adc();
|
||||
}
|
||||
}
|
||||
else if (cmd == 'T' && p==1) {
|
||||
immediate += pp[0];
|
||||
pp[1] = immediate;
|
||||
if (cmd == 'W') {
|
||||
s = base85_fill_buffer(s);
|
||||
r = base85_error;
|
||||
}
|
||||
else if (cmd == 'M' && p==2) {
|
||||
pp[2] = *a;
|
||||
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;
|
||||
}
|
||||
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 == 'W' && p==3) {
|
||||
pp[3] = *a;
|
||||
*a = pp[2];
|
||||
}
|
||||
else if (cmd == 'K' && p==4) {
|
||||
cli();
|
||||
pp[4] = clock_tick;
|
||||
clock = *(uint32_t*)pp;
|
||||
sei();
|
||||
}
|
||||
else if (cmd == 'D' && p==1) {
|
||||
pp[1] = test_calib;
|
||||
test_calib = pp[0];
|
||||
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
8
src/cmd.h
Normal 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
57
src/config.c
Normal 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
43
src/config.h
Normal 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
62
src/dose.c
Normal 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
25
src/dose.h
Normal 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;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
|
||||
#define ADC_CONF_ADDR __attribute__((__section__(".eeprom")))
|
||||
#define TESTDATA_ADDR __attribute__((__section__(".eeprom")))
|
||||
|
|
@ -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
212
src/flash.c
Normal 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
24
src/flash.h
Normal 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
284
src/mul.c
|
|
@ -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
|
||||
15
src/mul.h
15
src/mul.h
|
|
@ -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
30
src/pwm.c
Normal 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
32
src/pwm.h
Normal 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);
|
||||
}
|
||||
144
src/rtc.c
144
src/rtc.c
|
|
@ -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"
|
||||
"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"
|
||||
"reti" "\n"
|
||||
:[tick] "+m" (clock_tick),
|
||||
[clock] "+m" (clock)
|
||||
:[flag] "n" (&RTC.PITINTFLAGS)
|
||||
__asm__ (
|
||||
"push r24" "\n\t"
|
||||
"ldi r24, 1" "\n\t"
|
||||
"sts %[flag], r24" "\n\t"
|
||||
"sts pit_tick, r24" "\n\t"
|
||||
"ldi r24, " "\n\t"
|
||||
"sts %[flag], r24" "\n\t"
|
||||
"reti" "\n"
|
||||
::[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
|
||||
|
|
|
|||
50
src/rtc.h
50
src/rtc.h
|
|
@ -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
|
||||
|
|
|
|||
156
src/spi.c
156
src/spi.c
|
|
@ -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;
|
||||
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);
|
||||
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.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);
|
||||
}
|
||||
|
|
|
|||
69
src/spi.h
69
src/spi.h
|
|
@ -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
|
||||
|
|
|
|||
344
src/uart.c
344
src/uart.c
|
|
@ -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),
|
||||
[CTRLA] "n" (&USART0.CTRLA),
|
||||
[TXDATA] "n" (&USART0.TXDATAL)
|
||||
: "r19", "r21",
|
||||
"r24", "r25",
|
||||
"r30", "r31",
|
||||
"memory"
|
||||
:
|
||||
[TXC] "n" (USART_TXCIF_bp),
|
||||
[DRE] "n" (USART_DREIF_bp),
|
||||
[CTRLA] "n" (&USART0.CTRLA),
|
||||
[STATUS] "n" (&USART0.STATUS),
|
||||
[TXDATA] "n" (&USART0.TXDATAL)
|
||||
: "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 (!uart_rx_mes && c=='\n')
|
||||
uart_rx_mes = 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
|
||||
|
|
|
|||
27
src/uart.h
27
src/uart.h
|
|
@ -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;
|
||||
|
|
|
|||
158
src/uart_tx.S
158
src/uart_tx.S
|
|
@ -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
|
||||
brne 9f
|
||||
rcall uart_busy
|
||||
tst r23
|
||||
brne 9f
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue