eeprom-24lc64-clone/main.c
Daniele Lacamera 0c536ee600 Code.
2021-01-27 11:02:29 +01:00

429 lines
11 KiB
C

/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unicore-mx/stm32/rcc.h>
#include <unicore-mx/stm32/gpio.h>
#include <string.h>
#include <stdio.h>
#define DMB() __asm__ volatile ("dmb")
#define FLASH_BASE (0x40022000)
#define FLASH_ACR (*(volatile uint32_t *)(FLASH_BASE + 0x00))
#define LED_GREEN_PIN GPIO5
#define LED_GREEN_PORT GPIOA
#define LED_RED_PIN GPIO4
#define LED_RED_PORT GPIOA
#define I2C3_SDA_PIN GPIO1
#define I2C3_SDA_PORT GPIOC
#define I2C3_SCL_PIN GPIO0
#define I2C3_SCL_PORT GPIOC
#define I2C3_AF (7)
#define I2C3_BASE 0x40007800
#define I2C3 I2C3_BASE
#define I2C I2C3
#define I2C3_CR1 (*(volatile uint32_t *)(I2C3))
#define I2C3_CR2 (*(volatile uint32_t *)(I2C3 + 0x04))
#define I2C3_OAR1 (*(volatile uint32_t *)(I2C3 + 0x08))
#define I2C3_OAR2 (*(volatile uint32_t *)(I2C3 + 0x0c))
#define I2C3_TIMINGR (*(volatile uint32_t *)(I2C3 + 0x10))
#define I2C3_SR1 (*(volatile uint32_t *)(I2C3 + 0x14))
#define I2C3_ISR (*(volatile uint32_t *)(I2C3 + 0x18))
#define I2C3_ICR (*(volatile uint32_t *)(I2C3 + 0x1C))
#define I2C3_TXDR (*(volatile uint32_t *)(I2C3 + 0x28))
#define I2C3_RXDR (*(volatile uint32_t *)(I2C3 + 0x24))
#define I2C_CR1_ENABLE (1 << 0)
#define I2C_CR1_DNF (1 << 8)
#define I2C_CR1_ANFOFF (1 << 12)
#define I2C_CR1_NOSTRETCH (1 << 17)
#define I2C_CR2_START (1 << 13)
#define I2C_CR2_STOP (1 << 14)
#define I2C_CR2_RD_WRN (1 << 10)
#define I2C_CR2_NBYTES_SHIFT (16)
#define I2C_CR2_RELOAD (1 << 24)
#define I2C_CR2_AUTOEND (1 << 25)
#define I2C_CR1_ACK (1 << 10)
#define I2C_CR2_FREQ_MASK (0x3ff)
#define I2C_CCR_MASK (0xfff)
#define I2C_ISR_TXIS (1 << 1)
#define I2C_ISR_TXE (1 << 0)
#define I2C_ISR_RXNE (1 << 2)
#define I2C_ISR_NACKF (1 << 4)
#define I2C_ISR_TCR (1 << 7)
#define I2C_ICR_ARLOCF (1 << 9)
#define I2C_ICR_BERRCF (1 << 8)
#define I2C_ICR_ADDRCF (1 << 3)
#define I2C_ICR_NACKF (1 << 4)
#define I2C_ICR_STOPF (1 << 5)
#define I2C_ICR_ALLCF (I2C_ICR_NACKF | I2C_ICR_ARLOCF | I2C_ICR_BERRCF | I2C_ICR_ADDRCF )
#define RCC_CFGR_PLLDIV2 (0x01 << 22)
#define RCC_CFGR_PLLMUL4 (0x01 << 18)
#define RCC_PRESCALER_DIV_NONE 0
#define ROM_SRC 0x50
#define ROM_DST 0x51
#define ROM_TEST_NACK 0x52
#define ROM_PAGE_SIZE (32)
#define ROM_SIZE (8192)
#define ROM_PAGES (ROM_SIZE / ROM_PAGE_SIZE)
static void wait_a_bit(void)
{
volatile int i;
for (i = 0; i < 30000; i++) { /* Wait a bit. */
__asm__("nop");
}
}
static void i2c_acquire(void)
{
rcc_periph_clock_enable(RCC_I2C3);
I2C3_CR1 |= I2C_CR1_ENABLE;
}
static void i2c_release(void)
{
I2C3_CR1 &= ~(I2C_CR1_ENABLE);
rcc_periph_clock_disable(RCC_I2C3);
}
static void i2c_setup(void)
{
/* 400KHz */
uint32_t presc = 0, scll = 0x2E, sclh = 0x11, sdadel = 0x01, scldel = 0x0b;
/* 100KHz */
//uint32_t presc = 1, scll = 0x56, sclh = 0x3e, sdadel = 0x01, scldel = 0x0a;
rcc_periph_clock_enable(RCC_GPIOC);
gpio_set_output_options(GPIOC, GPIO_OTYPE_OD, GPIO_OSPEED_HIGH, I2C3_SDA_PIN);
gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP,
I2C3_SDA_PIN | I2C3_SCL_PIN);
gpio_set_af(GPIOC, I2C3_AF, I2C3_SDA_PIN | I2C3_SCL_PIN);
i2c_acquire();
/* Disable */
I2C3_CR1 &= ~(I2C_CR1_ENABLE);
/* configure analog noise filter */
I2C3_CR1 |= I2C_CR1_ANFOFF;
/* configure digital noise filter */
I2C3_CR1 |= I2C_CR1_DNF;
/* set timing registers */
I2C3_TIMINGR = (presc << 28) | (scldel << 20) | (sdadel << 16) | (sclh << 8) | scll;
/* configure clock stretching */
I2C3_CR1 &= ~(I2C_CR1_NOSTRETCH);
/* Clear interrupt */
I2C3_ICR |= I2C_ICR_ALLCF;
i2c_release();
}
static void gpio_setup(void)
{
/* Enable GPIO clock. */
rcc_periph_clock_enable(RCC_GPIOA);
/* set pins to output mode, push pull */
gpio_mode_setup(LED_GREEN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_GREEN_PIN);
gpio_mode_setup(LED_RED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_RED_PIN);
}
static void wait_sent(void)
{
volatile uint32_t sr1;
do {
sr1 = I2C3_ISR;
} while ((sr1 & (I2C_ISR_TXE)) == 0);
}
static void wait_reload(void)
{
volatile uint32_t sr1;
do {
sr1 = I2C3_ISR;
} while ((sr1 & (I2C_ISR_TCR)) == 0);
}
static void wait_start(void)
{
volatile uint32_t cr2;
do {
cr2 = I2C3_CR2;
} while ((cr2 & (I2C_CR2_START)) != 0);
}
static void wait_stop(void)
{
volatile uint32_t cr2;
do {
cr2 = I2C3_CR2;
} while ((cr2 & (I2C_CR2_STOP)) != 0);
}
static void wait_rxd(void)
{
volatile uint32_t sr1;
do {
sr1 = I2C3_ISR;
} while ((sr1 & (I2C_ISR_RXNE)) == 0);
}
static void clear_err(void)
{
I2C3_ICR |= I2C_ICR_ALLCF;
}
static void clear_stop(void)
{
I2C3_ICR |= I2C_ICR_STOPF;
}
static int eeprom_write_page(uint8_t rom, uint16_t address, const uint8_t *buf)
{
volatile uint32_t sr1, sr2, cr2;
int i;
if (rom != ROM_DST)
return 0;
clear_err();
I2C3_CR2 = ((ROM_PAGE_SIZE + 2) << I2C_CR2_NBYTES_SHIFT) | I2C_CR2_AUTOEND;
I2C3_CR2 |= (rom << 1);
I2C3_CR2 |= I2C_CR2_START;
wait_start();
I2C3_TXDR = ((uint8_t)((address >> 8) & (0xFF)));
wait_sent();
I2C3_TXDR = ((uint8_t)((address & 0xFF)));
for (i = 0; i < ROM_PAGE_SIZE; i++) {
wait_sent();
I2C3_TXDR = buf[i];
}
wait_a_bit();
clear_stop();
return i;
}
static int eeprom_read_page(uint8_t rom, uint16_t address, uint8_t *buf)
{
volatile uint32_t sr1, sr2;
int i;
clear_err();
I2C3_CR2 = (2 << I2C_CR2_NBYTES_SHIFT) | I2C_CR2_RELOAD;
I2C3_CR2 |= (rom << 1);
I2C3_CR2 |= I2C_CR2_START;
wait_start();
I2C3_TXDR = ((uint8_t)((address & 0xFF00) >> 8));
wait_sent();
clear_err();
I2C3_CR2 = (rom << 1);
I2C3_CR2 |= I2C_CR2_RD_WRN;
I2C3_CR2 |= (ROM_PAGE_SIZE << I2C_CR2_NBYTES_SHIFT);
I2C3_CR2 |= I2C_CR2_START;
I2C3_TXDR = ((uint8_t)((address & 0xFF)));
//wait_sent();
wait_start();
clear_err();
for (i = 0; i < ROM_PAGE_SIZE; i++) {
//I2C3_TXDR = 0xFF;
//wait_sent();
wait_rxd();
buf[i] = I2C3_RXDR;
}
I2C3_CR2 |= I2C_CR2_STOP;
clear_stop();
return i;
}
static int eeprom_read_cur(uint8_t rom, uint8_t *buf)
{
int i;
retry:
clear_err();
I2C3_CR2 = (ROM_PAGE_SIZE << I2C_CR2_NBYTES_SHIFT);
I2C3_CR2 |= (rom << 1) | I2C_CR2_RD_WRN;
I2C3_CR2 |= I2C_CR2_START;
wait_start();
if (I2C3_ISR & I2C_ISR_NACKF) {
wait_a_bit();
clear_stop();
goto retry;
}
for (i = 0; i < ROM_PAGE_SIZE; i++) {
wait_rxd();
buf[i] = I2C3_RXDR;
}
return i;
}
static void flash_set_waitstates(unsigned int waitstates)
{
if (waitstates && ((FLASH_ACR & 1) == 0))
FLASH_ACR |= 1;
if (!waitstates && ((FLASH_ACR & 1) == 1))
FLASH_ACR &= 1;
while ((FLASH_ACR & 1) != waitstates)
;
}
static void clock_pll_on(void)
{
uint32_t reg32;
uint32_t cpu_freq, hsi_freq, hpre, ppre1, ppre2, flash_waitstates;
/* Enable Power controller */
rcc_periph_clock_enable(RCC_PWR);
/* Select clock parameters (CPU Speed = 32MHz) */
cpu_freq = 32000000;
hsi_freq = 16000000;
hpre = RCC_PRESCALER_DIV_NONE;
ppre1 = RCC_PRESCALER_DIV_NONE;
ppre2 = RCC_PRESCALER_DIV_NONE;
flash_waitstates = 1;
flash_set_waitstates(flash_waitstates);
/* Enable internal high-speed oscillator. */
RCC_CR |= RCC_CR_HSI16ON;
DMB();
while ((RCC_CR & RCC_CR_HSI16RDY) == 0) {};
/* Select HSI as SYSCLK source. */
reg32 = RCC_CFGR;
reg32 &= ~((1 << 1) | (1 << 0));
RCC_CFGR = (reg32 | RCC_CFGR_SW_HSI16);
DMB();
/*
* Set prescalers for AHB, ADC, ABP1, ABP2.
*/
reg32 = RCC_CFGR;
reg32 &= ~(0xF << 4);
RCC_CFGR = (reg32 | (hpre << 4));
DMB();
reg32 = RCC_CFGR;
reg32 &= ~(0x07 << 8);
RCC_CFGR = (reg32 | (ppre1 << 8));
DMB();
reg32 &= ~(0x07 << 11);
RCC_CFGR = (reg32 | (ppre2 << 11));
DMB();
reg32 &= ~(0x0F << 18);
RCC_CFGR = (reg32 | RCC_CFGR_PLLMUL4);
DMB();
reg32 &= ~(0x03 << 22);
RCC_CFGR = (reg32 | RCC_CFGR_PLLDIV2);
DMB();
/* Enable PLL oscillator and wait for it to stabilize. */
RCC_CR |= RCC_CR_PLLON;
DMB();
while ((RCC_CR & RCC_CR_PLLRDY) == 0) {};
/* Select PLL as SYSCLK source. */
reg32 = RCC_CFGR;
reg32 &= ~((1 << 1) | (1 << 0));
RCC_CFGR = (reg32 | RCC_CFGR_SW_PLL);
DMB();
/* Wait for PLL clock to be selected. */
while (((RCC_CFGR >> 2) & 0x03) != RCC_CFGR_SW_PLL)
;
}
static uint8_t cache[ROM_PAGE_SIZE];
static uint8_t vcache[ROM_PAGE_SIZE];
int main(void)
{
int i;
clock_pll_on();
gpio_setup();
gpio_set(LED_RED_PORT, LED_RED_PIN);
i2c_setup();
wait_a_bit();
wait_a_bit();
wait_a_bit();
i2c_acquire();
#if 0
/* TEST - fill the DST */
for (i = 0; i < ROM_PAGES; i++) {
memset(cache, (uint8_t)i, 32);
eeprom_write_page(ROM_DST,i * ROM_PAGE_SIZE,cache);
}
#endif
memset(cache, 0, 32);
for (i = 0; i < ROM_PAGES; i++) {
/* Green: Read */
gpio_set(LED_RED_PORT, LED_GREEN_PIN);
gpio_clear(LED_GREEN_PORT, LED_GREEN_PIN);
eeprom_read_page(ROM_SRC, i * ROM_PAGE_SIZE, cache);
eeprom_read_page(ROM_DST, i * ROM_PAGE_SIZE, vcache);
if(memcmp(vcache, cache, ROM_PAGE_SIZE) == 0) {
/* Skip sector, already matching. */
continue;
}
/* Red: Write */
gpio_set(LED_GREEN_PORT, LED_GREEN_PIN);
gpio_clear(LED_RED_PORT, LED_RED_PIN);
eeprom_write_page(ROM_DST, i * ROM_PAGE_SIZE, cache);
/* Both: verify */
gpio_clear(LED_GREEN_PORT, LED_GREEN_PIN);
eeprom_read_page(ROM_DST, i * ROM_PAGE_SIZE, vcache);
if(memcmp(vcache, cache, ROM_PAGE_SIZE) != 0) {
while(1) {
/* blink red: panic */
gpio_toggle(LED_RED_PORT, LED_RED_PIN);
for (i = 0; i < 10; i++)
wait_a_bit();
}
}
gpio_set(LED_GREEN_PORT, LED_GREEN_PIN);
gpio_set(LED_RED_PORT, LED_RED_PIN);
}
i2c_release();
while (1) {
/* blink green: success */
gpio_set(LED_RED_PORT, LED_RED_PIN);
gpio_toggle(LED_GREEN_PORT, LED_GREEN_PIN);
for (i = 0; i < 10; i++)
wait_a_bit();
}
return 0;
}