From 6474598c655a055b2155d408284b40b11e9fe27b Mon Sep 17 00:00:00 2001 From: Hagen Kaye Date: Sat, 28 Dec 2013 19:23:33 -0500 Subject: [PATCH] Added i2c python interface object --- stm/i2c.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ stm/i2c.h | 1 + 2 files changed, 365 insertions(+) create mode 100644 stm/i2c.c create mode 100644 stm/i2c.h diff --git a/stm/i2c.c b/stm/i2c.c new file mode 100644 index 000000000..55a5517a1 --- /dev/null +++ b/stm/i2c.c @@ -0,0 +1,364 @@ +#include + +#include +#include +#include + +#include "misc.h" +#include "systick.h" +#include "mpconfig.h" +#include "obj.h" + +typedef enum { + PYB_I2C_1 = 0, + PYB_I2C_2 = 1, +} pyb_i2c_t; + +typedef enum { + I2C_STATE_IDLE = 0, + I2C_STATE_WRITE = 1, + I2C_STATE_READ = 2, +} i2c_state_t; + +// set to true if the port has already been initialized +bool i2c1_port_initialized = false; +bool i2c2_port_initialized = false; + +static I2C_TypeDef * _i2c_port_addr(pyb_i2c_t i2c_port) +{ + if (i2c_port == PYB_I2C_1) + return I2C1; + if (i2c_port == PYB_I2C_2) + return I2C2; + return NULL; +} + +// todo - perhaps there should be some global resource management for gpio +// this function would fail if the i2c pins have already been defined for +// use by another python object +// as it is, this always returns true (unless i2c_port is invalid) +static bool _i2c_init (pyb_i2c_t i2c_port) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + I2C_TypeDef *i2c = _i2c_port_addr(i2c_port); + if (i2c == NULL) + return false; + + if (i2c_port == PYB_I2C_1) { + if (i2c1_port_initialized == true) return true; + RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // enable I2C1 + + // PB6=SCL, PB7=SDA + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + // alternate functions for SCL and SDA + GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); + GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); + + + i2c1_port_initialized = true; + } + + if (i2c_port == PYB_I2C_2) { + if (i2c2_port_initialized == true) return true; + RCC->APB1ENR |= RCC_APB1ENR_I2C2EN; // enable I2C2 + + // PB10=SCL, PB11=SDA + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + // alternate functions for SCL and SDA + GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_I2C2); + GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_I2C2); + + + i2c2_port_initialized = true; + } + + // get clock speeds + RCC_ClocksTypeDef rcc_clocks; + RCC_GetClocksFreq(&rcc_clocks); + + // disable the I2C peripheral before we configure it + i2c->CR1 &= ~I2C_CR1_PE; + + // program peripheral input clock + i2c->CR2 = 4; // no interrupts; 4 MHz (hopefully!) (could go up to 42MHz) + + // configure clock control reg + uint32_t freq = rcc_clocks.PCLK1_Frequency / (100000 << 1); // want 100kHz, this is the formula for freq + i2c->CCR = freq; // standard mode (speed), freq calculated as above + + // configure rise time reg + i2c->TRISE = (rcc_clocks.PCLK1_Frequency / 1000000) + 1; // formula for trise, gives maximum rise time + + // enable the I2C peripheral + i2c->CR1 |= I2C_CR1_PE; + + + return true; +} + +static uint32_t _i2c_get_sr(pyb_i2c_t i2c_port) { + // must read SR1 first, then SR2, as the read can clear some flags + I2C_TypeDef *i2c = _i2c_port_addr(i2c_port); + if (i2c == NULL) return 0; + + uint32_t sr1 = i2c->SR1; + uint32_t sr2 = i2c->SR2; + return (sr2 << 16) | sr1; +} + +static bool _i2c_restart(pyb_i2c_t i2c_port, uint8_t addr, int write) { + I2C_TypeDef *i2c = _i2c_port_addr(i2c_port); + if (i2c == NULL) return false; + + // send start condition + i2c->CR1 |= I2C_CR1_START; + + // wait for BUSY, MSL and SB --> Slave has acknowledged start condition + uint32_t timeout = 1000000; + while ((_i2c_get_sr(i2c_port) & 0x00030001) != 0x00030001) { + if (--timeout == 0) { + //printf("timeout in _i2c_restart\n"); + return false; + } + } + + if (write) { + // send address and write bit + i2c->DR = (addr << 1) | 0; + // wait for BUSY, MSL, ADDR, TXE and TRA + timeout = 1000000; + while ((_i2c_get_sr(i2c_port) & 0x00070082) != 0x00070082) { + if (--timeout == 0) { + //printf("timeout in _i2c_restart write\n"); + return false; + } + } + } else { + // send address and read bit + i2c->DR = (addr << 1) | 1; + // wait for BUSY, MSL and ADDR flags + timeout = 1000000; + while ((_i2c_get_sr(i2c_port) & 0x00030002) != 0x00030002) { + if (--timeout == 0) { + //printf("timeout in _i2c_restart read\n"); + return false; + } + } + } + return true; +} + +static bool _i2c_send_byte(pyb_i2c_t i2c_port, uint8_t data) { + I2C_TypeDef *i2c = _i2c_port_addr(i2c_port); + if (i2c == NULL) return false; + + // send byte + i2c->DR = data; + // wait for TRA, BUSY, MSL, TXE and BTF (byte transmitted) + uint32_t timeout = 1000000; + while ((_i2c_get_sr(i2c_port) & 0x00070084) != 0x00070084) { + if (--timeout == 0) { + //printf("timeout in _i2c_send_byte\n"); + return false; + } + } + return true; +} + +static uint8_t _i2c_read_ack(pyb_i2c_t i2c_port) { + I2C_TypeDef *i2c = _i2c_port_addr(i2c_port); + if (i2c == NULL) return 0; + + // enable ACK of received byte + i2c->CR1 |= I2C_CR1_ACK; + // wait for BUSY, MSL and RXNE (byte received) + uint32_t timeout = 1000000; + while ((_i2c_get_sr(i2c_port) & 0x00030040) != 0x00030040) { + if (--timeout == 0) { + //printf("timeout in _i2c_read_ack\n"); + break; + } + } + // read and return data + uint8_t data = i2c->DR; + return data; +} + +static uint8_t _i2c_read_nack(pyb_i2c_t i2c_port) { + I2C_TypeDef *i2c = _i2c_port_addr(i2c_port); + if (i2c == NULL) return 0; + + // disable ACK of received byte (to indicate end of receiving) + i2c->CR1 &= (uint16_t)~((uint16_t)I2C_CR1_ACK); + // last byte should apparently also generate a stop condition + i2c->CR1 |= I2C_CR1_STOP; + // wait for BUSY, MSL and RXNE (byte received) + uint32_t timeout = 1000000; + while ((_i2c_get_sr(i2c_port) & 0x00030040) != 0x00030040) { + if (--timeout == 0) { + //printf("timeout in _i2c_read_nack\n"); + break; + } + } + // read and return data + uint8_t data = i2c->DR; + return data; +} + +static bool _i2c_start(pyb_i2c_t i2c_port) { + I2C_TypeDef *i2c = _i2c_port_addr(i2c_port); + if (i2c == NULL) return false; + + // wait until I2C is not busy + uint32_t timeout = 1000000; + while (i2c->SR2 & I2C_SR2_BUSY) { + if (--timeout == 0) { + return false; + } + } + return true; +} + +static void _i2c_stop(pyb_i2c_t i2c_port) { + I2C_TypeDef *i2c = _i2c_port_addr(i2c_port); + if (i2c == NULL) return; + + // send stop condition + i2c->CR1 |= I2C_CR1_STOP; +} + +/******************************************************************************/ +/* Micro Python bindings */ + +typedef struct _pyb_i2c_obj_t { + mp_obj_base_t base; + pyb_i2c_t i2c_port; + int i2c_addr; + i2c_state_t i2c_state; +} pyb_i2c_obj_t; + +void i2c_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) { + pyb_i2c_obj_t *self = self_in; + print(env, "", (unsigned int)self->i2c_port, (unsigned int)self->i2c_addr); +} + +// calls _i2c_start with write=0,1 depending on LSB of i2c_addr +mp_obj_t i2c_obj_start(mp_obj_t self_in) { + pyb_i2c_obj_t *self = self_in; + if (self->i2c_state != I2C_STATE_IDLE) { + _i2c_stop(self->i2c_port); + self->i2c_state = I2C_STATE_IDLE; + } + if (_i2c_start(self->i2c_port) == true) + return mp_const_true; + return mp_const_false; +} + +mp_obj_t i2c_obj_write(mp_obj_t self_in, mp_obj_t data_in) { + pyb_i2c_obj_t *self = self_in; + if (self->i2c_state != I2C_STATE_WRITE) { + if (_i2c_restart(self->i2c_port, self->i2c_addr, 1) == false) { + _i2c_stop(self->i2c_port); + self->i2c_state = I2C_STATE_IDLE; + return mp_const_false; + } + self->i2c_state = I2C_STATE_WRITE; + } + uint8_t data = mp_obj_get_int(data_in); + if (_i2c_send_byte(self->i2c_port, data) == false) + return mp_const_false; + return mp_const_true; +} + +mp_obj_t i2c_obj_read(mp_obj_t self_in) { + pyb_i2c_obj_t *self = self_in; + if (self->i2c_state != I2C_STATE_READ) { + if (_i2c_restart(self->i2c_port, self->i2c_addr, 0) == false) { + _i2c_stop(self->i2c_port); + self->i2c_state = I2C_STATE_IDLE; + return mp_const_false; + } + self->i2c_state = I2C_STATE_READ; + } + uint8_t data = _i2c_read_ack(self->i2c_port); + return mp_obj_new_int(data); +} + +mp_obj_t i2c_obj_readAndStop(mp_obj_t self_in) { + pyb_i2c_obj_t *self = self_in; + if (self->i2c_state != I2C_STATE_READ) { + if (_i2c_restart(self->i2c_port, self->i2c_addr, 0) == false) { + _i2c_stop(self->i2c_port); + self->i2c_state = I2C_STATE_IDLE; + return mp_const_false; + } + } + uint8_t data = _i2c_read_nack(self->i2c_port); + self->i2c_state = I2C_STATE_IDLE; + return mp_obj_new_int(data); +} + +mp_obj_t i2c_obj_stop(mp_obj_t self_in) { + pyb_i2c_obj_t *self = self_in; + _i2c_stop(self->i2c_port); + self->i2c_state = I2C_STATE_IDLE; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(i2c_obj_start_obj, i2c_obj_start); +static MP_DEFINE_CONST_FUN_OBJ_2(i2c_obj_write_obj, i2c_obj_write); +static MP_DEFINE_CONST_FUN_OBJ_1(i2c_obj_read_obj, i2c_obj_read); +static MP_DEFINE_CONST_FUN_OBJ_1(i2c_obj_readAndStop_obj, i2c_obj_readAndStop); +static MP_DEFINE_CONST_FUN_OBJ_1(i2c_obj_stop_obj, i2c_obj_stop); + +static const mp_obj_type_t i2c_obj_type = { + { &mp_const_type }, + "I2C", + i2c_obj_print, // print + NULL, // call_n + NULL, // unary_op + NULL, // binary_op + NULL, // getiter + NULL, // iternext + { // method list + { "start", &i2c_obj_start_obj }, + { "write", &i2c_obj_write_obj }, + { "read", &i2c_obj_read_obj }, + { "readAndStop", &i2c_obj_readAndStop_obj }, + { "stop", &i2c_obj_stop_obj }, + { NULL, NULL }, + } +}; + +// create the I2C object +// currently support either I2C1 (i2c_id = 0) or I2C2 (i2c_id = 1) + +mp_obj_t pyb_I2C(mp_obj_t i2c_id, mp_obj_t i2c_addr) { + pyb_i2c_t i2c_port; + switch(mp_obj_get_int(i2c_id)) { + case 0: i2c_port = PYB_I2C_1; break; + case 1: i2c_port = PYB_I2C_2; break; + default: return mp_const_none; + } + if (_i2c_init(i2c_port) == false) + return mp_const_none; + pyb_i2c_obj_t *o = m_new_obj(pyb_i2c_obj_t); + o->base.type = &i2c_obj_type; + o->i2c_port = i2c_port; + o->i2c_addr = mp_obj_get_int(i2c_addr); + o->i2c_state = I2C_STATE_IDLE; + return o; +} diff --git a/stm/i2c.h b/stm/i2c.h new file mode 100644 index 000000000..eacb7528c --- /dev/null +++ b/stm/i2c.h @@ -0,0 +1 @@ +mp_obj_t pyb_I2C(mp_obj_t i2c_id, mp_obj_t i2c_addr);