ed476473b6
Three defects prevented the board from working at all: - INT0/INT1 were falling-edge triggered. Edge detection needs the I/O clock, which SLEEP_MODE_PWR_DOWN stops, so neither the reed switch nor the RTC alarm could wake the MCU. Both are now low-level triggered (the only asynchronous mode), and each handler masks its own interrupt while the source is still asserted so the low level cannot retrigger. The reed and RTC pins also get their pull-ups; they were explicitly driven low. - Statics were 1440 B of 2048 with a 538 B main frame, so the first NFC read ran the stack into .data. Shrank the oversized buffers and made the NFC scratch buffer static: statics 1440 -> 1038 B, main frame -> 204 B. - The FIFO was filled after entering TX mode with TxStart = FifoNotEmpty, so transmission began before the payload was loaded. Load in standby. Memory safety: clamp the unvalidated RX length (len - 4 underflowed to >=252 into a 60-byte buffer), fix writes one byte past DATA_BUFFER_65, fix the diameter copy length in st25dv.c, NUL-terminate remove_spaces, and bounds-check the NDEF parser (dropping its tag-sized VLA and its unchecked payload_length decrements). Hangs: add bail-outs to every peripheral poll loop - RFM69 mode/TX/RX waits, the EEPROM WIP poll, all six I2C TWINT spins, and the ADC. The LDO is cut before sleeping, so a slow peripheral hung the firmware with no watchdog armed. Correctness: boot no longer wipes the EEPROM spool; the replayed packet is sent once and deleted only on success; short ATOMIC_BLOCK sections replace the blanket cli() that lost reed pulses during the radio window; the I2C rail comes up before the RTC is touched; sleep_bod_disable() moves into the timed sequence with the sleep race closed; sei() no longer runs inside ISRs; REG_FDEV_MSB was 0x06 twice so deviation was 0; ADC uses return ADC and a /64 prescaler; SS1 is an output before SPE is set. VAL_DATA_MODUL_OOK was misnamed rather than wrong - 0x01 lands in ModulationShaping, not ModulationType - so the register value is unchanged and on-air behavior still matches the base station. Verified: builds clean under -Wall -Wextra on both gnu17 and c23. Not yet run on hardware. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YVJKatfeMJjAmuH9KYiLuv
158 lines
3.7 KiB
C
158 lines
3.7 KiB
C
#include "m95128.h"
|
|
|
|
uint8_t read_value;
|
|
uint8_t old_last_page;
|
|
uint8_t new_last_page;
|
|
void spi_eeprom_select(bool state)
|
|
{
|
|
SET_PIN_OUT(DDRB, DDB0);
|
|
if (!state) {
|
|
SET_PIN_HIGH(PORTB, PB0);
|
|
} else {
|
|
SET_PIN_LOW(PORTB, PB0);
|
|
}
|
|
}
|
|
|
|
void eeprom_write(uint8_t page, unsigned const char* msg, uint8_t msg_len)
|
|
{
|
|
spi_rfm69_select(false);
|
|
|
|
spi_eeprom_select(true);
|
|
spi_write(EEPROM_WREN);
|
|
spi_eeprom_select(false);
|
|
|
|
spi_eeprom_select(true);
|
|
spi_write(EEPROM_WRITE);
|
|
write_page_address(page);
|
|
|
|
for (int i = 0; i < msg_len; i++) {
|
|
spi_write(msg[i]);
|
|
}
|
|
|
|
spi_eeprom_select(false);
|
|
|
|
spi_eeprom_select(true);
|
|
spi_write(EEPROM_WRDI);
|
|
spi_eeprom_select(false);
|
|
|
|
// Poll the write-in-progress bit, but give up rather than spin forever if
|
|
// the EEPROM is unpowered or absent.
|
|
for (uint16_t attempts = 0; attempts < EEPROM_POLL_TIMEOUT_MS; attempts++) {
|
|
spi_eeprom_select(true);
|
|
spi_write(EEPROM_RDSR);
|
|
read_value = spi_read();
|
|
spi_eeprom_select(false);
|
|
if ((read_value & EEPROM_STATUS_WIP) == 0) {
|
|
return;
|
|
}
|
|
_delay_ms(1);
|
|
}
|
|
}
|
|
|
|
void eeprom_read(uint8_t page, unsigned char* msg, uint8_t msg_len)
|
|
{
|
|
|
|
spi_eeprom_select(true);
|
|
spi_write(EEPROM_WRDI);
|
|
spi_eeprom_select(false);
|
|
|
|
spi_eeprom_select(true);
|
|
spi_write(EEPROM_READ);
|
|
write_page_address(page);
|
|
|
|
for (int i = 0; i < msg_len; i++) {
|
|
msg[i] = spi_read();
|
|
}
|
|
spi_eeprom_select(false);
|
|
}
|
|
|
|
void delete_last_page(void)
|
|
{
|
|
old_last_page = get_last_page();
|
|
if (old_last_page == 0) // If we have nothing, no need to delete anything
|
|
{
|
|
return;
|
|
}
|
|
new_last_page = old_last_page - 1;
|
|
write_last_page_value(new_last_page);
|
|
eeprom_clear_page(old_last_page);
|
|
}
|
|
|
|
uint8_t get_last_page(void)
|
|
{
|
|
memset(DATA_BUFFER_65, 0, 1);
|
|
eeprom_read(0, DATA_BUFFER_65, 1);
|
|
return (uint8_t)DATA_BUFFER_65[0];
|
|
}
|
|
|
|
void write_last_page_value(uint8_t page)
|
|
{
|
|
DATA_BUFFER_7[0] = page;
|
|
eeprom_write(0, DATA_BUFFER_7, 1);
|
|
}
|
|
|
|
void write_page_address(uint8_t page)
|
|
{
|
|
uint16_t addr = page * 0x40; // Each page starts at 64 byte intervals. And 64 byte is 0x40 or
|
|
// 0b01000000;
|
|
spi_write((addr >> 8) & 0xFF);
|
|
spi_write(addr & 0xFF);
|
|
}
|
|
|
|
void eeprom_write_tx_data(uint8_t page, tx_rx_data_struct tx_data_in)
|
|
{
|
|
if (page == 0) {
|
|
return;
|
|
}
|
|
memset(DATA_BUFFER_65, ' ', 64);
|
|
DATA_BUFFER_65[64] = '\0';
|
|
memcpy(DATA_BUFFER_65, tx_data_in.msg, 60);
|
|
DATA_BUFFER_65[60] = tx_data_in.flags;
|
|
DATA_BUFFER_65[61] = tx_data_in.from;
|
|
DATA_BUFFER_65[62] = tx_data_in.to;
|
|
DATA_BUFFER_65[63] = tx_data_in.dtype;
|
|
|
|
eeprom_write(page, DATA_BUFFER_65, 64);
|
|
}
|
|
|
|
tx_rx_data_struct eeprom_read_tx_data(uint8_t page)
|
|
{
|
|
reset_txrx_struct(&TX_DATA);
|
|
memset(DATA_BUFFER_65, ' ', 64);
|
|
DATA_BUFFER_65[64] = '\0';
|
|
eeprom_read(page, DATA_BUFFER_65, 64);
|
|
memcpy(TX_DATA.msg, DATA_BUFFER_65, 60);
|
|
TX_DATA.flags = DATA_BUFFER_65[60];
|
|
TX_DATA.from = DATA_BUFFER_65[61];
|
|
TX_DATA.to = DATA_BUFFER_65[62];
|
|
TX_DATA.dtype = DATA_BUFFER_65[63];
|
|
TX_DATA.len = 60;
|
|
return TX_DATA;
|
|
}
|
|
|
|
tx_rx_data_struct read_struct_last_page(void)
|
|
{
|
|
uint8_t page_num = get_last_page();
|
|
return eeprom_read_tx_data(page_num);
|
|
}
|
|
|
|
void write_struct_to_last_page(tx_rx_data_struct tx_data_in)
|
|
{
|
|
uint8_t page_val = get_last_page();
|
|
uint8_t next_page_val;
|
|
if (page_val == 255) {
|
|
next_page_val = 1;
|
|
} else {
|
|
next_page_val = page_val + 1;
|
|
}
|
|
|
|
eeprom_write_tx_data(next_page_val, tx_data_in);
|
|
write_last_page_value(next_page_val);
|
|
}
|
|
|
|
void eeprom_clear_page(uint8_t page)
|
|
{
|
|
memset(DATA_BUFFER_65, ' ', 64);
|
|
eeprom_write(page, DATA_BUFFER_65, 64);
|
|
}
|