diff --git a/src/common/memory.c b/src/common/memory.c index 75b6341..1519d37 100644 --- a/src/common/memory.c +++ b/src/common/memory.c @@ -1,48 +1,147 @@ #include "common.h" #include +#include -#define MEM_SIZE 1024 -#define BLOCK_SIZE sizeof(mem_data_t) -#define MAX_BLOCKS (MEM_SIZE / BLOCK_SIZE) -#define SENTINEL_MASK BIT(8) +#define MEM_SIZE 1024 +#define MEM_NUM_CELLS 2 +#define MEM_CELL_SIZE ((int) sizeof(word)) +#define MEM_BLOCK_SIZE (MEM_NUM_CELLS * MEM_CELL_SIZE + 1) +#define MEM_MAX_BLOCKS (MEM_SIZE / MEM_BLOCK_SIZE) +#define MEM_IS_DIRTY 0x80 // Dirty block marker -static byte ReadRaw(word addr); -static int WriteRaw(word addr, byte data); -static word GetBlockAddress(void); +// TODO: Automated testing /w multiple write cycles +// TODO: Accept structure containing variable data -int MEM_Read(mem_data_t *out) +static int GetUsedBlock(void); +static int GetFreeBlock(void); +static byte ReadRaw(int addr); +static void WriteRaw(int addr, byte data); + +void MEM_Init(void) { - // TODO - UNUSED(out); - UNUSED(ReadRaw); - UNUSED(GetBlockAddress); - return 0; + // Valid first sentinel? + if (ReadRaw(0) == 0xFF) { + Info("Initializing fresh memory..."); + MEM_Free(); // Set dirty block markers + } } -int MEM_Write(mem_data_t *in) +void MEM_Read(word *temp, word *dewp) { - // TODO - UNUSED(in); - UNUSED(WriteRaw); - return 0; + int bl; + word addr; + byte msb[2], lsb[2]; + + bl = GetUsedBlock(); + + if (bl < 0) { + *temp = 0; + *dewp = 0; + return; // Empty + } + + addr = bl * MEM_BLOCK_SIZE; + + msb[0] = ReadRaw(addr + 1); + lsb[0] = ReadRaw(addr + 2); + msb[1] = ReadRaw(addr + MEM_CELL_SIZE + 1); + lsb[1] = ReadRaw(addr + MEM_CELL_SIZE + 2); + + *temp = ((msb[0] & 0xFF) << 8) | (lsb[0] & 0xFF); + *dewp = ((msb[1] & 0xFF) << 8) | (lsb[1] & 0xFF); +} + +void MEM_Write(word temp, word dewp) +{ + int bl; + word addr; + byte msb[2], lsb[2]; + + bl = GetFreeBlock(); + + if (bl < 0) { + // Full + MEM_Free(); + bl = 0; + } + + addr = bl * MEM_BLOCK_SIZE; + + msb[0] = (temp >> 8) & 0xff; + lsb[0] = temp & 0xff; + msb[1] = (dewp >> 8) & 0xff; + lsb[1] = dewp & 0xff; + + WriteRaw(addr, MEM_IS_DIRTY); + WriteRaw(addr + 1, msb[0]); + WriteRaw(addr + 2, lsb[0]); + WriteRaw(addr + MEM_CELL_SIZE + 1, msb[1]); + WriteRaw(addr + MEM_CELL_SIZE + 2, lsb[1]); +} + +void MEM_Free(void) +{ + int bl; + + // Clear all dirty block markers + for (bl = 0; bl < MEM_MAX_BLOCKS; bl++) { + WriteRaw(bl * MEM_BLOCK_SIZE, 0x0); + if ((bl % 64) == 0) WDT_Reset(); + } } void MEM_Dump(void) { - // byte rom[1024]; - // Info("Dumping EEPROM memory:"); - // for (int i = 0; i < 1024; i++) { - // rom[i] = ReadRaw(i); - // } + byte raw; + char buf[3*16+1]; + + Info("Dumping EEPROM memory:"); + for (int i = 0; i < 64; i++) { + for (int j = 0; j < 16; j++) { + raw = ReadRaw(i * 16 + j); + sprintf(buf + j * 3, "%02X ", raw); + } + + WDT_Reset(); + Info(buf); + } } -static word GetBlockAddress(void) +static int GetUsedBlock(void) { - return 0; + int bl; + + bl = GetFreeBlock(); + + if (bl == 0) + return -1; + else if (bl < 0) + return MEM_MAX_BLOCKS; + else + return bl - 1; } -static int WriteRaw(word addr, byte data) +static int GetFreeBlock(void) +{ + int bl; + byte flag; + bool is_free; + + for (bl = 0; bl < MEM_MAX_BLOCKS; bl++) { + // Check dirty flag for current block + flag = ReadRaw(bl * MEM_BLOCK_SIZE); + if (flag != MEM_IS_DIRTY) { + is_free = true; + break; + } + } + + // Is there any space left? + return (is_free) ? bl : -1; +} + +static void WriteRaw(int addr, byte data) { // The EEMWE bit determines whether setting EEWE to // one causes the EEPROM to be written. When EEMWE @@ -68,12 +167,12 @@ static int WriteRaw(word addr, byte data) // Flag cleared during all the steps to avoid these // problems. - // Wait until ready - while (EECR & BIT(EEWE)); - // No interrupts during EEPROM write ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + // Wait until ready + while (EECR & BIT(EEWE)); + // The EEPROM Address Registers – EEARH and // EEARL – specify the EEPROM address in the // 1024bytes EEPROM space. The EEPROM data @@ -89,31 +188,33 @@ static int WriteRaw(word addr, byte data) EECR |= BIT(EEMWE); EECR |= BIT(EEWE); } - - return 0; } -static byte ReadRaw(word addr) +static byte ReadRaw(int addr) { - // Wait until ready - while (EECR & BIT(EEWE)); - - EEAR = addr; - // The EEPROM Read Enable Signal EERE is the read // strobe to the EEPROM. When the correct address // is set up in the EEAR Register, the EERE bit // must be written to a logic one to trigger the // EEPROM read. - // Read from address - EECR |= BIT(EERE); - // The EEPROM read access takes one instruction, // and the requested data is available immediately. // When the EEPROM is read, the CPU is halted for // four cycles before the next instruction is // executed. + // No interrupts during EEPROM read + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + + // Wait until ready + while (EECR & BIT(EEWE)); + + EEAR = addr; + + // Read from address + EECR |= BIT(EERE); + } + return EEDR; } diff --git a/src/common/memory.h b/src/common/memory.h index 6eed367..46db37a 100644 --- a/src/common/memory.h +++ b/src/common/memory.h @@ -1,15 +1,13 @@ #ifndef MAD_CORE_COMMON_MEMORY_H #define MAD_CORE_COMMON_MEMORY_H -typedef struct mem_data_s mem_data_t; +#define M_TEMPERATURE 0 +#define M_DEWPOINT 1 -struct mem_data_s { - byte sentinel; // Data marker bit - word value[2]; // Values to be written -}; - -int MEM_Read(mem_data_t *out); -int MEM_Write(mem_data_t *in); +void MEM_Init(void); +void MEM_Write(word temp, word dewp); +void MEM_Read(word *temp, word *dewp); +void MEM_Free(void); void MEM_Dump(void); #endif // MAD_CORE_COMMON_MEMORY_H diff --git a/src/main.c b/src/main.c index 9924207..286a50c 100644 --- a/src/main.c +++ b/src/main.c @@ -7,6 +7,7 @@ #include // TODO: Config header for chip specifics like EEPROM size. +// TODO: Make sure wear leveling resets memory correctly. // TODO: Implement primary state machine for update loop. // TODO: Migrate to ATMega 1284P-PU for 2nd 16-bit timer. // TODO: Keep persistent TEMP and DEWP targets in EEPROM. @@ -26,8 +27,8 @@ enum state_e }; static enum state_e state; -static float temp, temp_target; -static float dewp, dewp_target; +static word temp, temp_target; +static word dewp, dewp_target; static int Init(void) { @@ -67,6 +68,18 @@ static int Init(void) WDT_Enable(); WDT_SetTimeoutFlag(WDT2000); // 2 seconds + // Test persistent settings from EEPROM. There must + // be some sanity checking before these values are + // used. + + MEM_Init(); + MEM_Read(&temp_target, &dewp_target); + Info("Persistent memory contains [%d, %d]", + temp_target, dewp_target); + + // MEM_Write(20, 60); + // MEM_Dump(); + // There is a possiblity to use interrupt signals // for I2C communication but only as one large // branching routine for the whole I2C system.