Compare commits

..

5 commits

Author SHA1 Message Date
Stephan I. Böttcher
899798defa flash_find_free() 2026-01-12 20:16:48 +01:00
Stephan I. Böttcher
26e39e219e ADC_RTC flag 2026-01-12 20:14:31 +01:00
Stephan I. Böttcher
0248c1dab2 spi: isr rework 2026-01-12 15:32:21 +01:00
Stephan I. Böttcher
60abd1ab89 Makefile: all: eeprom 2026-01-12 15:31:56 +01:00
Stephan I. Böttcher
0e878a6fa4 fpga_cmd: provide .cmd 2026-01-12 14:19:13 +01:00
14 changed files with 272 additions and 53 deletions

View file

@ -4,7 +4,9 @@ PROJ=dose
PATH:=/usr/local/bin:$(PATH)
default: all
all: $(PROJ).hex
all: $(PROJ).hex $(PROJ)_all
dose_all: dose.eeprom dose.userrow
SN_dose = 1
MCU_dose = $(MCU_$(VAR))

View file

@ -209,6 +209,8 @@ uint8_t adc_poll(uint8_t flags)
if (r)
return r;
if (flags) {
if (flags & ADC_RTC && !rtc_cnt_tick())
return 1;
#ifdef HAVE_nFETs
if (flags & ADC_PWM)
pwm_step(config.pwm_max);

View file

@ -41,4 +41,5 @@ enum adc_flags {
ADC_16 = 0x10,
ADC_32 = 0x20,
ADC_64 = 0x40,
ADC_RTC = 0x80,
};

View file

@ -20,6 +20,8 @@ const struct config config = {
[0] = 0xd1 | FM_READ>>8, // Buffer 1 Read (Low-Frequency)
[1] = 0xd3 | FM_READ>>8, // Buffer 2 Read (Low-Frequency)
},
.page_start = 0x0800,
.page_end = 0x1000,
#ifdef HAVE_nFETs
.pwm_min = 0x0000,
.pwm_max = 0xffff,

View file

@ -17,6 +17,8 @@ struct config {
uint16_t write_buffer;
uint16_t read_array;
uint16_t read_buffer[2];
uint16_t page_start;
uint16_t page_end;
#ifdef HAVE_nFETs
uint16_t pwm_min;
uint16_t pwm_max;

View file

@ -26,10 +26,15 @@
section_status(main) struct magic magic;
const struct pipe_config cron_job[1] =
struct pipe_config cron_job[1] =
{
[0] = {
.pipe = {
.source = pipe_adc,
.dest = pipe_cmd,
.status = PS_OUT | PS_BCH,
.valid = 0,
.adc = ADC_64 | ADC_PWM | ADC_RTC,
},
},
@ -51,18 +56,20 @@ int main()
magic.reset_source = RSTCTRL.RSTFR;
RSTCTRL.RSTFR = magic.reset_source;
send_str("\nV Turbo Dose V0.9");
send_str("\nV Turbo Dose V0.0");
send_hex_byte_eol(magic.reset_source);
cron_job[0].page = flash_find_free();
cron_job[0].npages = config.page_end - cron_job[0].page;
if (config.cron & 1)
pipe_config(cron_job);
while (1) {
sei();
sleep_cpu();
command();
pipe_poll();
if (rtc_tick) {
rtc_tick = 0;
if (config.cron)
pipe_config(cron_job);
}
if (config.cron & 2 && rtc_cnt_tick())
pipe_config(cron_job);
}
}

View file

@ -124,6 +124,12 @@ uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte)
case FM_PAD2: pads += 1;
case FM_PAD1: pads += 1;
}
if (!(mode & FM_START)) {
spi.zsize = pads + size;
spi.zero = what;
spi_start_cmd(csize, flash_cmd_buffer);
return 0;
}
if (size >= 128) {
// for read of the security register
pads += size-64;
@ -136,8 +142,6 @@ uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte)
b = cmd_buffer + (what-96);
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);
@ -155,7 +159,6 @@ uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte)
struct flash_cmd {
uint16_t mode, what, page, byte;
uint8_t buffer[8];
};
uint8_t flash_submit_command(uint8_t *cmd)
@ -275,7 +278,7 @@ uint8_t flash_poll(uint8_t rr)
// request status bytes for pending Write or Error
r |= FS_StBsy;
fs.status = r;
flash_cmd_na(0xd7, 0xff02);
flash_cmd_na(0xd7 | FM_READ, 0xf002);
return r;
}
}
@ -316,3 +319,45 @@ uint8_t flash_start_stream(uint16_t page, uint16_t npages, uint8_t flags)
flash_status_bytes[0] = 0xff;
return flash_poll(0);
}
static inline
uint8_t flash_memset_buffer2()
{
for (uint16_t i=0; i<528; i += 176) {
uint8_t r = flash_cmd((uint16_t)config.flash_page_size << 8 | 0xff87, 176, 0, i);
if (r)
return r;
while (spi_busy_p()) ;
}
return 0;
}
static inline
uint8_t flash_compare_buffer2(uint16_t p)
{
uint8_t r;
r = flash_cmd((uint16_t)config.flash_page_size << 8 | 0x61, 0, p, 0);
if (r)
return r;
while (spi_busy_p()) ;
r = flash_cmd_na(0xd7 | FM_WAIT, 0xf002);
if (r)
return r;
while (spi_busy_p()) ;
return (flash_status_bytes[0] | flash_status_bytes[1]) & 0x40;
}
uint16_t flash_find_free()
{
flash_memset_buffer2();
uint16_t a = config.page_start;
uint16_t e = config.page_end;
while (e > a) {
uint16_t p = (a+e)>>1;
if (flash_compare_buffer2(p))
a = p;
else
e = p;
}
return a;
}

View file

@ -29,6 +29,7 @@ extern uint8_t flash_buffer[FB_SIZE];
uint8_t flash_submit_command(uint8_t *cmd);
uint8_t flash_start_stream(uint16_t page, uint16_t npages, uint8_t flags);
uint8_t flash_poll(uint8_t rr);
uint16_t flash_find_free();
extern
struct flash_stream {

View file

@ -26,7 +26,7 @@ void fpga_cmd(struct fpga_cmd *c)
spi_select(0);
spi.zero = c->n & 0x80; // send nop: 0x8080 (please), or zeros
spi.csize = n;
spi.wdata = c->d;
spi.cmd = c->d;
spi.rdata = c->d;
if (c->z & 0x80) {
spi.isize = n + z>>3 & 0x0e; // ignore cmd ± (z[6:4])

View file

@ -28,19 +28,19 @@ uint8_t pipe_poll()
if (pipe.dest & pipe_flash && !(fl & FS_Ready))
// flash did not finish successfully
goto done;
if (pipe.source & pipe_adc && !adc_poll(pipe.adc))
goto done;
// fpga is OUT when the spi is ready.
// We are done with this buffer
pipe.valid = 0;
if (pipe.source & pipe_flash)
flash_poll(1);
else if (pipe.source & pipe_adc)
adc_poll(pipe.adc);
#ifdef HAVE_FPGA
else if (pipe.source & pipe_fpga)
fpga_start(0);
#endif
r |=~ PS_OUT;
r &= ~4;
r &=~ (PS_OUT|4);
r++;
goto done;
}

View file

@ -14,6 +14,15 @@ volatile uint16_t clockh;
volatile uint8_t pit_tick;
volatile uint8_t rtc_tick;
uint8_t rtc_cnt_tick()
{
cli();
uint8_t r = rtc_tick;
rtc_tick = 0;
sei();
return r;
}
struct_ioconf(rtc_config) = {
conf_prefix(RTC),
conf_iow(RTC.CMP, 3600),

View file

@ -10,3 +10,5 @@ volatile extern uint16_t clockh;
volatile extern uint8_t pit_tick;
volatile extern uint8_t rtc_tick;
uint32_t time();
uint8_t rtc_cnt_tick();

208
src/spi.c
View file

@ -15,62 +15,213 @@ struct_ioconf(spi_config) = {
conf_io(SPI.CTRLA, SPI_MASTER_bm | SPI_ENABLE_bm | SPI_SPEED),
};
#if 0
ISR(SPI0_INT_vect)
{
uint8_t d;
uint8_t ifg = SPI.INTFLAGS;
while (ifg & SPI_DREIF_bm) {
if (ifg & SPI_DREIF_bm) {
repeat:
if (spi.csize) {
SPI.DATA = *spi.cmd++;
spi.csize--;
d = *spi.cmd++;
}
else if (spi.zsize) {
SPI.DATA = spi.zero;
spi.zsize--;
d = spi.zero;
}
else if (spi.wsize) {
if (spi.wdata)
SPI.DATA = *spi.wdata++;
else
SPI.DATA = spi.zero;
spi.wsize--;
d = *spi.wdata++;
}
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;
SPI.INTCTRL = SPI_TXCIF_bm | SPI_RXCIF_bm;
goto done_write;
}
SPI.DATA = d;
// clear a stray TXCIF, at high SCK rate
SPI.INTFLAGS = SPI_TXCIF_bm;
SPI.INTCTRL = SPI_TXCIF_bm | SPI_DREIF_bm | SPI_RXCIF_bm;
done_write:
ifg = SPI.INTFLAGS;
}
while (ifg & SPI_RXCIF_bm) {
uint8_t d = SPI.DATA; // clears the IF flag
if (ifg & SPI_RXCIF_bm) {
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;
goto cont;
}
spi.mask = 0;
}
if (spi.rsize) {
*spi.rdata++ = d;
spi.rsize--;
}
else if (!spi.mask) {
SPI.INTCTRL = SPI_DREIF_bm;
*spi.rdata++ = d;
}
}
cont:
ifg = SPI.INTFLAGS;
if (ifg & SPI_DREIF_bm)
// This may prevent this ISR to terminate
// before the job is done at high clock rate.
// DRE is kept set when all bytes were sent.
// With just the right clock rate, we may send
// two bytes per interrupt.
goto repeat;
}
if (ifg & SPI_TXCIF_bm) {
if (!(spi.mode & SPI_CONT))
SSEL_VPORT.OUT |= 1 << SSEL_PIN;
SPI.INTCTRL = SPI_RXCIF_bm;
}
}
#else
ISR(SPI0_INT_vect, ISR_NAKED)
{
__asm__ volatile(
"push r24" "\n\t"
"in r24, __SREG__" "\n\t"
"push r24" "\n\t"
"push r25" "\n\t"
"push r26" "\n\t"
"push r30" "\n\t"
"push r31" "\n\t"
"lds r24, %[IFLGS]" "\n\t"
"3:" "\n\t"
"sbrs r24, %[DRE]" "\n\t"
"rjmp 3f" "\n"
"5:" "\n"
"lds r25, %[CSZ]" "\n\t"
"subi r25, 1" "\n\t"
"brcs 1f" "\n\t"
"sts %[CSZ], r25" "\n\t"
"lds r30, %[CMD]" "\n\t"
"lds r31, %[CMD]+1" "\n\t"
"ld r25, Z+" "\n\t"
"sts %[CMD], r30" "\n\t"
"sts %[CMD]+1, r31" "\n\t"
"rjmp 2f" "\n"
"1:" "\n\t"
"lds r25, %[ZSZ]" "\n\t"
"subi r25, 1" "\n\t"
"brcs 1f" "\n\t"
"sts %[ZSZ], r25" "\n\t"
"lds r25, %[ZERO]" "\n\t"
"rjmp 2f" "\n"
"1:" "\n\t"
"lds r25, %[WSZ]" "\n\t"
"subi r25, 1" "\n\t"
"ldi r26, %[RIFLGS]" "\n\t"
"brcs 1f" "\n\t"
"sts %[WSZ], r25" "\n\t"
"lds r30, %[WD]" "\n\t"
"lds r31, %[WD]+1" "\n\t"
"ld r25, Z+" "\n\t"
"sts %[WD], r30" "\n\t"
"sts %[WD]+1, r31" "\n"
"2:" "\n\t"
"sts %[DATA], r25" "\n\t"
"ldi r25, 1<<%[TXC]" "\n\t"
"sts %[IFLGS], r25" "\n\t"
"ldi r26, %[AIFLGS]" "\n"
"1:" "\n\t"
"sts %[ICTRL], r26" "\n\t"
"lds r24, %[IFLGS]" "\n"
"sbrs r24, %[RXC]" "\n\t"
"rjmp 3f" "\n\t"
"lds r25, %[DATA]" "\n\t"
"lds r26, %[ISZ]" "\n\t"
"subi r26, 1" "\n\t"
"brcs 1f " "\n\t"
"sts %[ISZ], r26" "\n\t"
"rjmp 4f" "\n"
"1:" "\n\t"
"lds r26, %[MASK]" "\n\t"
"cpi r26, 0" "\n\t"
"breq 1f" "\n\t"
"and r26, r25" "\n\t"
"lds r24, %[WAIT]" "\n\t"
"cp r24, r26" "\n\t"
"brne 2f" "\n\t"
"lds r25, %[ZSZ]" "\n\t"
"subi r25, -1" "\n\t"
"sts %[ZSZ], r25" "\n\t"
"rjmp 4f" "\n"
"2:" "\n\t"
"clr r26" "\n\t"
"sts %[MASK], r26" "\n"
"1:" "\n\t"
"lds r26, %[RSZ]" "\n\t"
"subi r26, 1" "\n\t"
"brcs 4f" "\n\t"
"sts %[RSZ], r26" "\n\t"
"lds r30, %[RD]" "\n\t"
"lds r31, %[RD]+1" "\n\t"
"st Z+, r25" "\n\t"
"sts %[RD], r30" "\n\t"
"sts %[RD]+1, r31" "\n"
"4:" "\n\t"
"lds r24, %[IFLGS]" "\n\t"
"sbrc r24, %[DRE]" "\n\t"
"rjmp 5b" "\n\t"
"3:" "\n\t"
"sbrs r24, %[TXC]" "\n\t"
"rjmp 3f" "\n\t"
"lds r25, %[MODE]" "\n\t"
"sbrs r25, 7" "\n\t"
"sbi %[SPORT], %[SSEL]" "\n\t"
"ldi r25, 1<<%[RXC]" "\n\t"
"sts %[ICTRL], r25" "\n"
"3:" "\n\t"
"pop r31" "\n\t"
"pop r30" "\n\t"
"pop r26" "\n\t"
"pop r25" "\n\t"
"pop r24" "\n\t"
"out __SREG__, r24" "\n\t"
"pop r24" "\n\t"
"reti" "\n"
::
[CSZ] "m" (spi.csize),
[ZSZ] "m" (spi.zsize),
[WSZ] "m" (spi.wsize),
[ISZ] "m" (spi.isize),
[RSZ] "m" (spi.rsize),
[CMD] "m" (spi.cmd),
[ZERO] "m" (spi.zero),
[WD] "m" (spi.wdata),
[RD] "m" (spi.rdata),
[MODE] "m" (spi.mode),
[MASK] "m" (spi.mask),
[WAIT] "m" (spi.wait),
[IFLGS] "n" (&SPI.INTFLAGS),
[TXC] "n" (SPI_TXCIF_bp),
[DRE] "n" (SPI_DREIF_bp),
[RXC] "n" (SPI_RXCIF_bp),
[RIFLGS] "n" (SPI_TXCIF_bm | SPI_RXCIF_bm),
[AIFLGS] "n" (SPI_TXCIF_bm | SPI_RXCIF_bm | SPI_DREIF_bm),
[ICTRL] "n" (&SPI.INTCTRL),
[DATA] "n" (&SPI.DATA),
[SPORT] "n" (_SFR_IO_ADDR(SSEL_VPORT)),
[SSEL] "n" (SSEL_PIN)
);
}
#endif
uint8_t spi_select(uint8_t mode)
{
@ -80,9 +231,9 @@ uint8_t spi_select(uint8_t mode)
memset(&spi, 0, sizeof(spi));
spi.mode = mode;
if (mode & SPI_FLASH)
SSEL_PORT.OUT &=~ (1 << SSEL_PIN);
SSEL_VPORT.OUT &=~ (1 << SSEL_PIN);
else
SSEL_PORT.OUT |= 1 << SSEL_PIN;
SSEL_VPORT.OUT |= 1 << SSEL_PIN;
if (mode & SPI_CONFIG)
SPI.CTRLA |= SPI_DORD_bm;
else
@ -90,7 +241,6 @@ uint8_t spi_select(uint8_t mode)
return 0;
}
void spi_start_cmd(uint8_t csize, uint8_t *cmd)
{
spi.cmd = cmd;
@ -112,7 +262,7 @@ 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.zsize += rsize;
spi.rsize = rsize;
spi_start_cmd(csize, cmd);
}

View file

@ -14,7 +14,6 @@
extern
struct spi_job {
uint8_t mode;
uint8_t status;
uint8_t csize; // pipe.fpga.…
uint8_t zsize; //
uint8_t isize; //
@ -34,10 +33,6 @@ enum spi_mode_bits {
SPI_CONT = 0x80,
};
enum spi_status_bits {
SPI_IDLE = 0x01,
};
static inline
uint8_t spi_abort()
{
@ -51,13 +46,14 @@ uint8_t spi_abort()
static inline
uint8_t spi_busy_p()
{
return SPI.INTFLAGS;
return SPI.INTFLAGS & SPI_TXCIF_bm;
}
static inline
void spi_start()
{
SPI.INTCTRL = SPI_DREIF_bm | SPI_RXCIF_bm;
SPI.INTCTRL = SPI_DREIF_bm;
// the ISR will immediately set SPI_TXCIF_bm.
}
static inline void barrier() { __asm__("":::"memory"); }