Files
simtrace2/firmware/libboard/octsimtest/source/i2c.c
Oliver Smith f721e69bc1 treewide: remove FSF address
Remove the paragraph about writing to the Free Software Foundation's
mailing address. The FSF has changed addresses in the past, and may do
so again. In 2021 this is not useful, let's rather have a bit less
boilerplate at the start of source files.

Change-Id: Ie0a3b2273383adbb3303faffd6ff96be7f4cae99
2021-12-14 11:47:21 +01:00

222 lines
3.7 KiB
C

/* I2C EEPROM memory read and write utilities
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*/
#include "board.h"
#include <stdbool.h>
/* Low-Level I2C Routines */
static const Pin pin_sda = {PIO_PA30, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_OPENDRAIN };
static const Pin pin_sda_in = {PIO_PA30, PIOA, ID_PIOA, PIO_INPUT, PIO_DEFAULT };
static const Pin pin_scl = {PIO_PA31, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_OPENDRAIN };
static void i2c_delay()
{
volatile int v;
int i;
/* 100 cycles results in SCL peak length of 44us, so it's about
* 440ns per cycle here */
for (i = 0; i < 14; i++) {
v = 0;
}
}
void i2c_pin_init(void)
{
PIO_Configure(&pin_scl, PIO_LISTSIZE(pin_scl));
PIO_Configure(&pin_sda, PIO_LISTSIZE(pin_sda));
}
static void set_scl(void)
{
PIO_Set(&pin_scl);
i2c_delay();
}
static void set_sda(void)
{
PIO_Set(&pin_sda);
i2c_delay();
}
static void clear_scl(void)
{
PIO_Clear(&pin_scl);
i2c_delay();
}
static void clear_sda(void)
{
PIO_Clear(&pin_sda);
i2c_delay();
}
static bool read_sda(void)
{
bool ret;
PIO_Configure(&pin_sda_in, PIO_LISTSIZE(pin_sda_in));
if (PIO_Get(&pin_sda_in))
ret = true;
else
ret = false;
PIO_Configure(&pin_sda, PIO_LISTSIZE(pin_sda));
return ret;
}
/* Core I2C Routines */
static bool i2c_started = false;
static void i2c_start_cond(void)
{
if (i2c_started) {
set_sda();
set_scl();
}
clear_sda();
i2c_delay();
clear_scl();
i2c_started = true;
}
void i2c_stop_cond(void)
{
clear_sda();
set_scl();
set_sda();
i2c_delay();
i2c_started = false;
}
static void i2c_write_bit(bool bit)
{
if (bit)
set_sda();
else
clear_sda();
i2c_delay(); // ?
set_scl();
clear_scl();
}
static bool i2c_read_bit(void)
{
bool bit;
set_sda();
set_scl();
bit = read_sda();
clear_scl();
return bit;
}
bool i2c_write_byte(bool send_start, bool send_stop, uint8_t byte)
{
uint8_t bit;
bool nack;
if (send_start)
i2c_start_cond();
for (bit = 0; bit < 8; bit++) {
i2c_write_bit((byte & 0x80) != 0);
byte <<= 1;
}
nack = i2c_read_bit();
if (send_stop)
i2c_stop_cond();
return nack;
}
uint8_t i2c_read_byte(bool nack, bool send_stop)
{
uint8_t byte = 0;
uint8_t bit;
for (bit = 0; bit < 8; bit++) {
byte = (byte << 1) | i2c_read_bit();
}
i2c_write_bit(nack);
if (send_stop)
i2c_stop_cond();
return byte;
}
/* EEPROM related code */
int eeprom_write_byte(uint8_t slave, uint8_t addr, uint8_t byte)
{
bool nack;
WDT_Restart(WDT);
/* Write slave address */
nack = i2c_write_byte(true, false, slave << 1);
if (nack)
goto out_stop;
nack = i2c_write_byte(false, false, addr);
if (nack)
goto out_stop;
nack = i2c_write_byte(false, true, byte);
if (nack)
goto out_stop;
/* Wait tWR time to ensure EEPROM is writing correctly (tWR = 5 ms for AT24C02) */
mdelay(5);
out_stop:
i2c_stop_cond();
if (nack)
return -1;
else
return 0;
}
int eeprom_read_byte(uint8_t slave, uint8_t addr)
{
bool nack;
WDT_Restart(WDT);
/* dummy write cycle */
nack = i2c_write_byte(true, false, slave << 1);
if (nack)
goto out_stop;
nack = i2c_write_byte(false, false, addr);
if (nack)
goto out_stop;
/* Re-start with read */
nack = i2c_write_byte(true, false, (slave << 1) | 1);
if (nack)
goto out_stop;
return i2c_read_byte(true, true);
out_stop:
i2c_stop_cond();
if (nack)
return -1;
else
return 0;
}