From 4a99ede565fa61005f2a6202669adaa87a8ce0ee Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Sat, 16 Aug 2025 12:19:09 -0230 Subject: spi and eeprom modules --- sw/eeprom.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sw/eeprom.h | 20 ++++++++++ sw/main.c | 26 ++++++++++--- sw/spi.c | 30 +++++++++++++++ sw/spi.h | 9 +++++ sw/sys.h | 5 +++ sw/types.c | 13 +++++++ sw/types.h | 13 +++++++ 8 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 sw/eeprom.c create mode 100644 sw/eeprom.h create mode 100644 sw/spi.c create mode 100644 sw/spi.h create mode 100644 sw/sys.h create mode 100644 sw/types.c create mode 100644 sw/types.h (limited to 'sw') diff --git a/sw/eeprom.c b/sw/eeprom.c new file mode 100644 index 0000000..ce8aed7 --- /dev/null +++ b/sw/eeprom.c @@ -0,0 +1,123 @@ +#include + +#include +#include + +#include "sys.h" +#include "types.h" +#include "spi.h" + +#include "eeprom.h" + +// 16-byte page ('C' variant) +#define PAGE_SIZE 16u + +// Commands +enum { + CMD_READ = 0x03, + CMD_WRITE = 0x02, + CMD_WRITE_DISABLE = 0x04, + CMD_WRITE_ENABLE = 0x06, + CMD_READ_STATUS = 0x05, + CMD_WRITE_STATUS = 0x01, +}; + +// Status register masks +enum { + STATUS_WIP = 0x1, // write in progress + STATUS_WEL = 0x2, // write enable latch + STATUS_BP0 = 0x4, // block protection 0 + STATUS_BP1 = 0x8, // block protection 1 +}; + +// Timing +enum { + TIME_WRITE_CYCLE_MS = 5, +}; + +void +eepromInit(void) { + EEPROM_CS_TRIS = OUT; + EEPROM_CS = 1; + + eepromWriteDisable(); +} + +void +eepromWriteEnable(void) { + EEPROM_CS = 0; + (void)spiTx(CMD_WRITE_ENABLE); + EEPROM_CS = 1; +} + +void +eepromWriteDisable(void) { + EEPROM_CS = 0; + (void)spiTx(CMD_WRITE_DISABLE); + EEPROM_CS = 1; +} + +static bool +isWriteInProgress(void) { + U8 status; + + EEPROM_CS = 0; + (void)spiTx(CMD_READ_STATUS); + status = spiTx(0x00); + EEPROM_CS = 1; + return status & STATUS_WIP; +} + +static void +spiTxAddr(U16 addr) { + (void)spiTx(addr.hi); + (void)spiTx(addr.lo); +} + +static bool +isPageStart(U16 addr) { + return (addr.lo % PAGE_SIZE) == 0; +} + +void +eepromWrite(U16 addr, U8 *data, U8 size) { + while (isWriteInProgress()) {} // wait for previous write to finish + + EEPROM_CS = 0; // pull chip-select low + + (void)spiTx(CMD_WRITE); + spiTxAddr(addr); + while (size--) { + (void)spiTx(*data); + data++; + incU16(&addr); + + // Check if write crosses page boundary + if (isPageStart(addr) && size) { + // Write current page to memory + EEPROM_CS = 1; + while (isWriteInProgress()) {} + EEPROM_CS = 0; + + // Start next page + (void)spiTx(CMD_WRITE); + spiTxAddr(addr); + } + } + + EEPROM_CS = 1; // release chip-select +} + +void +eepromRead(U16 addr, U8 *data, U8 size) { + EEPROM_CS = 0; // pull chip-select low + + (void)spiTx(CMD_READ); + spiTxAddr(addr); + while (size--) { + *data = spiTx(0x00); + data++; + } + + EEPROM_CS = 1; // release chip-select +} diff --git a/sw/eeprom.h b/sw/eeprom.h new file mode 100644 index 0000000..4e4dfe2 --- /dev/null +++ b/sw/eeprom.h @@ -0,0 +1,20 @@ +/* Microchip 25LC160C 2KiB EEPROM + * + * Usage: + * + * #include + * #include + * #include "types.h" + * #include "spi.h" + * #include "eeprom.h" + */ + +// Pin mapping +#define EEPROM_CS_TRIS TRISAbits.TRISA5 +#define EEPROM_CS LATAbits.LATA5 + +void eepromInit(void); +void eepromWriteEnable(void); +void eepromWriteDisable(void); +void eepromWrite(U16 addr, U8 data[], U8 size); +void eepromRead(U16 addr, U8 data[], U8 size); diff --git a/sw/main.c b/sw/main.c index c79b6e8..4d9b2be 100644 --- a/sw/main.c +++ b/sw/main.c @@ -1,5 +1,11 @@ #include +#include + +#include "types.h" +#include "spi.h" +#include "eeprom.h" + // CONFIG1 #pragma config FOSC = INTOSC // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin) #pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled) @@ -23,22 +29,32 @@ #pragma config LVP = ON // Low-Voltage Programming Enable static void -clock_init(void) { +clockInit(void) { OSCCON = 0xFC; // HFINTOSC @ 16MHz, 3x PLL, PLL enabled ACTCON = 0x90; // active clock tuning enabled for USB } static void -gpio_init(void) { - +pinsInit(void) { + // Disable all analog pin functions + ANSELA = 0; + ANSELB = 0; + ANSELC = 0; } void main(void) { - clock_init(); - gpio_init(); + clockInit(); + pinsInit(); + spiInit(); + eepromInit(); for (;;) { } } + +void +__interrupt() isr(void) { + +} diff --git a/sw/spi.c b/sw/spi.c new file mode 100644 index 0000000..1793c4d --- /dev/null +++ b/sw/spi.c @@ -0,0 +1,30 @@ +#include + +#include + +#include "sys.h" +#include "types.h" + +#include "spi.h" + +void +spiInit(void) { + U8 junk; + + TRISBbits.TRISB4 = IN; // SDI + TRISCbits.TRISC7 = OUT; // SDO + TRISBbits.TRISB6 = OUT; // SCK + + SSPSTAT = 0x00; + SSPCON1 = 0x01; // FOSC/16 => 3MHz SPI clock + SSPCON1bits.SSPEN = 1; // enable + junk = SSPBUF; // dummy read to clear BF + (void)junk; +} + +U8 +spiTx(U8 c) { + SSPBUF = c; + while (!SSPSTATbits.BF) {} + return SSPBUF; +} diff --git a/sw/spi.h b/sw/spi.h new file mode 100644 index 0000000..a949155 --- /dev/null +++ b/sw/spi.h @@ -0,0 +1,9 @@ +/* Usage: + * + * #include + * #include "types.h" + * #include "spi.h" + */ + +void spiInit(void); +U8 spiTx(U8 c); diff --git a/sw/sys.h b/sw/sys.h new file mode 100644 index 0000000..1d54039 --- /dev/null +++ b/sw/sys.h @@ -0,0 +1,5 @@ +// TRIS +enum { + OUT = 0, + IN = 1, +}; diff --git a/sw/types.c b/sw/types.c new file mode 100644 index 0000000..c18392e --- /dev/null +++ b/sw/types.c @@ -0,0 +1,13 @@ +#include + +#include + +#include "types.h" + +void +incU16(U16 *x) { + x->lo++; + if (x->lo == 0u) { + x->hi++; + } +} diff --git a/sw/types.h b/sw/types.h new file mode 100644 index 0000000..ba56050 --- /dev/null +++ b/sw/types.h @@ -0,0 +1,13 @@ +/* Usage: + * + * #include + * #include "types.h" + */ + +typedef uint8_t U8; + +typedef struct { + U8 lo, hi; +} U16; + +void incU16(U16 *x); -- cgit v1.2.3