Compare commits

..

6 commits

Author SHA1 Message Date
Stephan I. Böttcher
fd9957aee1 adc: fix and call send_calib_adc() 2024-04-12 18:58:12 +02:00
Stephan I. Böttcher
8d38419263 adc: fix saddus() 2024-04-12 18:34:23 +02:00
Stephan I. Böttcher
ad4effa467 uart: rework uart_busy
- implement `tx()` in asm, does not work yet!
- enable TXC interrupt, to be woken when the uart is done
  in order to power down.  Alias the DRE interrupt.
- Rework `send_char()` and `send_str()`.
- Remove …_nosleep() and `panik()`.
- Better account for volatile-ness of buffer pointers.
2024-04-12 16:41:46 +02:00
Stephan I. Böttcher
410915515e bate: print eeprom in words 2024-04-12 16:40:57 +02:00
Stephan I. Böttcher
1eecc696da adc: calibrated output
- add calib values to `adc_conf`,
- double the size of `struct adc_conf`
- reduce the sape for `testdata`.
- implement the calibration with offset and scale.
2024-04-12 02:08:31 +02:00
Stephan I. Böttcher
c452e5bc61 eeprom: forget about specifying the address or vars 2024-04-12 02:07:04 +02:00
9 changed files with 309 additions and 84 deletions

View file

@ -119,7 +119,7 @@ fuse: $(PROJ).fuse$F
BC_MAGIC = 0xba
BC_VERS = 4
BC_TRIG = 0x08
BC_SEND = 0x1b
BC_SEND = 0x2b
BC_PWR = 0x04
BC_TEST = 5
BC_SPI = 0xff

129
src/adc.c
View file

@ -4,6 +4,7 @@
#include "adc.h"
#include "bate.h"
#include "mul.h"
#include "eeprom.h"
#include <avr/interrupt.h>
@ -23,32 +24,54 @@ struct adc_conf adc_conf[N_ADC] ADC_CONF_ADDR = {
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | ADC_MUXPOS_TEMPSENSE_gc,
.calib = 80 * 256,
.shifts = 1,
},
{ // 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
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | V_BAT,
.calib = 1024 * 11,
.shifts = 3, // V [×11]
},
{ // NTC biased by V_RF
.mode = MODE_DIFF,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | V_NTC,
.inn = INP | V_RFP,
.calib = 2048 * 10,
.shifts = 1, // mV
},
{ // Batterie Voltage / 11
{ // NTC biased by V_RF
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | V_BAT,
.ref = REF | ADC_REFSEL_2500MV_gc,
.inp = INP | V_NTC,
.calib = 2500 * 10,
.shifts = 1,
},
{ // Transmitter power
{ // NTC biased by V_RF
.mode = MODE,
.ref = REF | ADC_REFSEL_VDD_gc,
.inp = INP | V_RFP,
.inp = INP | V_NTC,
},
{ // Transmitter power
.mode = MODE,
.ref = REF | ADC_REFSEL_2500MV_gc,
.inp = INP | V_RFP,
.calib = 2500 * 10,
.shifts = 1,
},
{ // Transmitter power
.mode = MODE,
.ref = REF | ADC_REFSEL_VDD_gc,
.inp = INP | V_RFP,
},
};
@ -143,6 +166,7 @@ ISR(ADC0_RESRDY_vect, ISR_NAKED)
"ldi r31, 0" "\n\t"
"lsl r30" "\n\t"
"lsl r30" "\n\t"
"lsl r30" "\n\t"
"subi r30, lo8(-(adc_conf))" "\n\t"
"sbci r31, hi8(-(adc_conf))" "\n"
"1:" "\n\t"
@ -183,3 +207,98 @@ 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,9 +10,12 @@ struct adc_conf {
uint8_t ref;
uint8_t inp;
uint8_t inn;
int8_t offset;
uint8_t shifts;
uint16_t calib;
};
#define N_ADC (32/sizeof(struct adc_conf))
#define N_ADC (64/sizeof(struct adc_conf))
extern uint16_t adc_readings[N_ADC];
extern uint8_t adc_current;
@ -21,3 +24,4 @@ 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);

View file

@ -438,8 +438,16 @@ int main()
if (adc_busy())
continue;
adc_current = 0xff;
if (config.send & SEND_ADC_HEX)
send_hex('A', (uint8_t *)adc_readings,
2*n_adc, 3);
if (config.send & SEND_ADC_VOLT) {
send_char('V');
for (uint8_t i=0; i<n_adc; i++)
send_calib_adc(i);
send_char('\n');
}
}
if (uart_busy()) {
@ -520,14 +528,17 @@ int main()
send_char('\n');
}
uint8_t send_test = 0;
const union bate *b = &bate;
if (test_calib)
b = testdata + test_calib;
if (config.send & SEND_BATEW || send_config || test_calib)
if (test_calib) {
b = testdata + -- test_calib;
send_test = 1;
}
if (config.send & SEND_BATEW || send_config)
send_hex('W', b->b, 8, 3);
if (config.send & SEND_BATED || test_calib)
if (config.send & SEND_BATED || send_test)
send_hex('D', b->b+8, 4, 3);
if (config.send & SEND_CALIB || test_calib) {
if (config.send & SEND_CALIB || send_test) {
bate_calib(b, &pressure);
send_str("P ");
send_str(decimal_str(pressure.p, 1));
@ -535,8 +546,6 @@ int main()
send_str(decimal_str(pressure.T-2732, 1));
send_str(" °C\n");
}
if (test_calib)
test_calib--;
if (send_config) {
send_config = 0;
@ -544,7 +553,7 @@ int main()
send_hex('F', (uint8_t *)&FUSE, sizeof(FUSE_t), 1);
send_hex('U', (uint8_t *)&USERROW, sizeof(USERROW_t), 1);
send_hex('C', (uint8_t *)&config, sizeof(struct config), 1);
send_hex('E', (uint8_t *)adc_conf, sizeof(adc_conf), 1);
send_hex('E', (uint8_t *)adc_conf, sizeof(adc_conf), 3);
}
if (config.send & SEND_DEBUG) {

View file

@ -44,9 +44,10 @@ enum send_flags {
SEND_BATED = 0x02,
SEND_BATEW = 0x04,
SEND_CLOCK = 0x08,
SEND_ADC = 0x10,
SEND_CALIB = 0x20,
SEND_VOLT = 0x40,
SEND_CALIB = 0x10,
SEND_ADC_HEX = 0x20,
SEND_ADC_VOLT = 0x40,
SEND_ADC = 0x60,
SEND_DEBUG = 0x80,
};
enum trigger_flags {

View file

@ -24,6 +24,6 @@ struct pressure {
uint16_t p;
};
#define N_TESTDATA (96/sizeof(union bate))
#define N_TESTDATA (64/sizeof(union bate))
void bate_calib(const union bate *bate, struct pressure *pt);
extern const union bate testdata[N_TESTDATA];

View file

@ -1,4 +1,3 @@
#define EEPROM(a) __attribute__((__section__(".eeprom")))
#define ADC_CONF_ADDR EEPROM(0x1400)
#define TESTDATA_ADDR EEPROM(0x1420)
#define ADC_CONF_ADDR __attribute__((__section__(".eeprom")))
#define TESTDATA_ADDR __attribute__((__section__(".eeprom")))

View file

@ -7,8 +7,9 @@
#include "bate.h"
#include "mul.h"
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#define Bit(x) (1<<(x))
@ -33,13 +34,12 @@ volatile uint8_t rx_tick;
ISR(PORTB_PORT_vect, ISR_NAKED)
{
__asm__ ("push r24" "\n\t"
"lds r24,%[stat]" "\n\t"
"sts %[tick], 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"
:[tick] "+m" (rx_tick)
:[stat] "n" (&VPORTB.INTFLAGS)
: : [stat] "n" (&VPORTB.INTFLAGS)
);
}
@ -56,23 +56,32 @@ uint8_t uart_tick()
uint8_t uart_tx[128];
volatile uint8_t uart_tx_w;
volatile uint8_t uart_tx_r;
volatile uint8_t uart_tx_b;
volatile uint8_t uart_tx_busy;
static const uint8_t uart_tx_m = sizeof(uart_tx) - 1;
static inline void tx()
#if 1
__attribute__ ((noinline, noclone))
void tx()
{
// interrupts must be disabled
uint8_t r = uart_tx_r;
while (r != uart_tx_w) {
if (!(USART0.STATUS & USART_DREIF_bm)) {
USART0.CTRLA |= USART_DREIE_bm;
if (USART0.STATUS & USART_TXCIF_bm) {
USART0.STATUS = USART_TXCIF_bm;
uart_tx_b = 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;
uart_tx_r = r;
return;
}
USART0.TXDATAL = uart_tx[r & uart_tx_m];
r++;
USART0.TXDATAL = uart_tx[r++ & uart_tx_m];
// race? TXC while we were looping? Drop it.
USART0.STATUS = USART_TXCIF_bm;
}
uart_tx_r = r;
USART0.CTRLA &=~ USART_DREIE_bm;
@ -84,54 +93,154 @@ ISR(USART0_DRE_vect)
tx();
}
#else
__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)
__asm__("\n"
" lds r18, 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"
" rjmp 2f \n"
" \n"
"1: \n"
" mov r30, r18 ; \n"
" subi r18, 0xff ; r++ & uart_tx_m \n"
" andi r30, 0x7f ; \n"
" ldi r31, 0 \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"
"2: \n"
" cp r18, r19 \n"
" breq 3f \n"
" sts uart_tx_busy, r24 ; =0x40 \n"
" lds r24, %[STATUS] \n"
" sbrc r24, 5 ; DREIF \n"
" rjmp 1b \n"
" ori r25, 0x60 ; set DREIE TXCIE \n"
"3: \n"
" sts %[CTRLA], r25 \n"
" sts uart_tx_r, r18 \n"
:
: [STATUS] "n" (&USART0.STATUS),
[CTRLA] "n" (&USART0.CTRLA),
[TXDATA] "n" (&USART0.TXDATAL)
: "r18", "r19",
"r24", "r25",
"r30", "r31",
"memory"
);
}
ISR(USART0_DRE_vect, ISR_NAKED)
{
// Doing this naked is a bit dangerous,
// but saves five instructions and two bytes stack (r0, r1).
// 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 r18 \n"
" push r19 \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 r19 \n"
" pop r18 \n"
" pop r25 \n"
" pop r24 \n"
" out __SREG__, r24 \n"
" pop r24 \n"
);
}
#endif
ISR(USART0_TXC_vect, ISR_ALIASOF(USART0_DRE_vect));
__attribute__ ((noinline, noclone))
uint8_t uart_busy()
{
// How to test if we can power down?
// `uart_tx_b` is set to `TXC` when a char is loaded for transmission
// and the TXCIF is cleared.
// When `uart_tx_b` is zero, we are good,
// else when `TXCIF` is set we are also good.
// When `uart_tx_b` is set but the `TXCIF` not yet, we are busy,
//returning TXC.
cli();
tx();
uint8_t b = uart_tx_b;
if (!b || USART0.STATUS & b)
b = uart_tx_b = 0;
sei();
return b;
return uart_tx_busy;
}
__attribute__ ((noinline, noclone))
uint8_t send_char_nosleep(uint8_t c)
static inline
uint8_t uart_put_char(uint8_t c)
{
uint8_t r = 0;
uint8_t ww = uart_tx_w + 1;
if ((ww & uart_tx_m) == (uart_tx_r & uart_tx_m))
goto full;
r = 1;
uart_tx[uart_tx_w & uart_tx_m] = 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");
full:
uart_busy();
return r;
return 0;
}
__attribute__ ((noinline, noclone))
void send_char(uint8_t c)
{
uint8_t s = 0;
while (!send_char_nosleep(c)) {
s = 1;
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();
}
uint8_t uart_rx[16];
volatile uint8_t uart_rx_w;
volatile uint8_t uart_rx_n;
@ -162,8 +271,9 @@ void rx_dismiss(uint8_t n)
cli();
uint8_t i = 0;
uart_rx_mes = 0;
if (uart_rx_w != uart_rx_m)
while (n < uart_rx_w) {
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 (c=='\n')

View file

@ -5,15 +5,13 @@
#include <stdint.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
void init_uart(uint16_t div);
uint8_t uart_tick();
uint8_t uart_busy();
void send_char(uint8_t c);
uint8_t send_char_nosleep(uint8_t c);
extern uint8_t uart_tx[];
extern volatile uint8_t uart_tx_w;
extern volatile uint8_t uart_tx_r;
void send_str(const char *s);
uint8_t uart_busy();
extern uint8_t uart_rx[];
extern volatile uint8_t uart_rx_n;
@ -32,18 +30,3 @@ void send_hex_long(uint32_t b);
void send_hex(uint8_t header, const uint8_t *s, uint8_t n, uint8_t words);
void send_decimal(uint16_t b, uint8_t dec);
static inline
void send_str(const char *s)
{
while (*s)
send_char(*s++);
}
static inline
void panic(const char *s)
{
uart_tx_w = 0;
uart_tx_r = 0;
while (*s)
send_char_nosleep(*s++);
}