Skip to content

Commit

Permalink
runtime: bit-banged i2c support (untested)
Browse files Browse the repository at this point in the history
sbourdeauducq committed Mar 3, 2016
1 parent 73bfbe5 commit 423ca03
Showing 5 changed files with 214 additions and 2 deletions.
1 change: 1 addition & 0 deletions artiq/gateware/targets/kc705.py
Original file line number Diff line number Diff line change
@@ -353,6 +353,7 @@ def __init__(self, cpu_type="or1k", **kwargs):
i2c = platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
self.register_kernel_cpu_csrdevice("i2c")
self.config["I2C_BUS_COUNT"] = 1


def main():
2 changes: 1 addition & 1 deletion artiq/runtime/Makefile
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \
session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \
ksupport_data.o kloader.o test_mode.o main.o
OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \
bridge.o rtio.o dds.o
bridge.o rtio.o dds.o i2c.o

CFLAGS += -I$(LIBALLOC_DIRECTORY) \
-I$(MISOC_DIRECTORY)/software/include/dyld \
194 changes: 194 additions & 0 deletions artiq/runtime/i2c.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#include <generated/csr.h>

#include "rtio.h"
#include "i2c.h"


static void i2c_halfperiod()
{
timer_kernel_en_write(0);
timer_kernel_load_write(CONFIG_CLOCK_FREQUENCY/10000);
timer_kernel_reload_write(0);
timer_kernel_en_write(1);

timer_kernel_update_value_write(1);
while(timer_kernel_value_read() != 0)
timer_kernel_update_value_write(1);
}

#if (defined CONFIG_I2C_BUS_COUNT) && (CONFIG_I2C_BUS_COUNT > 0)

#define SDA_BIT (1 << (2*busno + 1))
#define SCL_BIT (1 << (2*busno))

static int i2c_sda_i(int busno)
{
if(busno >= CONFIG_I2C_BUS_COUNT)
return 1;
else
return i2c_in_read() & SDA_BIT;
}

static void i2c_sda_oe(int busno, int oe)
{
int reg;

reg = i2c_oe_read();
if(oe)
reg |= SDA_BIT;
else
reg &= ~SDA_BIT;
i2c_oe_write(reg);
}

static void i2c_sda_o(int busno, int o)
{
int reg;

reg = i2c_out_read();
if(o)
reg |= SDA_BIT;
else
reg &= ~SDA_BIT;
i2c_out_write(reg);
}

static void i2c_scl_oe(int busno, int oe)
{
int reg;

reg = i2c_oe_read();
if(oe)
reg |= SCL_BIT;
else
reg &= ~SCL_BIT;
i2c_oe_write(reg);
}

static void i2c_scl_o(int busno, int o)
{
int reg;

reg = i2c_out_read();
if(o)
reg |= SCL_BIT;
else
reg &= ~SCL_BIT;
i2c_out_write(reg);
}

#else

static int i2c_sda_i(int busno)
{
return 1;
}
static void i2c_sda_oe(int busno, int oe) {}
static void i2c_sda_o(int busno, int o) {}
static void i2c_scl_oe(int busno, int oe) {}
static void i2c_scl_o(int busno, int o) {}

#endif


int i2c_init(int busno)
{
/* Set SCL as output, and high level */
i2c_scl_o(busno, 1);
i2c_scl_oe(busno, 1);
/* Prepare a zero level on SDA so that i2c_sda_oe pulls it down */
i2c_sda_o(busno, 0);
/* Release SDA */
i2c_sda_oe(busno, 0);

/* Check the I2C bus is ready */
i2c_halfperiod();
i2c_halfperiod();
if(i2c_sda_i(busno))
return 1; /* success */
else
return 0;
}

void i2c_start(int busno)
{
/* Set SCL high then SDA low */
i2c_scl_o(busno, 1);
i2c_halfperiod();
i2c_sda_oe(busno, 1);
i2c_halfperiod();
}

void i2c_stop(int busno)
{
/* First, make sure SCL is low, so that the target releases the SDA line */
i2c_scl_o(busno, 0);
i2c_halfperiod();
/* Set SCL high then SDA high */
i2c_sda_oe(busno, 1);
i2c_scl_o(busno, 1);
i2c_halfperiod();
i2c_sda_oe(busno, 0);
i2c_halfperiod();
}

int i2c_write(int busno, char b)
{
int i;

/* MSB first */
for(i=7;i>=0;i--) {
/* Set SCL low and set our bit on SDA */
i2c_scl_o(busno, 0);
i2c_sda_oe(busno, b & (1 << i) ? 0 : 1);
i2c_halfperiod();
/* Set SCL high ; data is shifted on the rising edge of SCL */
i2c_scl_o(busno, 1);
i2c_halfperiod();
}
/* Check ack */
/* Set SCL low, then release SDA so that the I2C target can respond */
i2c_scl_o(busno, 0);
i2c_halfperiod();
i2c_sda_oe(busno, 0);
/* Set SCL high and check for ack */
i2c_scl_o(busno, 1);
i2c_halfperiod();
/* returns 1 if acked (I2C target pulled SDA low) */
return !i2c_sda_i(busno);
}

char i2c_read(int busno, int ack)
{
int i;
char b;

/* Set SCL low first, otherwise setting SDA as input may cause a transition
* on SDA with SCL high which will be interpreted as START/STOP condition.
*/
i2c_scl_o(busno, 0);
i2c_halfperiod(); /* make sure SCL has settled low */
i2c_sda_oe(busno, 0);

b = 0;
/* MSB first */
for(i=7;i>=0;i--) {
i2c_scl_o(busno, 0);
i2c_halfperiod();
/* Set SCL high and shift data */
i2c_scl_o(busno, 1);
i2c_halfperiod();
if(i2c_sda_i(busno)) b |= (1 << i);
}
/* Send ack */
/* Set SCL low and pull SDA low when acking */
i2c_scl_o(busno, 0);
if(ack)
i2c_sda_oe(busno, 1);
i2c_halfperiod();
/* then set SCL high */
i2c_scl_o(busno, 1);
i2c_halfperiod();

return b;
}
10 changes: 10 additions & 0 deletions artiq/runtime/i2c.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef __I2C_H
#define __I2C_H

int i2c_init(int busno);
void i2c_start(int busno);
void i2c_stop(int busno);
int i2c_write(int busno, char b);
char i2c_read(int busno, int ack);

#endif
9 changes: 8 additions & 1 deletion artiq/runtime/ksupport.c
Original file line number Diff line number Diff line change
@@ -13,8 +13,9 @@
#include "messages.h"
#include "bridge.h"
#include "artiq_personality.h"
#include "dds.h"
#include "rtio.h"
#include "dds.h"
#include "i2c.h"

double round(double x);

@@ -117,6 +118,12 @@ static const struct symbol runtime_exports[] = {
{"dds_batch_exit", &dds_batch_exit},
{"dds_set", &dds_set},

{"i2c_init", &i2c_init},
{"i2c_start", &i2c_start},
{"i2c_stop", &i2c_stop},
{"i2c_write", &i2c_write},
{"i2c_read", &i2c_read},

{"cache_get", &cache_get},
{"cache_put", &cache_put},

0 comments on commit 423ca03

Please sign in to comment.