From: Nathael Pajani Date: Tue, 27 Sep 2016 21:02:37 +0000 (+0200) Subject: Updated I2C driver to follow API documented here : http://wiki.techno-innov.fr/index... X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=bc750d78196c1ba6d1be65652952da0df8342ff2;p=soft%2Flpc122x%2Fcore Updated I2C driver to follow API documented here : wiki.techno-innov.fr/index.php/Technique/Logiciel/API/Use/Drivers/I2C Updated TMP101 sensor driver for API compliance Updated EEPROM driver for API compliance --- diff --git a/drivers/i2c.c b/drivers/i2c.c index f26437c..0d96e21 100644 --- a/drivers/i2c.c +++ b/drivers/i2c.c @@ -38,6 +38,7 @@ /* * I2C Bus structure * + * mode : current configuration mode : I2C_MASTER, I2C_SLAVE or I2C_MONITOR. * clock : current i2c clock. * state : global state of the i2c engine. * master_status : status returned by i2c block as found in "status" register. @@ -52,6 +53,7 @@ */ struct i2c_bus { volatile struct lpc_i2c* regs; + uint8_t mode; volatile uint32_t clock; volatile uint32_t state; volatile uint32_t master_status; @@ -70,7 +72,12 @@ struct i2c_bus { volatile uint32_t read_index; }; -static struct i2c_bus mod_i2c; +static struct i2c_bus i2c_buses[NB_I2C_BUSSES] = { + { + .regs = (struct lpc_i2c*)LPC_I2C0, + .state = I2C_OK, + }, +}; /* FIXME : Should add a field "what to do on addr NACK" to i2c_bus structure, and use @@ -86,7 +93,7 @@ static struct i2c_bus mod_i2c; void I2C_0_Handler(void) { uint8_t status; - struct i2c_bus* i2c = &mod_i2c; + struct i2c_bus* i2c = &(i2c_buses[0]); i2c->timeout = 0; @@ -271,6 +278,55 @@ void I2C_0_Handler(void) /***************************************************************************** */ /* I2C access */ /***************************************************************************** */ +static int i2c_perform_data_transfer(struct i2c_bus* i2c) +{ + int ret = 0; + + /* Start the process */ + i2c->state = I2C_BUSY; + i2c->regs->ctrl_set = I2C_START_FLAG; + /* Wait for process completion */ + do {} while (i2c->state == I2C_BUSY); + + /* Handle returned state (errors or OK) */ + switch (i2c->state) { + case I2C_OK: + case I2C_NO_DATA: + /* Return 0 : success */ + break; + case I2C_NACK: + ret = -EREMOTEIO; + break; + case I2C_ARBITRATION_LOST: + ret = -EBUSY; + break; + case I2C_BUS_ERROR: /* This one is bad ... */ + case I2C_ERROR_UNKNOWN: + default: + ret = -EIO; + break; + } + + return ret; +} + +/* Release Bus + * Some devices do not release the Bus at the end of a transaction if they don't receive + * a start condition immediately followed by a stop condition. + */ +void i2c_release_bus(uint8_t bus_num) +{ + struct i2c_bus* i2c = &(i2c_buses[0]); + /* Force device to release the bus : + * send a START followed by a STOP (initiate transmission with nul write_length) */ + i2c->state = I2C_BUSY; + i2c->write_length = 0; + i2c->regs->ctrl_set = I2C_START_FLAG; + do {} while (i2c->state == I2C_BUSY); + i2c->state = I2C_OK; +} + + /* Read * Performs a non-blocking read on the module's i2c bus. * cmd_buf : buffer containing all control byte to be sent on the i2c bus @@ -283,72 +339,45 @@ void I2C_0_Handler(void) * Upon successfull completion, returns the number of bytes read. On error, returns a negative * integer equivalent to errors from glibc. */ -int i2c_read(const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count) +int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count) { + struct i2c_bus* i2c = &(i2c_buses[0]); int ret = 0; /* Checks */ - if (mod_i2c.regs != LPC_I2C0) + if (i2c->regs != LPC_I2C0) return -EBADFD; if (cmd_buf == NULL) return -EINVAL; if ((inbuff == NULL) && (count > 0)) return -EINVAL; - if (mod_i2c.state == I2C_BUSY) { + if (i2c->state == I2C_BUSY) { return -EBUSY; } - if (mod_i2c.state != I2C_OK) { + if (i2c->state != I2C_OK) { /* What should we do ??? someone failed to reset status ? */ } - /* Set up mod_i2c for read operation */ + /* Set up i2c structure for read operation */ /* command (write) buffer */ - mod_i2c.out_buff = cmd_buf; - mod_i2c.write_length = cmd_size; + i2c->out_buff = cmd_buf; + i2c->write_length = cmd_size; /* control buffer, if any. Note that it's the only way to control * operations on modules i2C bus to simplify the interface */ - mod_i2c.repeated_start_restart = ctrl_buf; - mod_i2c.restart_after_addr = I2C_CONT; - mod_i2c.restart_after_data = 0; + i2c->repeated_start_restart = ctrl_buf; + i2c->restart_after_addr = I2C_CONT; + i2c->restart_after_data = 0; /* read buffer */ - mod_i2c.in_buff = inbuff; - mod_i2c.read_length = count; - mod_i2c.read_index = 0; + i2c->in_buff = inbuff; + i2c->read_length = count; + i2c->read_index = 0; - /* Start the process */ - mod_i2c.state = I2C_BUSY; - mod_i2c.regs->ctrl_set = I2C_START_FLAG; - /* And wait for process completion */ - do {} while (mod_i2c.state == I2C_BUSY); - - /* Handle returned state (errors or OK) */ - switch (mod_i2c.state) { - case I2C_OK: - case I2C_NO_DATA: - ret = mod_i2c.read_index; - break; - case I2C_NACK: - ret = -EREMOTEIO; - break; - case I2C_ARBITRATION_LOST: - ret = -EBUSY; - break; - case I2C_BUS_ERROR: /* This one is bad ... */ - case I2C_ERROR_UNKNOWN: - default: - ret = -EIO; - break; + ret = i2c_perform_data_transfer(i2c); + if (ret == 0) { + return i2c->read_index; } - /* Force device to release the bus : - * send a START followed by a STOP (initiate transmission with nul write_length) */ - mod_i2c.state = I2C_BUSY; - mod_i2c.write_length = 0; - mod_i2c.regs->ctrl_set = I2C_START_FLAG; - do {} while (mod_i2c.state == I2C_BUSY); - mod_i2c.state = I2C_OK; - return ret; } @@ -364,69 +393,42 @@ int i2c_read(const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* i * Upon successfull completion, returns the number of bytes written. On error, returns a negative * integer equivalent to errors from glibc. */ -int i2c_write(const void *buf, size_t count, const void* ctrl_buf) +int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf) { + struct i2c_bus* i2c = &(i2c_buses[0]); int ret = 0; /* Checks */ - if (mod_i2c.regs != LPC_I2C0) + if (i2c->regs != LPC_I2C0) return -EBADFD; if (buf == NULL) return -EINVAL; - if (mod_i2c.state == I2C_BUSY) { + if (i2c->state == I2C_BUSY) { return -EBUSY; } - if (mod_i2c.state != I2C_OK) { + if (i2c->state != I2C_OK) { /* What should we do ??? someone failed to reset status ? */ } /* Clear read information to prevent entering master receiver states */ - mod_i2c.read_length = 0; - mod_i2c.in_buff = NULL; - mod_i2c.state = I2C_BUSY; - /* Set up mod_i2c for write operation */ - mod_i2c.out_buff = buf; - mod_i2c.write_length = count; + i2c->read_length = 0; + i2c->in_buff = NULL; + i2c->state = I2C_BUSY; + /* Set up i2c_bus structure for write operation */ + i2c->out_buff = buf; + i2c->write_length = count; /* control buffer, if any. Note that it's the only way to control * operations on modules i2C bus to simplify the interface */ - mod_i2c.restart_after_addr = I2C_CONT; - mod_i2c.repeated_start_restart = ctrl_buf; - mod_i2c.restart_after_data = 0; + i2c->restart_after_addr = I2C_CONT; + i2c->repeated_start_restart = ctrl_buf; + i2c->restart_after_data = 0; - /* Start the process */ - mod_i2c.state = I2C_BUSY; - mod_i2c.regs->ctrl_set = I2C_START_FLAG; - /* Wait for process completion */ - do {} while (mod_i2c.state == I2C_BUSY); - - /* Handle returned state (errors or OK) */ - switch (mod_i2c.state) { - case I2C_OK: - case I2C_NO_DATA: - ret = mod_i2c.write_length; - break; - case I2C_NACK: - ret = -EREMOTEIO; - break; - case I2C_ARBITRATION_LOST: - ret = -EBUSY; - break; - case I2C_BUS_ERROR: /* This one is bad ... */ - case I2C_ERROR_UNKNOWN: - default: - ret = -EIO; - break; + ret = i2c_perform_data_transfer(i2c); + if (ret == 0) { + return i2c->write_length; } - /* Force device to release the bus : - * send a START followed by a STOP (initiate transmission with nul write_length) */ - mod_i2c.state = I2C_BUSY; - mod_i2c.write_length = 0; - mod_i2c.regs->ctrl_set = I2C_START_FLAG; - do {} while (mod_i2c.state == I2C_BUSY); - mod_i2c.state = I2C_OK; - return ret; } @@ -447,40 +449,47 @@ static void i2c_clock_on(uint32_t i2c_clk_freq) i2c->clk_duty_low = (scl_clk - i2c->clk_duty_high); } -void i2c_on(uint32_t i2c_clk_freq) +/* I2C on / off + * bus_num : I2C bus number to use. Ignored on this micro-controller which has only one I2C bus. + * i2c_clk_freq : I2C clock freqeuncy in Hz + * mode is one of I2C_MASTER, I2C_SLAVE or I2C_MONITOR. + * Note that only I2C_MASTER is currently supported. + */ +int i2c_on(uint8_t bus_num, uint32_t i2c_clk_freq, uint8_t mode) { - struct lpc_i2c* i2c = LPC_I2C0; + struct i2c_bus* i2c = &(i2c_buses[0]); NVIC_DisableIRQ(I2C0_IRQ); /* Power on I2C 0 block */ subsystem_power(LPC_SYS_ABH_CLK_CTRL_I2C, 1); /* Set clock */ i2c_clock_on(i2c_clk_freq); - mod_i2c.clock = i2c_clk_freq; - /* Initialize i2c_bus struct */ - memset(&mod_i2c, 0, sizeof(struct i2c_bus)); - mod_i2c.regs = (struct lpc_i2c*)LPC_I2C0; - mod_i2c.state = I2C_OK; + i2c->clock = i2c_clk_freq; /* Enable I2C */ /* FIXME: if enabling slave functions, add I2C_ASSERT_ACK flag */ - i2c->ctrl_set = (I2C_ENABLE_FLAG); + i2c->regs->ctrl_set = (I2C_ENABLE_FLAG); /* And enable interrupts */ NVIC_EnableIRQ(I2C0_IRQ); + + return 0; } -void i2c_off(void) +int i2c_off(uint8_t bus_num) { + struct i2c_bus* i2c = &(i2c_buses[0]); NVIC_DisableIRQ(I2C0_IRQ); subsystem_power(LPC_SYS_ABH_CLK_CTRL_I2C, 0); - mod_i2c.clock = 0; + i2c->clock = 0; + return 0; } /* Allow system to propagate main clock */ void i2c_clock_update(void) { - if (mod_i2c.clock) { + struct i2c_bus* i2c = &(i2c_buses[0]); + if (i2c->clock) { /* FIXME : we should stop I2C transfers, disable I2C interrupts and stop I1C clock. */ - i2c_clock_on(mod_i2c.clock); /* 6 is not a module num, nor system i2c (5) */ + i2c_clock_on(i2c->clock); /* 6 is not a module num, nor system i2c (5) */ } } diff --git a/extdrv/eeprom.c b/extdrv/eeprom.c index 9306f0b..91e2c7d 100644 --- a/extdrv/eeprom.c +++ b/extdrv/eeprom.c @@ -70,14 +70,14 @@ int eeprom_detect(uint8_t eeprom_addr) if (eeprom_addr == EEPROM_ID_SMALL_ADDR_1) { cmd_buf[0] = EEPROM_ID_SMALL_ADDR_2; } - ret = i2c_read(cmd_buf, 1, NULL, NULL, 0); + ret = i2c_read(0, cmd_buf, 1, NULL, NULL, 0); if (ret == 0) { return EEPROM_TYPE_SMALL; } /* No small eeprom ... look for big ones */ cmd_buf[0] = eeprom_addr; - ret = i2c_read(cmd_buf, 1, NULL, NULL, 0); + ret = i2c_read(0, cmd_buf, 1, NULL, NULL, 0); if (ret == 0) { return EEPROM_TYPE_BIG; } @@ -129,14 +129,14 @@ int eeprom_read(uint8_t eeprom_addr, uint32_t offset, void *buf, size_t count) cmd_buf[0] = EEPROM_ID_SMALL_ADDR_1 | ((offset & 0x700) >> 7); cmd_buf[1] = offset & 0xFF; cmd_buf[2] = EEPROM_ID_SMALL_ADDR_1 | 0x01; - ret = i2c_read(cmd_buf, CMD_BUF_SIZE - 1, ctrl_buf + 1, buf, count); + ret = i2c_read(0, cmd_buf, CMD_BUF_SIZE - 1, ctrl_buf + 1, buf, count); break; case EEPROM_TYPE_BIG: cmd_buf[0] = eeprom_addr; cmd_buf[1] = ((offset & 0xFF00) >> 8); cmd_buf[2] = offset & 0xFF; cmd_buf[3] = (eeprom_addr | 0x01); - ret = i2c_read(cmd_buf, CMD_BUF_SIZE, ctrl_buf, buf, count); + ret = i2c_read(0, cmd_buf, CMD_BUF_SIZE, ctrl_buf, buf, count); break; default: ret = -1; @@ -207,7 +207,7 @@ int eeprom_write(uint8_t eeprom_addr, uint32_t offset, const void *buf, size_t c offset += size; memcpy(full_buff, cmd, cmd_size); memcpy(full_buff + cmd_size, buf + write_count, size); - ret = i2c_write(full_buff, (cmd_size + size), NULL); + ret = i2c_write(0, full_buff, (cmd_size + size), NULL); if (ret != (cmd_size + size)) { break; @@ -215,7 +215,7 @@ int eeprom_write(uint8_t eeprom_addr, uint32_t offset, const void *buf, size_t c /* Wait for page write completion : The device does not acknoledge anything during * page write, perform page writes with no data, until it returns 1 */ do { - ret = i2c_write(full_buff, 1, NULL); + ret = i2c_write(0, full_buff, 1, NULL); } while (ret != 1); write_count += size; diff --git a/extdrv/tmp101_temp_sensor.c b/extdrv/tmp101_temp_sensor.c index e0f9d90..8677947 100644 --- a/extdrv/tmp101_temp_sensor.c +++ b/extdrv/tmp101_temp_sensor.c @@ -67,7 +67,7 @@ int tmp101_probe_sensor(struct tmp101_sensor_config* conf) /* Did we already probe the sensor ? */ if (conf->probe_ok != 1) { - conf->probe_ok = i2c_read(&cmd_buf, 1, NULL, NULL, 0); + conf->probe_ok = i2c_read(conf->bus_num, &cmd_buf, 1, NULL, NULL, 0); } return conf->probe_ok; } @@ -104,23 +104,25 @@ int tmp101_sensor_read(struct tmp101_sensor_config* conf, uint16_t* raw, int* de /* Read the requested data */ if (conf->last_accessed_register == TMP_REG_TEMPERATURE) { /* No need to switch back to temperature register */ - ret = i2c_read((cmd_buf + 2), 1, (ctrl_buf + 2), (char*)&temp, 2); + ret = i2c_read(conf->bus_num, (cmd_buf + 2), 1, (ctrl_buf + 2), (char*)&temp, 2); } else { /* Send (write) temperature register address to TMP101 internal pointer */ - ret = i2c_read(cmd_buf, CMD_BUF_SIZE, ctrl_buf, (char*)&temp, 2); + ret = i2c_read(conf->bus_num, cmd_buf, CMD_BUF_SIZE, ctrl_buf, (char*)&temp, 2); conf->last_accessed_register = TMP_REG_TEMPERATURE; } - if (ret == 2) { - if (raw != NULL) { - *raw = byte_swap_16(temp); - } - if (deci_degrees != NULL) { - *deci_degrees = tmp101_convert_to_deci_degrees(byte_swap_16(temp)); - } - return 0; + if (ret != 2) { + conf->probe_ok = 0; + return ret; } - return ret; + + if (raw != NULL) { + *raw = byte_swap_16(temp); + } + if (deci_degrees != NULL) { + *deci_degrees = tmp101_convert_to_deci_degrees(byte_swap_16(temp)); + } + return 0; } @@ -146,12 +148,13 @@ int tmp101_sensor_config(struct tmp101_sensor_config* conf) conf->actual_config = (TMP_SHUTDOWN_MODE_ON | TMP_THERMOSTAT_INTERRUPT_MODE | TMP_ALERT_POLARITY_HIGH); conf->actual_config |= (conf->resolution & (0x03 << 5)); cmd[2] = conf->actual_config; - ret = i2c_write(cmd, 3, NULL); + ret = i2c_write(conf->bus_num, cmd, 3, NULL); conf->last_accessed_register = TMP_REG_CONFIG; - if (ret == 3) { - return 0; /* Config success */ + if (ret != 3) { + conf->probe_ok = 0; + return ret; } - return ret; + return 0; /* Config success */ } /* Start a conversion when the sensor is in shutdown mode. */ @@ -166,12 +169,13 @@ int tmp101_sensor_start_conversion(struct tmp101_sensor_config* conf) cmd[2] = conf->actual_config; cmd[2] |= TMP_ONE_SHOT_TRIGGER; - ret = i2c_write(cmd, 3, NULL); + ret = i2c_write(conf->bus_num, cmd, 3, NULL); conf->last_accessed_register = TMP_REG_CONFIG; - if (ret == 3) { - return 0; /* Conversion start success */ + if (ret != 3) { + conf->probe_ok = 0; + return ret; } - return ret; + return 0; /* Conversion start success */ } diff --git a/include/drivers/i2c.h b/include/drivers/i2c.h index 520e5a6..ecce01c 100644 --- a/include/drivers/i2c.h +++ b/include/drivers/i2c.h @@ -35,12 +35,27 @@ #include "core/lpc_regs.h" +#define NB_I2C_BUSSES 1 + #define I2C_CLK_100KHz (100*1000) #define I2C_CLK_400KHz (400*1000) #define I2C_READ_BIT 0x01 #define I2C_WRITE_BIT 0x00 +enum i2c_busses { + I2C0 = 0, + I2C1, + I2C2, + I2C3, +}; + +enum i2c_modes { + I2C_MASTER = 0, + I2C_SLAVE, + I2C_MONITOR, +}; + enum i2c_conditions { I2C_CONT = 0, I2C_DO_REPEATED_START, @@ -106,7 +121,7 @@ enum i2c_state_machine_states { * -EREMOTEIO : Device did not acknowledge * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine */ -int i2c_read(const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count); +int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count); /* I2C Write * Performs a non-blocking write on the i2c bus. @@ -127,16 +142,28 @@ int i2c_read(const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* i * -EREMOTEIO : Device did not acknowledge * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine */ -int i2c_write(const void *buf, size_t count, const void* ctrl_buf); +int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf); + +/* Release Bus + * Some devices do not release the Bus at the end of a transaction if they don't receive + * a start condition immediately followed by a stop condition. + */ +void i2c_release_bus(uint8_t bus_num); /***************************************************************************** */ /* I2C Init */ /***************************************************************************** */ -void i2c_on(uint32_t i2c_clk_freq); -void i2c_off(void); +/* I2C on / off + * bus_num : I2C bus number to use. Ignored on this micro-controller which has only one I2C bus. + * i2c_clk_freq : I2C clock freqeuncy in Hz + * mode is one of I2C_MASTER, I2C_SLAVE or I2C_MONITOR. + * Note that only I2C_MASTER is currently supported. + */ +int i2c_on(uint8_t bus_num, uint32_t i2c_clk_freq, uint8_t mode); +int i2c_off(uint8_t bus_num); /* Allow system to propagate main clock */ void i2c_clock_update(void); diff --git a/include/extdrv/tmp101_temp_sensor.h b/include/extdrv/tmp101_temp_sensor.h index 6fd6ec3..a3413f1 100644 --- a/include/extdrv/tmp101_temp_sensor.h +++ b/include/extdrv/tmp101_temp_sensor.h @@ -57,6 +57,7 @@ */ struct tmp101_sensor_config { uint8_t addr; + uint8_t bus_num; uint8_t probe_ok; uint8_t actual_config; int last_accessed_register;