mirror of
https://codeberg.org/SiB64/turbo_weather.git
synced 2026-05-01 15:14:22 +02:00
Compare commits
No commits in common. "fd9957aee14bef578b53de0972ad213e156afe08" and "f692d97cf033b195c6c8cd62224dc7145caa7258" have entirely different histories.
fd9957aee1
...
f692d97cf0
9 changed files with 84 additions and 309 deletions
|
|
@ -119,7 +119,7 @@ fuse: $(PROJ).fuse$F
|
|||
BC_MAGIC = 0xba
|
||||
BC_VERS = 4
|
||||
BC_TRIG = 0x08
|
||||
BC_SEND = 0x2b
|
||||
BC_SEND = 0x1b
|
||||
BC_PWR = 0x04
|
||||
BC_TEST = 5
|
||||
BC_SPI = 0xff
|
||||
|
|
|
|||
129
src/adc.c
129
src/adc.c
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "adc.h"
|
||||
#include "bate.h"
|
||||
#include "mul.h"
|
||||
#include "eeprom.h"
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
|
|
@ -24,53 +23,31 @@ 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
|
||||
},
|
||||
{ // NTC biased by V_RF
|
||||
{ // Batterie Voltage / 11
|
||||
.mode = MODE,
|
||||
.ref = REF | ADC_REFSEL_2500MV_gc,
|
||||
.inp = INP | V_NTC,
|
||||
.calib = 2500 * 10,
|
||||
.shifts = 1,
|
||||
},
|
||||
{ // NTC biased by V_RF
|
||||
.mode = MODE,
|
||||
.ref = REF | ADC_REFSEL_VDD_gc,
|
||||
.inp = INP | V_NTC,
|
||||
.ref = REF | ADC_REFSEL_1024MV_gc,
|
||||
.inp = INP | V_BAT,
|
||||
},
|
||||
{ // Transmitter power
|
||||
.mode = MODE,
|
||||
.ref = REF | ADC_REFSEL_2500MV_gc,
|
||||
.ref = REF | ADC_REFSEL_VDD_gc,
|
||||
.inp = INP | V_RFP,
|
||||
.calib = 2500 * 10,
|
||||
.shifts = 1,
|
||||
},
|
||||
{ // Transmitter power
|
||||
.mode = MODE,
|
||||
.ref = REF | ADC_REFSEL_VDD_gc,
|
||||
.ref = REF | ADC_REFSEL_2500MV_gc,
|
||||
.inp = INP | V_RFP,
|
||||
},
|
||||
};
|
||||
|
|
@ -166,7 +143,6 @@ 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"
|
||||
|
|
@ -207,98 +183,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 (32/sizeof(struct adc_conf))
|
||||
|
||||
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);
|
||||
|
|
|
|||
29
src/bate.c
29
src/bate.c
|
|
@ -438,16 +438,8 @@ 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');
|
||||
}
|
||||
send_hex('A', (uint8_t *)adc_readings,
|
||||
2*n_adc, 3);
|
||||
}
|
||||
|
||||
if (uart_busy()) {
|
||||
|
|
@ -528,17 +520,14 @@ int main()
|
|||
send_char('\n');
|
||||
}
|
||||
|
||||
uint8_t send_test = 0;
|
||||
const union bate *b = &bate;
|
||||
if (test_calib) {
|
||||
b = testdata + -- test_calib;
|
||||
send_test = 1;
|
||||
}
|
||||
if (config.send & SEND_BATEW || send_config)
|
||||
if (test_calib)
|
||||
b = testdata + test_calib;
|
||||
if (config.send & SEND_BATEW || send_config || test_calib)
|
||||
send_hex('W', b->b, 8, 3);
|
||||
if (config.send & SEND_BATED || send_test)
|
||||
if (config.send & SEND_BATED || test_calib)
|
||||
send_hex('D', b->b+8, 4, 3);
|
||||
if (config.send & SEND_CALIB || send_test) {
|
||||
if (config.send & SEND_CALIB || test_calib) {
|
||||
bate_calib(b, &pressure);
|
||||
send_str("P ");
|
||||
send_str(decimal_str(pressure.p, 1));
|
||||
|
|
@ -546,6 +535,8 @@ 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;
|
||||
|
|
@ -553,7 +544,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), 3);
|
||||
send_hex('E', (uint8_t *)adc_conf, sizeof(adc_conf), 1);
|
||||
}
|
||||
|
||||
if (config.send & SEND_DEBUG) {
|
||||
|
|
|
|||
|
|
@ -44,10 +44,9 @@ enum send_flags {
|
|||
SEND_BATED = 0x02,
|
||||
SEND_BATEW = 0x04,
|
||||
SEND_CLOCK = 0x08,
|
||||
SEND_CALIB = 0x10,
|
||||
SEND_ADC_HEX = 0x20,
|
||||
SEND_ADC_VOLT = 0x40,
|
||||
SEND_ADC = 0x60,
|
||||
SEND_ADC = 0x10,
|
||||
SEND_CALIB = 0x20,
|
||||
SEND_VOLT = 0x40,
|
||||
SEND_DEBUG = 0x80,
|
||||
};
|
||||
enum trigger_flags {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,6 @@ struct pressure {
|
|||
uint16_t p;
|
||||
};
|
||||
|
||||
#define N_TESTDATA (64/sizeof(union bate))
|
||||
#define N_TESTDATA (96/sizeof(union bate))
|
||||
void bate_calib(const union bate *bate, struct pressure *pt);
|
||||
extern const union bate testdata[N_TESTDATA];
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
#define ADC_CONF_ADDR __attribute__((__section__(".eeprom")))
|
||||
#define TESTDATA_ADDR __attribute__((__section__(".eeprom")))
|
||||
#define EEPROM(a) __attribute__((__section__(".eeprom")))
|
||||
#define ADC_CONF_ADDR EEPROM(0x1400)
|
||||
#define TESTDATA_ADDR EEPROM(0x1420)
|
||||
|
|
|
|||
188
src/uart.c
188
src/uart.c
|
|
@ -7,9 +7,8 @@
|
|||
#include "bate.h"
|
||||
#include "mul.h"
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/sleep.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
#define Bit(x) (1<<(x))
|
||||
|
||||
|
|
@ -34,12 +33,13 @@ volatile uint8_t rx_tick;
|
|||
ISR(PORTB_PORT_vect, ISR_NAKED)
|
||||
{
|
||||
__asm__ ("push r24" "\n\t"
|
||||
"lds r24, %[stat]" "\n\t"
|
||||
"lds r24,%[stat]" "\n\t"
|
||||
"sts %[tick], r24" "\n\t"
|
||||
"sts %[stat], r24" "\n\t"
|
||||
"sts rx_tick, r24" "\n\t"
|
||||
"pop r24" "\n\t"
|
||||
"reti" "\n"
|
||||
: : [stat] "n" (&VPORTB.INTFLAGS)
|
||||
:[tick] "+m" (rx_tick)
|
||||
:[stat] "n" (&VPORTB.INTFLAGS)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -56,32 +56,23 @@ 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_busy;
|
||||
volatile uint8_t uart_tx_b;
|
||||
static const uint8_t uart_tx_m = sizeof(uart_tx) - 1;
|
||||
|
||||
#if 1
|
||||
|
||||
__attribute__ ((noinline, noclone))
|
||||
void tx()
|
||||
static inline 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;
|
||||
while (r != uart_tx_w) {
|
||||
if (!(USART0.STATUS & USART_DREIF_bm)) {
|
||||
USART0.CTRLA |= USART_DREIE_bm | USART_TXCIE_bm;
|
||||
USART0.CTRLA |= USART_DREIE_bm;
|
||||
USART0.STATUS = USART_TXCIF_bm;
|
||||
uart_tx_b = USART_TXCIF_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.TXDATAL = uart_tx[r & uart_tx_m];
|
||||
r++;
|
||||
}
|
||||
uart_tx_r = r;
|
||||
USART0.CTRLA &=~ USART_DREIE_bm;
|
||||
|
|
@ -93,154 +84,54 @@ 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 uart_tx_busy;
|
||||
return b;
|
||||
}
|
||||
|
||||
static inline
|
||||
uint8_t uart_put_char(uint8_t c)
|
||||
__attribute__ ((noinline, noclone))
|
||||
uint8_t send_char_nosleep(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;
|
||||
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;
|
||||
__asm__("" ::: "memory");
|
||||
uart_tx_w = ww;
|
||||
__asm__("" ::: "memory");
|
||||
return 0;
|
||||
full:
|
||||
uart_busy();
|
||||
return r;
|
||||
}
|
||||
|
||||
__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();
|
||||
while (!send_char_nosleep(c)) {
|
||||
s = 1;
|
||||
sleep_cpu();
|
||||
}
|
||||
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;
|
||||
|
|
@ -271,9 +162,8 @@ void rx_dismiss(uint8_t n)
|
|||
cli();
|
||||
uint8_t i = 0;
|
||||
uart_rx_mes = 0;
|
||||
uint8_t w = uart_rx_w;
|
||||
if (w != uart_rx_m)
|
||||
while (n < w) {
|
||||
if (uart_rx_w != uart_rx_m)
|
||||
while (n < uart_rx_w) {
|
||||
uint8_t c = uart_rx[n++];
|
||||
uart_rx[i++] = c;
|
||||
if (c=='\n')
|
||||
|
|
|
|||
23
src/uart.h
23
src/uart.h
|
|
@ -5,13 +5,15 @@
|
|||
#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();
|
||||
void send_char(uint8_t c);
|
||||
void send_str(const char *s);
|
||||
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;
|
||||
|
||||
extern uint8_t uart_rx[];
|
||||
extern volatile uint8_t uart_rx_n;
|
||||
|
|
@ -30,3 +32,18 @@ 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++);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue