From 70d329d3080c1d7ae187a057a3fc59c2899ddadf Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Thu, 9 Aug 2018 11:39:34 +0100 Subject: [PATCH 01/45] docs(hal-i2c): Add initial design document for the I2C HAL API overhaul --- hal/rfcs/0001-i2c-overhaul.md | 374 ++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 hal/rfcs/0001-i2c-overhaul.md diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md new file mode 100644 index 00000000000..96716d752a5 --- /dev/null +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -0,0 +1,374 @@ +# I2C Design Document + + +## Description + +This update to the I2C HAL API is the first stage in an initiative to standardise and gain consistency across existing and future HAL APIs and their implementations. + +## Motivation + +Many of the current HAL API interfaces, including the I2C API, do not provide a clear, concrete specification to develop implementations against. As a result, the existing implementations lack consistency and do not have uniform behaviour across all partner boards. + +The inconsistencies in the current implementations due to poorly defined specifications are listed below, these will be fixed as part of this overhaul. + +As well as fixing existing inconsistencies there are a few unimplemented features that have been requested from the API, or unused features that have been overlooked, these will be added and removed respectively. + +Additionally, since the original implementations were added for the API a large number of additional boards have been supported, the overhaul to the API allows partners the opportunity to consolidate the implementations for a family of boards into a single implementation, which will reduce the development overhead in future if any changes are made down the line. + +### Inconsistencies + +- The implementation for `i2c_reset` varies across all platforms. + + The purpose of this function is to add a method of resetting the i2c bus. Most platform implementations do not implement this function correctly, either the implementation acts as a NOP and does nothing, or it calls `i2c_stop` which already has a functional representation in the API and is designed to perform a different action. + +- The implementation of the `i2c_start` and `i2c_stop` functions are not well defined. + + The return value of the function is not defined in the header file, the value has been left open to interpretation by the partner implementations, for example, some implementations return 0 on all conditions. Some implementations have added timeouts to the functionality of the function and return 1 on timeouts. This needs to be defined correctly to be uniform across all implementations. + +- The `i2c_slave_address` function has a parameter called `mask`, this parameter is listed as unused but some implementations use it to mask the `address` parameter. + + This parameter should be removed as it is not required by the API. It can be assumed that the address will be masked to exclude the read/write bit from the address, the behaviour of this does not need to change. + +- The `i2c_slave_address` function has a parameter called `idx`, this parameter is listed as unused but is utilised in some API implementations. + + This parameter is not required by the API, it should be removed to keep behaviour uniform across all platforms. + +- The implementation of the `i2c_byte_write` differs between platforms. + + The API documentation indicates that the return value should be 0 on success, 1 on NAK, 2 on timeout. Not all platforms return a value or handle a timeout. Some platforms return a completely different set of status, the Atmel implementation returns an `I2C_ERROR ` which does not map to the documented return value. + +- The return value of the `i2c_start` and `i2c_stop` functions are not documented. + + The function returns an integer but there is no documentation to suggest what the return value should be. Some implementations return 0 in all cases, and some use it to return error values, if this function is to return a value, the values should be standardised. + +- Some platforms overload the `i2c_slave_read`, `i2c_slave_write`, `i2c_read`, and `i2c_write` return values with statuses that aren't documented. + + These functions are documented to return the number of bytes written or read. Some implementations will return a non-zero value if the write/read fails, this is not documented anywhere and the behaviour is not common across all implementations. + +- Some platforms overload the `i2c_read` and `i2c_write` return values with statuses that aren't documented. + + The functions are documented to return the number of bytes written or read. Some implementations will return a non-zero value if the write/read fails, this is not documented anywhere and the behaviour is not common across all implementations. + +- `i2c_master_transfer` takes a `uint32_t` instead of a function pointer type for its `handler` argument. + +- `i2c_slave_receive` should return an enumeration of the slave mode, currently it returns one of 4 integers depending on what mode the peripheral is in, this is much more suited to an enumeration. + +- The behaviour of the `stop` parameter in `i2c_write` is not consistent across platforms. + + The parameter is intended to issue the `STOP` command when a transfer has finished. Not all platforms use this bit, some platforms ignore the argument and either always issue a `STOP` command, or never. This parameter should be removed, there is already a means to call `STOP`. + +## Use cases + +List of drivers and examples currently using the I2C interface: + +* [I2C EEPROM Driver](https://github.com/ARMmbed/i2cee-driver) + +### Other use cases + +- I2C Slave + + In this use case the peripheral receives the clock instead of generating it and responds when addressed by the master. + +- I2C Async + + These are basically the same the regular ones except that the function call should return immediately and a callback should be triggered once the operation is completed. + + + +## API changes + +### General changes + +- **Add** an `i2c_free` function to the API. + + All new HAL APIs must provide a free function to act as a destructor so that the resources used by the i2c instance can be reused. This is useful for OOP functionality in the platform API, and potentially reducing power consumption through the freeing of resources. + +- **Add** an `i2c_get_capabilities` function to API return supported capabilities and constraints on the currently running platform. + +- **Remove** `i2c_reset` function from the API. + + Most target implementations do not implement this function correctly. Most implementations invoke the `stop` command, or directly call the `i2c_stop` from this function which already has a function associated with it in the API. This function is not implemented by the user facing platform API so this change does not affect users. + +- **Change** the `frequency` parameter of `i2c_frequency` from `int` to `uint32_t`. + + Frequency should be unsigned as a signed value does not make sense in this context. `int` also does not have a guaranteed size. + +- **Change** the `stop` parameter for the transfer function from an `int` value to a `bool` value. + + This function argument does not make sense as an `int` value other than for outdated compatibility reasons, the `bool` value expresses the intent more accurately. + + +### Sync API changes + +The main changes involve the removal of the single byte read/write functions and rolling their functionality into the block read/write functions, removing unnecessary parameters from functions and amending their types. + +- **Remove** the `stop` parameter from `i2c_write` and `i2c_read` functions. + + This parameter is not required, the STOP command should be sent manually by calling `i2c_stop` this reduces the amount of conditions that implementations have to meet. + +- **Remove** the `i2c_byte_read` and `i2c_byte_write` functions from the API and integrate the functionality into `i2c_read` and `i2c_write`. + + The functionality of these calls can be implemented as part of the normal `i2c_read` and `i2c_write` functions. Sending individual bytes of data is inefficient and should be avoided where possible. + +- **Change** the `address` parameter in `i2c_write` and `i2c_read` functions from `int` to `uint16_t`. + + The address parameter is up to a 9-bit value, specifying a type with correct sign and size is sensible. + +- **Change** the `length` parameter in `i2c_write` and `i2c_read` functions from `int` to `uint32_t`. + + The length parameter cannot be signed. + +- **Change** the `data` parameter in `i2c_write` and `i2c_read` functions from `char*` to `void*`. + +### Slave API changes + +The main changes involve removing the slave specific read/write functions and rolling them into the normal read/write functions, removing most of the slave configuration which can be handled at construction by the `init` function. + +- **Remove** the `i2c_slave_mode` function, add an `is_slave` parameter to the `i2c_init` function. + + The decision to initialise the peripheral in master or slave mode should be decided at construction time. This simplifies the API as it removes the need for two separate functions to initialise slave mode `i2c_slave_mode` and `i2c_slave_address` . + +- **Remove** the `i2c_slave_address` function, add an `address` parameter to the `i2c_init` function. + + The decision to initialise the I2C peripheral in master or slave mode should be decided at construction time. Adding the `address` parameter removes the need to initialise the address separately. This parameter is ignored if the `is_slave` Boolean is not set. + +- **Remove** the I2C slave specific transfer functions: `i2c_slave_read`, `i2c_slave_write`. + + These functions are superfluous and can be rolled into the existing `i2c_read` and `i2c_write` functions. The `transfer` function will execute the slave read/write based on the current configuration of the peripheral. + +- **Change** the return type of `i2c_slave_receive` from an integer to an enumeration. + + The function returns which mode the peripheral is currently operating in as an integer, this is better expressed as an enumeration. + +### Async API changes + +- **Support** the `DMAUsage` argument for asynchronous transfers. + + Currently the `DMAUsage` argument of the `i2c_transfer_asynch` function is unimplemented, the argument is unused by all `I2C` implementations. There are currently requests for it to be included in the specification here: [Expose DMAUsage in I2C API](https://github.com/ARMmbed/mbed-os/issues/6877) + +- **Change** the `stop` parameter from the `i2c_transfer_async` from an `uint32_t` to a `bool`. + + The stop parameter indicates whether or not the function should send a `STOP` command after the transfer has complete, there is no reason at all for this to be a `uint32_t`. + +- **Change** the return type of `i2c_transfer_asynch` to indicate whether or not a transfer has been scheduled or not. + + The function now returns a `bool` indicate if a transfer was scheduled or not, which can occur if the peripheral is already busy. + +- **Remove** the `i2c_irq_handler_asynch` function from the API. + + The event is now passed as an argument to the callback this method is no longer required. + +- **Remove** the `event` parameter from the `i2c_transfer_async` function. + + The callback will now be invoked on any event with the event as an argument. + +- **Remove** the `i2c_active` function from the API. + + The the async callback is now always invoked on async operation termination (unless cancelled), this status can be tracked from driver layer without any HAL request. + +### The new API + +```c++ +typedef struct { + /** Minimum frequency supported must be set by target device and it will be assessed during + * testing. + */ + uint32_t minimum_frequency; + /** Maximum frequency supported must be set by target device and it will be assessed during + * testing. + */ + uint32_t maximum_frequency; + /**< If true, the device can handle I2C slave mode. */ + bool support_slave_mode; + /**< Supports 10 bit addressing */ + bool supported_10bit_addressing; +} i2c_capabilities_t; + +/** Fills structure indicating supported features and frequencies on the current platform + * + * @param[out] capabilities Capabilities structure filled with supported configurations. + */ +void i2c_get_capabilities(i2c_capabilities_t *capabilities); + +/** Initialize the I2C peripheral. It sets the default parameters for I2C + * peripheral, and configures its pins. + * + * @param obj The I2C object + * @param sda The sda pin + * @param scl The scl pin + * @param is_slave Choose whether the peripheral is initialised as master or slave. + * @param address Specify the address for the peripheral in slave mode. + * This parameter is ignored in master mode. + */ +void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave, uint16_t address); + +/** Configure the frequency in Hz the I2C peripheral should operate at. + * + * @param obj The I2C object + * @param frequency Frequency in Hz + */ +void i2c_frequency(i2c_t *obj, uint32_t frequency); + +/** Send START command + * + * @param obj The I2C object + * @returns True if slave responds with ACK, false otherwise. + */ +bool i2c_start(i2c_t *obj); + +/** Send STOP command + * + * @param obj The I2C object + * @returns True if slave responds with ACK, false otherwise. + */ +bool i2c_stop(i2c_t *obj); + +/** Blocking sending data + * + * @param obj The I2C object + * @param address 7-bit address (last bit is 0) + * @param data The buffer for sending + * @param length Number of bytes to write + * @return + * zero or non-zero - Number of written bytes + * negative - I2C_ERROR_XXX status + */ +int i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length); + +/** Blocking reading data + * + * @param obj The I2C object + * @param address 7-bit address (last bit is 1) + * @param data The buffer for receiving + * @param length Number of bytes to read + * @return + * zero or non-zero - Number of written bytes + * negative - I2C_ERROR_XXX status + */ +int i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length); + +typedef enum { + NO_ADDRESS = 0, + READ = 1, + BROADCAST = 2, + WRITE = 3 +} i2c_slave_status; + +/** Check to see if the I2C slave has been addressed. + * @param obj The I2C object + * @return The status - i2c_slave_status indicating what mode the peripheral is configured in. + */ +i2c_slave_status i2c_slave_receive(i2c_t *obj); + +typedef void (*i2c_async_handler_f)(i2c_t *obj, void *ctx, i2c_async_event_t event); + +/** Start I2C asynchronous transfer + * + * @param obj The I2C object + * @param tx The transmit buffer + * @param tx_length The number of bytes to transmit + * @param rx The receive buffer + * @param rx_length The number of bytes to receive + * @param address The address to be set - 7bit or 9bit + * @param stop If true, stop will be generated after the transfer is done + * @param handler The I2C IRQ handler to be set + * @param hint DMA hint usage + */ +void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, void *rx, uint32_t rx_length, uint16_t address, bool stop, i2c_async_handler_f handler, DMAUsage hint); + +/** Abort asynchronous transfer + * + * This function does not perform any check - that should happen in upper layers. + * @param obj The I2C object + */ +void i2c_abort_async(i2c_t *obj); +``` + +## Behaviours + +### Defined behaviours + +- `i2c_init()` initialises the pins. + +- `i2c_init()` ignores the address parameter if `is_slave` is false. + +- `i2c_get_capabilities()` fills the given `i2c_capabilities_t` instance + +- `i2c_free()` resets the pins to their default states. + +- `i2c_free()` disables the peripheral clock. + +- `i2c_frequency()` sets the frequency to use during the following transfers. + +- `i2c_write()` + + - Writes `tx_len` symbols to the bus. + +- `i2c_read()` + + - Reads `rx_len` symbols from the bus. + - If `rx` is `nullptr` then inputs are discarded. + +- `i2c_transfer_async` returns immediately with a `bool` indicating whether the transfer was successfully scheduled or not. + +- The callback given to `i2c_transfer_async` is invoked when the transfer completes (with a success or an error) + +- `i2c_abort_asynch` aborts an on-going async transfer. + + + +### Undefined behaviours + +- Calling `i2c_init` multiple times without calling `i2c_free`. +- Calling any i2c function before calling `i2c_init` or after calling `i2c_free`. +- Passing an invalid pointer as `obj` to any function. +- Passing invalid pins to `i2c_init` +- Setting a frequency outside the supported range given by `i2c_get_capabilities` +- Setting `address` to an invalid value when initialising the peripheral as a slave. +- Specifying an invalid `address` when calling any `read` or `write` functions. +- Passing an invalid pointer as `handler` to `i2c_transfer_async`. +- Calling `i2c_transfer_abort` when no transfer is currently in progress. + +``` +TODO: List of undefined behaviours +``` + + + +## Impact on Partner implementations + +For each target implementation the existing API must be refactored or reimplemented to conform to the updated API. Given that many partners have a single implementation for each board family, generally only one target will need to be updated per partner. + +- STM32 - Impact: Low + + There is only one implementation for I2C that functions for all of the partners boards, updating to the new API requires a rewrite of only this single implementation. + +- K64F - Impact: Medium - No implementations: 3 + + There are three implementations for I2C. One for `K20` family boards, one for `KL` family boards, and one for `MCUX` boards. All three implementations would require rewriting for the new API. + +- NORDIC - Impact: Low - No implementations: 2 + + There are two implementations one for `NRF5*` family boards, and one specifically for `NRF51822`. There doesn't seem to be an apparent reason why these couldn't be unified into a single implementation when the API is updated. + +- Atmel - Impact: Low - No implementations: 2 + + One implementation for `M0+` family boards, another implementation for `M4` boards. + +- Silicon Labs - Impact: Low - No implementations: 1 + + A single implementation for all boards, only a single update required. + +- Maxim - Impact: Medium - No implementation 6 + + There are 6 instances of the `i2c_api` driver, only 3 of these are unique and the rest are copies in different folders so realistically only 3 updates are required. + + + +## Drawbacks + +These are breaking changes to the API. The changes will require each partner to reimplement their current I2C drivers to conform with the updated API. + + +## FAQ + From 1f70325d59b2323c73fe64b16e925d8168ead20b Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 20 Aug 2018 13:49:50 +0100 Subject: [PATCH 02/45] docs(hal-i2c): Add modifications for timeout and multimaster support --- hal/rfcs/0001-i2c-overhaul.md | 123 ++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 51 deletions(-) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md index 96716d752a5..f42fd1a4ce7 100644 --- a/hal/rfcs/0001-i2c-overhaul.md +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -17,7 +17,7 @@ Additionally, since the original implementations were added for the API a large ### Inconsistencies -- The implementation for `i2c_reset` varies across all platforms. +- The implementation for `i2c_reset` varies across all platforms. The purpose of this function is to add a method of resetting the i2c bus. Most platform implementations do not implement this function correctly, either the implementation acts as a NOP and does nothing, or it calls `i2c_stop` which already has a functional representation in the API and is designed to perform a different action. @@ -41,21 +41,23 @@ Additionally, since the original implementations were added for the API a large The function returns an integer but there is no documentation to suggest what the return value should be. Some implementations return 0 in all cases, and some use it to return error values, if this function is to return a value, the values should be standardised. -- Some platforms overload the `i2c_slave_read`, `i2c_slave_write`, `i2c_read`, and `i2c_write` return values with statuses that aren't documented. +- Some platforms overload the `i2c_slave_read`, `i2c_slave_write`, `i2c_read`, and `i2c_write` return values with statuses that aren't documented. These functions are documented to return the number of bytes written or read. Some implementations will return a non-zero value if the write/read fails, this is not documented anywhere and the behaviour is not common across all implementations. -- Some platforms overload the `i2c_read` and `i2c_write` return values with statuses that aren't documented. +- Some platforms overload the `i2c_read` and `i2c_write` return values with statuses that aren't documented. The functions are documented to return the number of bytes written or read. Some implementations will return a non-zero value if the write/read fails, this is not documented anywhere and the behaviour is not common across all implementations. -- `i2c_master_transfer` takes a `uint32_t` instead of a function pointer type for its `handler` argument. +- `i2c_master_transfer` takes a `uint32_t` instead of a function pointer type for its `handler` argument. - `i2c_slave_receive` should return an enumeration of the slave mode, currently it returns one of 4 integers depending on what mode the peripheral is in, this is much more suited to an enumeration. - The behaviour of the `stop` parameter in `i2c_write` is not consistent across platforms. - The parameter is intended to issue the `STOP` command when a transfer has finished. Not all platforms use this bit, some platforms ignore the argument and either always issue a `STOP` command, or never. This parameter should be removed, there is already a means to call `STOP`. + The parameter is intended to issue the `STOP` command when a transfer has finished. Not all platforms use this bit, some platforms ignore the argument and either always issue a `STOP` command, or never. This parameter should be removed, there is already a means to call `STOP`. + +- The behaviour of the I2C peripheral is not uniform in multimaster configurations. There are no specifications indicating what a device should do when needs to handle collision or arbitration loss due to other master devices sharing the bus. Some platforms will transparently handle arbitration and others will return an error to the user API, and some do not handle multimaster. ## Use cases @@ -71,9 +73,11 @@ List of drivers and examples currently using the I2C interface: - I2C Async - These are basically the same the regular ones except that the function call should return immediately and a callback should be triggered once the operation is completed. + These are basically the same the regular ones except that the function call should return immediately and a callback should be triggered once the operation is completed. + +- I2C MultiMaster - + The I2C specification defines I2C as a multimaster bus. This allows multiple I2C devices in master mode to concurrently control a slave device on the same bus. The requirements on the master hardware is the ability to handle transfer collisions when both devices attempt to communicate with the slave simultaneously. ## API changes @@ -83,9 +87,15 @@ List of drivers and examples currently using the I2C interface: All new HAL APIs must provide a free function to act as a destructor so that the resources used by the i2c instance can be reused. This is useful for OOP functionality in the platform API, and potentially reducing power consumption through the freeing of resources. +- **Add** `i2c_timeout` function to the API. + + The timeout value is used when a slave attached to the master is clock stretching. The timeout value is used to specify the maximum period of time that the master peripheral will wait for the slave to release the SCL line. This timeout duration is not currently configurable, adding this function will allow this to be set to a specific period before failing the transfer with a timeout error. + - **Add** an `i2c_get_capabilities` function to API return supported capabilities and constraints on the currently running platform. -- **Remove** `i2c_reset` function from the API. +- **Add** specification enforcing multimaster support on platforms that support it. Not all partner boards support multimaster, any platform that does support it must handle arbitration with other masters on the same bus transparently to the user and API. + +- **Remove** `i2c_reset` function from the API. Most target implementations do not implement this function correctly. Most implementations invoke the `stop` command, or directly call the `i2c_stop` from this function which already has a function associated with it in the API. This function is not implemented by the user facing platform API so this change does not affect users. @@ -144,7 +154,7 @@ The main changes involve removing the slave specific read/write functions and ro - **Support** the `DMAUsage` argument for asynchronous transfers. - Currently the `DMAUsage` argument of the `i2c_transfer_asynch` function is unimplemented, the argument is unused by all `I2C` implementations. There are currently requests for it to be included in the specification here: [Expose DMAUsage in I2C API](https://github.com/ARMmbed/mbed-os/issues/6877) + Currently the `DMAUsage` argument of the `i2c_transfer_asynch` function is unimplemented, the argument is unused by all `I2C` implementations. There are currently requests for it to be included in the specification here: [Expose DMAUsage in I2C API](https://github.com/ARMmbed/mbed-os/issues/6877) - **Change** the `stop` parameter from the `i2c_transfer_async` from an `uint32_t` to a `bool`. @@ -160,28 +170,26 @@ The main changes involve removing the slave specific read/write functions and ro - **Remove** the `event` parameter from the `i2c_transfer_async` function. - The callback will now be invoked on any event with the event as an argument. + The callback will now be invoked on any event with the event as an argument. - **Remove** the `i2c_active` function from the API. - The the async callback is now always invoked on async operation termination (unless cancelled), this status can be tracked from driver layer without any HAL request. + The the async callback is now always invoked on async operation termination (unless cancelled), this status can be tracked from driver layer without any HAL request. ### The new API ```c++ typedef struct { - /** Minimum frequency supported must be set by target device and it will be assessed during - * testing. - */ + /**< Minimum frequency supported must be set by target device */ uint32_t minimum_frequency; - /** Maximum frequency supported must be set by target device and it will be assessed during - * testing. - */ + /**< Maximum frequency supported must be set by target device */ uint32_t maximum_frequency; /**< If true, the device can handle I2C slave mode. */ - bool support_slave_mode; - /**< Supports 10 bit addressing */ - bool supported_10bit_addressing; + bool supports_slave_mode; + /**< If true, supports 10-bit addressing. */ + bool supports_10bit_addressing; + /**< If true, the device handle multimaster collisions and arbitration safely*/ + bool supports_multi_master; } i2c_capabilities_t; /** Fills structure indicating supported features and frequencies on the current platform @@ -192,7 +200,7 @@ void i2c_get_capabilities(i2c_capabilities_t *capabilities); /** Initialize the I2C peripheral. It sets the default parameters for I2C * peripheral, and configures its pins. - * + * * @param obj The I2C object * @param sda The sda pin * @param scl The scl pin @@ -209,6 +217,16 @@ void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave, uint16_t addr */ void i2c_frequency(i2c_t *obj, uint32_t frequency); + +/** Configure the timeout duration in milliseconds the I2C peripheral should allow the slave + * peripheral to stretch the clock for before timing out. + * + * @param obj The I2C object + * @param timeout Clock stretching timeout in milliseconds. + */ +void i2c_timeout(i2c_t *obj, uint32_t timeout); + + /** Send START command * * @param obj The I2C object @@ -229,7 +247,13 @@ bool i2c_stop(i2c_t *obj); * @param address 7-bit address (last bit is 0) * @param data The buffer for sending * @param length Number of bytes to write - * @return + * + * @note If the current platform supports multimaster operation the transfer will block until + * the peripheral can gain arbitration of the bus and complete the transfer. If the + * device does not support multimaster operation this function is not safe to execute + * when the bus is shared with another device in master mode. + * + * @return * zero or non-zero - Number of written bytes * negative - I2C_ERROR_XXX status */ @@ -241,7 +265,13 @@ int i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length); * @param address 7-bit address (last bit is 1) * @param data The buffer for receiving * @param length Number of bytes to read - * @return + * + * @note If the current platform supports multimaster operation the transfer will block until + * the peripheral can gain arbitration of the bus and complete the transfer. If the + * device does not support multimaster operation this function is not safe to execute + * when the bus is shared with another device in master mode. + * + * @return * zero or non-zero - Number of written bytes * negative - I2C_ERROR_XXX status */ @@ -289,33 +319,28 @@ void i2c_abort_async(i2c_t *obj); ### Defined behaviours - `i2c_init()` initialises the pins. - - `i2c_init()` ignores the address parameter if `is_slave` is false. - -- `i2c_get_capabilities()` fills the given `i2c_capabilities_t` instance - +- `i2c_get_capabilities()` fills the given `i2c_capabilities_t` instance - `i2c_free()` resets the pins to their default states. - - `i2c_free()` disables the peripheral clock. - - `i2c_frequency()` sets the frequency to use during the following transfers. - +- `i2c_timeout()` set the clock stretching timeout to use during the following transfers. - `i2c_write()` - Writes `tx_len` symbols to the bus. - -- `i2c_read()` + - Handles transfer collisions and loss of arbitration if the platform supports multimaster in hardware. + - The transfer will timeout and return `I2C_ERROR_TIMEOUT ` if the slave stretches the clock for longer than the configured timeout duration. +- `i2c_read()` - Reads `rx_len` symbols from the bus. + - Handles transfer collisions and loss of arbitration if the platform supports multimaster in hardware. - If `rx` is `nullptr` then inputs are discarded. - + - The transfer will timeout and return `I2C_ERROR_TIMEOUT ` if the slave stretches the clock for longer than the configured timeout duration. - `i2c_transfer_async` returns immediately with a `bool` indicating whether the transfer was successfully scheduled or not. - - The callback given to `i2c_transfer_async` is invoked when the transfer completes (with a success or an error) - - `i2c_abort_asynch` aborts an on-going async transfer. - + ### Undefined behaviours @@ -323,47 +348,43 @@ void i2c_abort_async(i2c_t *obj); - Calling any i2c function before calling `i2c_init` or after calling `i2c_free`. - Passing an invalid pointer as `obj` to any function. - Passing invalid pins to `i2c_init` -- Setting a frequency outside the supported range given by `i2c_get_capabilities` +- Using the device in multimaster configuration when `supports_multimaster_mode` is false. +- Setting a frequency outside the supported range given by `i2c_get_capabilities`. +- Setting a timeout outside the supported range given by `i2c_get_capabilities`. - Setting `address` to an invalid value when initialising the peripheral as a slave. - Specifying an invalid `address` when calling any `read` or `write` functions. - Passing an invalid pointer as `handler` to `i2c_transfer_async`. - Calling `i2c_transfer_abort` when no transfer is currently in progress. -``` -TODO: List of undefined behaviours -``` - - - ## Impact on Partner implementations For each target implementation the existing API must be refactored or reimplemented to conform to the updated API. Given that many partners have a single implementation for each board family, generally only one target will need to be updated per partner. -- STM32 - Impact: Low +- STM32 - No implementations: 1 - There is only one implementation for I2C that functions for all of the partners boards, updating to the new API requires a rewrite of only this single implementation. + There is a single I2C implementation that implements the interface for all partner boards. Updating the implementation to match the new API will require a single rewrite of this implementation, though ensuring the changes work without regressions on all of the boards may be difficult. -- K64F - Impact: Medium - No implementations: 3 +- K64F - No implementations: 3 There are three implementations for I2C. One for `K20` family boards, one for `KL` family boards, and one for `MCUX` boards. All three implementations would require rewriting for the new API. -- NORDIC - Impact: Low - No implementations: 2 +- NORDIC - No implementations: 2 There are two implementations one for `NRF5*` family boards, and one specifically for `NRF51822`. There doesn't seem to be an apparent reason why these couldn't be unified into a single implementation when the API is updated. -- Atmel - Impact: Low - No implementations: 2 +- Atmel - No implementations: 2 One implementation for `M0+` family boards, another implementation for `M4` boards. -- Silicon Labs - Impact: Low - No implementations: 1 +- Silicon Labs - No implementations: 1 A single implementation for all boards, only a single update required. -- Maxim - Impact: Medium - No implementation 6 +- Maxim - No implementation 6 There are 6 instances of the `i2c_api` driver, only 3 of these are unique and the rest are copies in different folders so realistically only 3 updates are required. - + ## Drawbacks From 6453cbc8df8dc04cff081432a40ed976c539b235 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Thu, 6 Sep 2018 13:38:42 +0100 Subject: [PATCH 03/45] docs(hal-i2c): Update the RFC to reflect removing the DMAUsage parameter --- hal/rfcs/0001-i2c-overhaul.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md index f42fd1a4ce7..366613f0e03 100644 --- a/hal/rfcs/0001-i2c-overhaul.md +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -152,9 +152,9 @@ The main changes involve removing the slave specific read/write functions and ro ### Async API changes -- **Support** the `DMAUsage` argument for asynchronous transfers. +- **Remove** the `DMAUsage` argument for asynchronous transfers. - Currently the `DMAUsage` argument of the `i2c_transfer_asynch` function is unimplemented, the argument is unused by all `I2C` implementations. There are currently requests for it to be included in the specification here: [Expose DMAUsage in I2C API](https://github.com/ARMmbed/mbed-os/issues/6877) + Currently the `DMAUsage` argument of the `i2c_transfer_asynch` function is unimplemented, the argument is unused by all `I2C` implementations. There is no real demand for it so can be removed from the API. - **Change** the `stop` parameter from the `i2c_transfer_async` from an `uint32_t` to a `bool`. @@ -302,9 +302,8 @@ typedef void (*i2c_async_handler_f)(i2c_t *obj, void *ctx, i2c_async_event_t eve * @param address The address to be set - 7bit or 9bit * @param stop If true, stop will be generated after the transfer is done * @param handler The I2C IRQ handler to be set - * @param hint DMA hint usage */ -void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, void *rx, uint32_t rx_length, uint16_t address, bool stop, i2c_async_handler_f handler, DMAUsage hint); +void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, void *rx, uint32_t rx_length, uint16_t address, bool stop, i2c_async_handler_f handler); /** Abort asynchronous transfer * From 6d2162cf808287c5222d7ddd0d0f29c54f78f305 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Thu, 6 Sep 2018 13:42:26 +0100 Subject: [PATCH 04/45] docs(hal-i2c): Add missing stop and last parameters to i2c_read/write --- hal/rfcs/0001-i2c-overhaul.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md index 366613f0e03..01177500410 100644 --- a/hal/rfcs/0001-i2c-overhaul.md +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -247,6 +247,7 @@ bool i2c_stop(i2c_t *obj); * @param address 7-bit address (last bit is 0) * @param data The buffer for sending * @param length Number of bytes to write + * @param stop If true, stop will be generated after the transfer is done * * @note If the current platform supports multimaster operation the transfer will block until * the peripheral can gain arbitration of the bus and complete the transfer. If the @@ -257,7 +258,7 @@ bool i2c_stop(i2c_t *obj); * zero or non-zero - Number of written bytes * negative - I2C_ERROR_XXX status */ -int i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length); +int i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length, bool stop); /** Blocking reading data * @@ -265,6 +266,8 @@ int i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length); * @param address 7-bit address (last bit is 1) * @param data The buffer for receiving * @param length Number of bytes to read + * @param last If true, indicates that the transfer contains the last byte + * to be sent. * * @note If the current platform supports multimaster operation the transfer will block until * the peripheral can gain arbitration of the bus and complete the transfer. If the @@ -275,7 +278,7 @@ int i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length); * zero or non-zero - Number of written bytes * negative - I2C_ERROR_XXX status */ -int i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length); +int i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool last); typedef enum { NO_ADDRESS = 0, From 846658ab021b75d5aa85d640d511256746dde757 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Thu, 6 Sep 2018 13:45:00 +0100 Subject: [PATCH 05/45] docs(hal-i2c): Move address parameter into a seperate i2c_slave function --- hal/rfcs/0001-i2c-overhaul.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md index 01177500410..326d9f434b8 100644 --- a/hal/rfcs/0001-i2c-overhaul.md +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -205,10 +205,8 @@ void i2c_get_capabilities(i2c_capabilities_t *capabilities); * @param sda The sda pin * @param scl The scl pin * @param is_slave Choose whether the peripheral is initialised as master or slave. - * @param address Specify the address for the peripheral in slave mode. - * This parameter is ignored in master mode. */ -void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave, uint16_t address); +void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave); /** Configure the frequency in Hz the I2C peripheral should operate at. * @@ -293,6 +291,13 @@ typedef enum { */ i2c_slave_status i2c_slave_receive(i2c_t *obj); +/** Configure I2C address. + * + * @param obj The I2C object + * @param address The address to be set + */ +void i2c_slave_address(i2c_t *obj, uint16_t address); + typedef void (*i2c_async_handler_f)(i2c_t *obj, void *ctx, i2c_async_event_t event); /** Start I2C asynchronous transfer From 52ca16c7d0b002b4511b6e40021478cb9aa2ebc1 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Tue, 2 Oct 2018 13:01:01 +0100 Subject: [PATCH 06/45] docs(hal-i2c): Add additional defined function behaviours --- hal/rfcs/0001-i2c-overhaul.md | 86 +++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md index 326d9f434b8..bef53d81652 100644 --- a/hal/rfcs/0001-i2c-overhaul.md +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -325,41 +325,72 @@ void i2c_abort_async(i2c_t *obj); ### Defined behaviours -- `i2c_init()` initialises the pins. -- `i2c_init()` ignores the address parameter if `is_slave` is false. -- `i2c_get_capabilities()` fills the given `i2c_capabilities_t` instance -- `i2c_free()` resets the pins to their default states. -- `i2c_free()` disables the peripheral clock. -- `i2c_frequency()` sets the frequency to use during the following transfers. -- `i2c_timeout()` set the clock stretching timeout to use during the following transfers. -- `i2c_write()` - - - Writes `tx_len` symbols to the bus. +- `i2c_init`: + - Initialises the peripheral pins specified in the input parameters. + - Initialises the peripheral in master mode if `is_slave` is false. + - Initialises the peripheral in slave mode if `is_slave` is true and `supports_slave_mode` is true. +- `i2c_free`: + - Resets the pins used to initialise the peripheral to their default state. + - Disables the peripheral clock. +- `i2c_get_capabilities`: + - Fills the contents of the `i2c_capabilities_t` parameter +- `i2c_frequency`: + - Sets the frequency to use for the transfer. + - Must leave all other configuration unchanged. +- `i2c_timeout`: + - Sets the clock stretching timeout to use for the following transfers. + - If the timeout is set to 0, disables clock stretching. +- `i2c_write`: + - Writes `length` number of symbols to the bus. + - Returns the number of symbols sent to the bus. + - Returns an error status if transfer fails. + - Generates a stop condition on the bus at the end of the transfer if `stop` parameter is true. - Handles transfer collisions and loss of arbitration if the platform supports multimaster in hardware. - The transfer will timeout and return `I2C_ERROR_TIMEOUT ` if the slave stretches the clock for longer than the configured timeout duration. -- `i2c_read()` - +- `i2c_read`: - Reads `rx_len` symbols from the bus. + - Returns the number of symbols received from the bus. + - Returns an error code if transfer fails. - Handles transfer collisions and loss of arbitration if the platform supports multimaster in hardware. - - If `rx` is `nullptr` then inputs are discarded. - The transfer will timeout and return `I2C_ERROR_TIMEOUT ` if the slave stretches the clock for longer than the configured timeout duration. -- `i2c_transfer_async` returns immediately with a `bool` indicating whether the transfer was successfully scheduled or not. -- The callback given to `i2c_transfer_async` is invoked when the transfer completes (with a success or an error) -- `i2c_abort_asynch` aborts an on-going async transfer. - - +- `i2c_start`: + - Generates I2C START condition on the bus in master mode. + - Does nothing if called when the peripheral is configured in slave mode. +- `i2c_stop`: + - Generates I2C STOP condition on the bus in master mode. + - Does nothing if called when the peripheral is configured in slave mode. +- `i2c_slave_receive`: + - Indicates which mode the peripheral has been addressed in. + - Returns not addressed when called in master mode. +- `i2c_slave_address`: + - Sets the address of the peripheral to the `address` parameter. + - Does nothing if called master mode. +- `i2c_transfer_async`: + - Returns immediately with a `bool` indicating whether the transfer was successfully scheduled or not. + - The callback given to `i2c_transfer_async` is invoked when the transfer finishes. + - Must save the handler and context pointers inside the `obj` pointer. + - The context pointer is passed to the callback on transfer completion. + - The callback must be invoked on completion unless the transfer is aborted. + - `i2c_async_event_t` must be filled with the number of symbols sent to the bus during transfer. +- `i2c_abort_async`: + - Aborts any on-going async transfers. ### Undefined behaviours -- Calling `i2c_init` multiple times without calling `i2c_free`. -- Calling any i2c function before calling `i2c_init` or after calling `i2c_free`. -- Passing an invalid pointer as `obj` to any function. -- Passing invalid pins to `i2c_init` -- Using the device in multimaster configuration when `supports_multimaster_mode` is false. -- Setting a frequency outside the supported range given by `i2c_get_capabilities`. -- Setting a timeout outside the supported range given by `i2c_get_capabilities`. -- Setting `address` to an invalid value when initialising the peripheral as a slave. -- Specifying an invalid `address` when calling any `read` or `write` functions. +- Use of a `null` pointer as an argument to any function. +- Calling any `I2C` function before calling `i2c_init` or after calling `i2c_free`. +- Initialising the `I2C` peripheral with invalid `SDA` and `SCL` pins. +- Initialising the peripheral in slave mode if slave mode is not supported, indicated by `i2c_get_capabilities`. +- Operating the peripheral in slave mode without first specifying and address using `i2c_address` +- Setting an address using `i2c_address` after initialising the peripheral in master mode. +- Setting an address to an `I2C` reserved value. +- Setting an address larger than the 7-bit supported maximum if 10-bit addressing is not supported. +- Setting an address larger than the 10-bit supported maximum. +- Setting a frequency outside the supported range given by `i2c_get_capabilities` +- Using the device in a multimaster configuration when `supports_multimaster_mode` is false. +- Setting the timeout outside the supported range given by `i2c_get_capabilities`. +- Specifying an invalid address when calling any `read` or `write` functions. +- Setting the length of the transfer or receive buffers to larger than the buffers are. - Passing an invalid pointer as `handler` to `i2c_transfer_async`. - Calling `i2c_transfer_abort` when no transfer is currently in progress. @@ -392,7 +423,6 @@ For each target implementation the existing API must be refactored or reimplemen There are 6 instances of the `i2c_api` driver, only 3 of these are unique and the rest are copies in different folders so realistically only 3 updates are required. - ## Drawbacks These are breaking changes to the API. The changes will require each partner to reimplement their current I2C drivers to conform with the updated API. From 28e4f05c0f8cda2b789ff2da360982c34de94880 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 29 Oct 2018 11:02:29 +0000 Subject: [PATCH 07/45] docs(hal-i2c): Make additional amendments to I2C API specification - Add return value to i2c_frequency indicating configured frequency. - Change return type of i2c_write/i2c_read from int to int32_t - Add type suffix to i2c_slave_status enum. - Rename i2c_slave_receive to i2c_slave_status. --- hal/rfcs/0001-i2c-overhaul.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md index bef53d81652..836714641e9 100644 --- a/hal/rfcs/0001-i2c-overhaul.md +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -212,6 +212,10 @@ void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave); * * @param obj The I2C object * @param frequency Frequency in Hz + * + * @returns The actual frequency that the peripheral will be generating to + * allow a user adjust its strategy in case the target cannot be + * reached. */ void i2c_frequency(i2c_t *obj, uint32_t frequency); @@ -256,7 +260,7 @@ bool i2c_stop(i2c_t *obj); * zero or non-zero - Number of written bytes * negative - I2C_ERROR_XXX status */ -int i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length, bool stop); +int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length, bool stop); /** Blocking reading data * @@ -276,20 +280,20 @@ int i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length, b * zero or non-zero - Number of written bytes * negative - I2C_ERROR_XXX status */ -int i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool last); +int32_t i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool last); typedef enum { NO_ADDRESS = 0, READ = 1, BROADCAST = 2, WRITE = 3 -} i2c_slave_status; +} i2c_slave_status_t; /** Check to see if the I2C slave has been addressed. * @param obj The I2C object - * @return The status - i2c_slave_status indicating what mode the peripheral is configured in. + * @return The status - i2c_slave_status_t indicating what mode the peripheral is configured in. */ -i2c_slave_status i2c_slave_receive(i2c_t *obj); +i2c_slave_status_t i2c_slave_status(i2c_t *obj); /** Configure I2C address. * @@ -335,6 +339,7 @@ void i2c_abort_async(i2c_t *obj); - `i2c_get_capabilities`: - Fills the contents of the `i2c_capabilities_t` parameter - `i2c_frequency`: + - Returns the actual frequency that will be used. - Sets the frequency to use for the transfer. - Must leave all other configuration unchanged. - `i2c_timeout`: From d2ed73607e90c0ec947d72fade0427923323c0ce Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 29 Oct 2018 11:30:21 +0000 Subject: [PATCH 08/45] docs(hal-i2c): Add additional notes to synchronous read/write --- hal/rfcs/0001-i2c-overhaul.md | 73 ++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md index 836714641e9..becc776fdc7 100644 --- a/hal/rfcs/0001-i2c-overhaul.md +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -192,9 +192,11 @@ typedef struct { bool supports_multi_master; } i2c_capabilities_t; -/** Fills structure indicating supported features and frequencies on the current platform +/** Fills structure indicating supported features and frequencies on the current + * platform. * - * @param[out] capabilities Capabilities structure filled with supported configurations. + * @param[out] capabilities Capabilities structure filled with supported + * configurations. */ void i2c_get_capabilities(i2c_capabilities_t *capabilities); @@ -204,7 +206,8 @@ void i2c_get_capabilities(i2c_capabilities_t *capabilities); * @param obj The I2C object * @param sda The sda pin * @param scl The scl pin - * @param is_slave Choose whether the peripheral is initialised as master or slave. + * @param is_slave Choose whether the peripheral is initialised as master or + * slave. */ void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave); @@ -220,8 +223,8 @@ void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave); void i2c_frequency(i2c_t *obj, uint32_t frequency); -/** Configure the timeout duration in milliseconds the I2C peripheral should allow the slave - * peripheral to stretch the clock for before timing out. +/** Configure the timeout duration in milliseconds the I2C peripheral should + * allow the slave peripheral to stretch the clock for before timing out. * * @param obj The I2C object * @param timeout Clock stretching timeout in milliseconds. @@ -244,6 +247,26 @@ bool i2c_start(i2c_t *obj); bool i2c_stop(i2c_t *obj); /** Blocking sending data + * + * This function transmits data, when the peripheral is configured as Master to + * the selected slave, and when configured as Slave transmits data to the + * Master. + * + * This function is blocking, it will return when the transfer is complete or a + * timeout event is triggered. The number of bytes transmitted is returned by + * the function after the operation is completed. Transmit operation cannot be + * cancelled or aborted. + * + * The data buffer must stay allocated during the duration of the transfer and + * the contents must not be modified. The value of the specified `address` is + * ignored when configured in slave mode, in master mode it contains the + * address of the target peripheral. This is a 7-bit value unless 10-bit + * addressing is configured and supported by the target. + * + * When in master mode the operation consists of: + * - Address the slave as a Master transmitter. + * - Transmit data to the addressed slave. + * - Generate a STOP condition if the specified `stop` field is true. * * @param obj The I2C object * @param address 7-bit address (last bit is 0) @@ -251,10 +274,11 @@ bool i2c_stop(i2c_t *obj); * @param length Number of bytes to write * @param stop If true, stop will be generated after the transfer is done * - * @note If the current platform supports multimaster operation the transfer will block until - * the peripheral can gain arbitration of the bus and complete the transfer. If the - * device does not support multimaster operation this function is not safe to execute - * when the bus is shared with another device in master mode. + * @note If the current platform supports multimaster operation the transfer + * will block until the peripheral can gain arbitration of the bus and + * complete the transfer. If the device does not support multimaster + * operation this function is not safe to execute when the bus is shared + * with another device in master mode. * * @return * zero or non-zero - Number of written bytes @@ -263,6 +287,19 @@ bool i2c_stop(i2c_t *obj); int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length, bool stop); /** Blocking reading data + * + * This function receives data, when the peripheral is configured as Master + * from the selected slave, and when configured as Slave from the Master. + * + * This function is blocking, it will return when the transfer is complete or a + * timeout event is triggered. The number of bytes received is returned by + * the function after the operation is completed. Receive operation cannot be + * cancelled or aborted. + * + * When in master mode the operation consists of: + * - Address the slave as a Master receiver. + * - Receive data from the addressed slave. + * - Generate a STOP condition if the specified `stop` field is true. * * @param obj The I2C object * @param address 7-bit address (last bit is 1) @@ -271,10 +308,11 @@ int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t lengt * @param last If true, indicates that the transfer contains the last byte * to be sent. * - * @note If the current platform supports multimaster operation the transfer will block until - * the peripheral can gain arbitration of the bus and complete the transfer. If the - * device does not support multimaster operation this function is not safe to execute - * when the bus is shared with another device in master mode. + * @note If the current platform supports multimaster operation the transfer + * will block until the peripheral can gain arbitration of the bus and + * complete the transfer. If the device does not support multimaster + * operation this function is not safe to execute when the bus is shared + * with another device in master mode. * * @return * zero or non-zero - Number of written bytes @@ -291,11 +329,14 @@ typedef enum { /** Check to see if the I2C slave has been addressed. * @param obj The I2C object - * @return The status - i2c_slave_status_t indicating what mode the peripheral is configured in. + * @return The status - i2c_slave_status_t indicating what mode the peripheral + * is configured in. */ i2c_slave_status_t i2c_slave_status(i2c_t *obj); /** Configure I2C address. + * + * @note This function does nothing when configured in master mode. * * @param obj The I2C object * @param address The address to be set @@ -319,7 +360,9 @@ void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, void *rx /** Abort asynchronous transfer * - * This function does not perform any check - that should happen in upper layers. + * This function does not perform any check - that should happen in upper + * layers. + * * @param obj The I2C object */ void i2c_abort_async(i2c_t *obj); From b2db1757d1edfba6996f35ca37155db0b9af3385 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 29 Oct 2018 11:41:20 +0000 Subject: [PATCH 09/45] docs(hal-i2c): Add clock stretching field to target capabilities --- hal/rfcs/0001-i2c-overhaul.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md index becc776fdc7..b70890bb7aa 100644 --- a/hal/rfcs/0001-i2c-overhaul.md +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -190,6 +190,8 @@ typedef struct { bool supports_10bit_addressing; /**< If true, the device handle multimaster collisions and arbitration safely*/ bool supports_multi_master; + /**< If true, supports configuring clock stretching. */ + bool supports_clock_stretching; } i2c_capabilities_t; /** Fills structure indicating supported features and frequencies on the current From 4a88ec4f97a1851b83c6d0b8b9cf59d9798626c3 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 29 Oct 2018 13:21:56 +0000 Subject: [PATCH 10/45] docs(hal-i2c): Add context pointer to async transfer function --- hal/rfcs/0001-i2c-overhaul.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md index b70890bb7aa..30192178fa4 100644 --- a/hal/rfcs/0001-i2c-overhaul.md +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -358,7 +358,9 @@ typedef void (*i2c_async_handler_f)(i2c_t *obj, void *ctx, i2c_async_event_t eve * @param stop If true, stop will be generated after the transfer is done * @param handler The I2C IRQ handler to be set */ -void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, void *rx, uint32_t rx_length, uint16_t address, bool stop, i2c_async_handler_f handler); +void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, + void *rx, uint32_t rx_length, uint16_t address, + bool stop, i2c_async_handler_f handler, void *ctx); /** Abort asynchronous transfer * From 76e954c501723fec24e32ea7f40ae993f959a0dc Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 5 Nov 2018 15:13:36 +0000 Subject: [PATCH 11/45] docs(hal-i2c): Fix return type of i2c_frequency function --- hal/rfcs/0001-i2c-overhaul.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/hal/rfcs/0001-i2c-overhaul.md index 30192178fa4..b4a1a92c1a7 100644 --- a/hal/rfcs/0001-i2c-overhaul.md +++ b/hal/rfcs/0001-i2c-overhaul.md @@ -222,7 +222,7 @@ void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave); * allow a user adjust its strategy in case the target cannot be * reached. */ -void i2c_frequency(i2c_t *obj, uint32_t frequency); +uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency); /** Configure the timeout duration in milliseconds the I2C peripheral should From 0f3cba476a4ffcb460656fc052cf948985b96725 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Wed, 14 Nov 2018 17:13:20 +0000 Subject: [PATCH 12/45] docs(hal-i2c): Move design document to the updated folder --- {hal/rfcs => docs/design-documents/hal}/0001-i2c-overhaul.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {hal/rfcs => docs/design-documents/hal}/0001-i2c-overhaul.md (100%) diff --git a/hal/rfcs/0001-i2c-overhaul.md b/docs/design-documents/hal/0001-i2c-overhaul.md similarity index 100% rename from hal/rfcs/0001-i2c-overhaul.md rename to docs/design-documents/hal/0001-i2c-overhaul.md From 280ffcecb2d47117eb17231d8b70b226e281fa06 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Thu, 20 Dec 2018 16:39:31 +0000 Subject: [PATCH 13/45] docs(hal-i2c): Update design document to match recent API alterations --- .../design-documents/hal/0001-i2c-overhaul.md | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/docs/design-documents/hal/0001-i2c-overhaul.md b/docs/design-documents/hal/0001-i2c-overhaul.md index b4a1a92c1a7..e0d8d3cc31b 100644 --- a/docs/design-documents/hal/0001-i2c-overhaul.md +++ b/docs/design-documents/hal/0001-i2c-overhaul.md @@ -103,6 +103,10 @@ List of drivers and examples currently using the I2C interface: Frequency should be unsigned as a signed value does not make sense in this context. `int` also does not have a guaranteed size. +- **Add** a return value to the `i2c_frequency` which indicates the frequency that the peripheral was configured to. + + The frequency requested may not be supported by the I2C peripheral, the peripheral will select the nearest supported frequency instead, this selected frequency will be returned to inform the caller of the difference. + - **Change** the `stop` parameter for the transfer function from an `int` value to a `bool` value. This function argument does not make sense as an `int` value other than for outdated compatibility reasons, the `bool` value expresses the intent more accurately. @@ -124,6 +128,10 @@ The main changes involve the removal of the single byte read/write functions and The address parameter is up to a 9-bit value, specifying a type with correct sign and size is sensible. +- **Remove** the return values from `i2c_start` and `i2c_stop` + + Most platforms cannot detect failure at this point and a return status does not make sense at this point. + - **Change** the `length` parameter in `i2c_write` and `i2c_read` functions from `int` to `uint32_t`. The length parameter cannot be signed. @@ -213,6 +221,12 @@ void i2c_get_capabilities(i2c_capabilities_t *capabilities); */ void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave); +/** Release the I2C object. + * + * @param obj The I2C object + */ +void i2c_free(i2c_t *obj); + /** Configure the frequency in Hz the I2C peripheral should operate at. * * @param obj The I2C object @@ -233,20 +247,17 @@ uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency); */ void i2c_timeout(i2c_t *obj, uint32_t timeout); - /** Send START command * * @param obj The I2C object - * @returns True if slave responds with ACK, false otherwise. */ -bool i2c_start(i2c_t *obj); +void i2c_start(i2c_t *obj); /** Send STOP command * * @param obj The I2C object - * @returns True if slave responds with ACK, false otherwise. */ -bool i2c_stop(i2c_t *obj); +void i2c_stop(i2c_t *obj); /** Blocking sending data * @@ -307,8 +318,7 @@ int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t lengt * @param address 7-bit address (last bit is 1) * @param data The buffer for receiving * @param length Number of bytes to read - * @param last If true, indicates that the transfer contains the last byte - * to be sent. + * @param stop If true, stop will be generated after the transfer is done * * @note If the current platform supports multimaster operation the transfer * will block until the peripheral can gain arbitration of the bus and @@ -320,13 +330,13 @@ int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t lengt * zero or non-zero - Number of written bytes * negative - I2C_ERROR_XXX status */ -int32_t i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool last); +int32_t i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool stop); typedef enum { - NO_ADDRESS = 0, - READ = 1, - BROADCAST = 2, - WRITE = 3 + NoData = 0, // Slave has not been addressed. + ReadAddressed = 1, // Master has requested a read from this slave. + WriteGeneral = 2, // Master is writing to all slaves. + WriteAddressed = 3 // Master is writing to this slave. } i2c_slave_status_t; /** Check to see if the I2C slave has been addressed. From 459097ea0ee5404da90e72d06fb98a85f4696da7 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 29 Oct 2018 11:58:36 +0000 Subject: [PATCH 14/45] feat(hal-i2c): Disable I2C on all targets in preparation --- targets/targets.json | 276 ++++++------------------------------------- 1 file changed, 36 insertions(+), 240 deletions(-) diff --git a/targets/targets.json b/targets/targets.json index 7126959e250..3d5e15b2a68 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -175,8 +175,6 @@ "device_has": [ "ANALOGIN", "CAN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -202,8 +200,6 @@ "supported_toolchains": ["ARM", "uARM", "GCC_ARM", "IAR"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -232,8 +228,6 @@ "detect_code": ["1040"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "LOCALFILESYSTEM", "PORTIN", @@ -261,8 +255,6 @@ "extra_labels": ["NXP", "LPC11UXX"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -287,8 +279,6 @@ "supported_toolchains": ["ARM", "uARM", "GCC_ARM", "IAR"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "LOCALFILESYSTEM", "PORTIN", @@ -316,8 +306,6 @@ "supported_toolchains": ["ARM", "uARM", "GCC_ARM"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -350,8 +338,6 @@ "supported_toolchains": ["ARM", "uARM", "GCC_ARM", "IAR"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -378,8 +364,6 @@ "supported_toolchains": ["ARM", "uARM", "GCC_ARM", "IAR"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -406,8 +390,6 @@ "supported_toolchains": ["ARM", "uARM", "GCC_ARM", "IAR"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -436,8 +418,6 @@ "supported_toolchains": ["ARM", "uARM", "GCC_ARM", "IAR"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -468,8 +448,6 @@ "inherits": ["LPC11U37_501"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -495,8 +473,6 @@ "inherits": ["LPCTarget"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -521,8 +497,6 @@ "detect_code": ["1168"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PWMOUT", "SERIAL", @@ -543,8 +517,6 @@ "supported_toolchains": ["ARM", "GCC_ARM", "IAR"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -571,7 +543,6 @@ "ANALOGIN", "ANALOGOUT", "CAN", - "I2C", "INTERRUPTIN", "PWMOUT", "SERIAL", @@ -598,8 +569,6 @@ "DEBUG_AWARENESS", "EMAC", "ETHERNET", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "LOCALFILESYSTEM", "PORTIN", @@ -648,8 +617,6 @@ "DEBUG_AWARENESS", "EMAC", "ETHERNET", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -699,8 +666,6 @@ "DEBUG_AWARENESS", "EMAC", "ETHERNET", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -735,8 +700,6 @@ "CAN", "DEBUG_AWARENESS", "ETHERNET", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "LOCALFILESYSTEM", "PORTIN", @@ -763,8 +726,6 @@ "is_disk_virtual": true, "supported_toolchains": ["uARM", "IAR", "GCC_ARM"], "device_has": [ - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PWMOUT", "SERIAL", @@ -786,8 +747,6 @@ "inherits": ["LPCTarget"], "detect_code": ["1050"], "device_has": [ - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PWMOUT", "SERIAL", @@ -810,8 +769,6 @@ "inherits": ["LPCTarget"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PWMOUT", "SERIAL", @@ -832,8 +789,6 @@ "supported_toolchains": ["uARM", "GCC_ARM"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PWMOUT", "SERIAL", @@ -860,8 +815,6 @@ "DEBUG_AWARENESS", "EMAC", "ETHERNET", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -897,8 +850,6 @@ "ANALOGOUT", "DEBUG_AWARENESS", "ETHERNET", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -923,8 +874,6 @@ "ANALOGOUT", "DEBUG_AWARENESS", "ETHERNET", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -947,8 +896,6 @@ "ANALOGOUT", "DEBUG_AWARENESS", "ETHERNET", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -984,8 +931,6 @@ "inherits": ["LPCTarget"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1023,8 +968,6 @@ "USTICKER", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1053,8 +996,6 @@ "USTICKER", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1081,8 +1022,6 @@ "USTICKER", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1109,8 +1048,6 @@ "USTICKER", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1137,8 +1074,6 @@ "detect_code": ["0230"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1169,8 +1104,6 @@ "device_has": [ "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1210,8 +1143,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1248,8 +1179,6 @@ "LPTICKER", "RTC", "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTOUT", @@ -1279,8 +1208,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1312,8 +1239,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1352,8 +1277,6 @@ "LPTICKER", "RTC", "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1391,8 +1314,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1435,8 +1356,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1489,8 +1408,6 @@ "ANALOGIN", "ANALOGOUT", "EMAC", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1548,7 +1465,6 @@ "SLEEP", "INTERRUPTIN", "SPI", - "I2C", "ANALOGIN", "MPU" ], @@ -1579,7 +1495,6 @@ "SLEEP", "INTERRUPTIN", "SPI", - "I2C", "ANALOGIN", "MPU" ], @@ -1606,8 +1521,6 @@ "USTICKER", "LPTICKER", "RTC", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1643,8 +1556,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1696,8 +1607,6 @@ "LPTICKER", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1736,8 +1645,6 @@ "LPTICKER", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1778,8 +1685,6 @@ "ANALOGIN", "ANALOGOUT", "EMAC", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1817,8 +1722,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1883,9 +1786,6 @@ "LPTICKER", "RTC", "ANALOGIN", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1926,8 +1826,6 @@ "LPTICKER", "ANALOGIN", "EMAC", - "I2C", - "I2CSLAVE", "ERROR_RED", "INTERRUPTIN", "PORTIN", @@ -1965,8 +1863,6 @@ "USTICKER", "RTC", "ANALOGIN", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -2001,8 +1897,6 @@ "RTC", "ANALOGIN", "EMAC", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -4619,7 +4513,6 @@ "features": ["BLE"], "device_has": [ "ANALOGIN", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -4864,7 +4757,6 @@ "device_has": [ "ANALOGIN", "DEBUG_AWARENESS", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -4897,8 +4789,6 @@ "USTICKER", "LPTICKER", "ANALOGIN", - "I2C", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5012,8 +4902,6 @@ "USTICKER", "LPTICKER", "ANALOGIN", - "I2C", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5056,8 +4944,6 @@ "USTICKER", "LPTICKER", "ANALOGIN", - "I2C", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5093,7 +4979,6 @@ "ANALOGIN", "CLCD", "ETHERNET", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5120,7 +5005,6 @@ "ANALOGIN", "CLCD", "ETHERNET", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5144,7 +5028,6 @@ "ANALOGIN", "CLCD", "ETHERNET", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5168,7 +5051,6 @@ "ANALOGIN", "CLCD", "ETHERNET", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5192,7 +5074,6 @@ "ANALOGIN", "CLCD", "ETHERNET", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5216,7 +5097,6 @@ "ANALOGIN", "CLCD", "ETHERNET", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5237,7 +5117,6 @@ "ANALOGIN", "CLCD", "ETHERNET", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5260,7 +5139,6 @@ "ANALOGIN", "CLCD", "ETHERNET", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5285,7 +5163,6 @@ "ANALOGIN", "EMAC", "FLASH", - "I2C", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -5306,6 +5183,42 @@ "target.network-default-interface-type": "ETHERNET" } }, + "ARM_BEETLE_SOC": { + "inherits": ["ARM_IOTSS_Target"], + "core": "Cortex-M3", + "supported_toolchains": ["ARM", "GCC_ARM", "IAR"], + "default_toolchain": "ARM", + "extra_labels": ["ARM_SSG", "BEETLE"], + "macros": [ + "CMSDK_BEETLE", + "WSF_MS_PER_TICK=20", + "WSF_TOKEN_ENABLED=FALSE", + "WSF_TRACE_ENABLED=TRUE", + "WSF_ASSERT_ENABLED=FALSE", + "WSF_PRINTF_MAX_LEN=128", + "ASIC", + "CONFIG_HOST_REV=0x20", + "CONFIG_ALLOW_DEEP_SLEEP=FALSE", + "HCI_VS_TARGET", + "CONFIG_ALLOW_SETTING_WRITE=TRUE", + "WSF_MAX_HANDLERS=20", + "NO_LEDS" + ], + "device_has": [ + "ANALOGIN", + "CLCD", + "INTERRUPTIN", + "PORTIN", + "PORTINOUT", + "PORTOUT", + "SERIAL", + "SLEEP", + "SPI", + "MPU" + ], + "features": ["BLE"], + "release_versions": ["2", "5"] + }, "RZ_A1XX": { "inherits": ["Target"], "core": "Cortex-A9", @@ -5318,9 +5231,6 @@ "ANALOGIN", "CAN", "ETHERNET", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5379,7 +5289,6 @@ "device_has": [ "ANALOGIN", "ANALOGOUT", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5403,7 +5312,6 @@ "device_has": [ "ANALOGIN", "ANALOGOUT", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5424,7 +5332,6 @@ "supported_toolchains": ["GCC_ARM", "IAR", "ARM"], "device_has": [ "ANALOGIN", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -5454,7 +5361,6 @@ "device_has": [ "ANALOGIN", "FLASH", - "I2C", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -5485,7 +5391,6 @@ "device_has": [ "ANALOGIN", "FLASH", - "I2C", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -5509,7 +5414,6 @@ "supported_toolchains": ["GCC_ARM", "IAR", "ARM"], "device_has": [ "ANALOGIN", - "I2C", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -5559,7 +5463,6 @@ "supported_toolchains": ["GCC_ARM", "IAR", "ARM"], "device_has": [ "ANALOGIN", - "I2C", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -5603,9 +5506,6 @@ "device_has": [ "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -5680,9 +5580,6 @@ "device_has": [ "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -5758,9 +5655,6 @@ "device_has": [ "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -5834,9 +5728,6 @@ "inherits": ["EFM32ZG222F32"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -5907,9 +5798,6 @@ "inherits": ["EFM32HG322F64"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -5980,9 +5868,6 @@ "device_has": [ "ANALOGIN", "CRC", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -6079,9 +5964,6 @@ "802_15_4_PHY", "ANALOGIN", "CRC", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -6149,9 +6031,6 @@ "802_15_4_PHY", "ANALOGIN", "CRC", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -6226,9 +6105,6 @@ "device_has": [ "ANALOGIN", "CRC", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -6314,9 +6190,6 @@ "802_15_4_PHY", "ANALOGIN", "CRC", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -6394,9 +6267,6 @@ "ANALOGIN", "CRC", "EMAC", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", @@ -6476,7 +6346,6 @@ "inherits": ["Target"], "device_has": [ "ANALOGIN", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6501,7 +6370,6 @@ "inherits": ["Target"], "device_has": [ "ANALOGIN", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6525,7 +6393,6 @@ "supported_toolchains": ["uARM", "ARM", "GCC_ARM", "IAR"], "device_has": [ "ANALOGIN", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6552,9 +6419,6 @@ "supported_toolchains": ["GCC_ARM", "ARM", "uARM"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6586,9 +6450,6 @@ "device_has": [ "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6620,9 +6481,6 @@ "device_has": [ "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6654,9 +6512,6 @@ "device_has": [ "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6688,9 +6543,6 @@ "default_toolchain": "ARM", "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6767,7 +6619,6 @@ }, "device_has": [ "ANALOGIN", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6806,8 +6657,6 @@ "USTICKER", "LPTICKER", "ANALOGIN", - "I2C", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6830,8 +6679,6 @@ "USTICKER", "LPTICKER", "ANALOGIN", - "I2C", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6855,8 +6702,6 @@ "device_has": [ "USTICKER", "LPTICKER", - "I2C", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6878,8 +6723,6 @@ "USTICKER", "LPTICKER", "ANALOGIN", - "I2C", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -6908,8 +6751,6 @@ "device_has": [ "ANALOGIN", "FLASH", - "I2C", - "I2C_ASYNCH", "INTERRUPTIN", "ITM", "LPTICKER", @@ -7034,8 +6875,6 @@ "device_has": [ "ANALOGIN", "FLASH", - "I2C", - "I2C_ASYNCH", "INTERRUPTIN", "ITM", "LPTICKER", @@ -7157,9 +6996,6 @@ "LPTICKER", "RTC", "ANALOGIN", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -7231,7 +7067,6 @@ "device_has": [ "ANALOGIN", "SERIAL", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -7285,9 +7120,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -7354,9 +7186,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -7433,8 +7262,6 @@ "device_has": [ "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -7494,8 +7321,6 @@ "USTICKER", "LPTICKER", "ANALOGIN", - "I2C", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -7674,9 +7499,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -7733,8 +7555,6 @@ "PORTOUT", "SERIAL", "SLEEP", - "I2C", - "I2CSLAVE", "STDIO_MESSAGES", "PWMOUT" ], @@ -7749,9 +7569,6 @@ "extra_labels_add": ["STM32F4", "STM32F411xE", "STM32F411RE"], "device_has": [ "ANALOGIN", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -7797,7 +7614,6 @@ "SERIAL", "SERIAL_FC", "SPI", - "I2C", "STDIO_MESSAGES", "TRNG", "FLASH", @@ -7825,7 +7641,6 @@ "CLCD", "EMAC", "FLASH", - "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -7916,9 +7731,6 @@ "RTC", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2CSLAVE", - "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -7961,8 +7773,6 @@ "SERIAL", "SLEEP", "SPI", - "I2C", - "I2CSLAVE", "STDIO_MESSAGES", "MPU" ], @@ -7987,8 +7797,6 @@ "PWMOUT", "SERIAL", "SPI", - "I2C", - "I2CSLAVE", "STDIO_MESSAGES", "FLASH", "SLEEP", @@ -8020,8 +7828,6 @@ "PWMOUT", "ANALOGIN", "ANALOGOUT", - "I2C", - "I2C_ASYNCH", "SPI", "SPI_ASYNCH", "STDIO_MESSAGES", @@ -8318,8 +8124,6 @@ "SERIAL", "SLEEP", "SPI", - "I2C", - "I2CSLAVE", "STDIO_MESSAGES", "MPU" ], @@ -8382,9 +8186,7 @@ "extra_labels_add": ["GD32F30X", "GD32F307VG", "GD_EMAC"], "device_has_add": [ "RTC", - "I2C", "CAN", - "I2CSLAVE", "ANALOGOUT", "SPI", "SPISLAVE", @@ -8421,8 +8223,6 @@ "SERIAL", "SLEEP", "SPI", - "I2C", - "I2CSLAVE", "STDIO_MESSAGES", "MPU" ], @@ -8437,9 +8237,7 @@ "extra_labels_add": ["GD32F4XX", "GD32F450ZI", "GD_EMAC"], "device_has_add": [ "RTC", - "I2C", "CAN", - "I2CSLAVE", "ANALOGOUT", "SPI", "SPISLAVE", @@ -8466,9 +8264,7 @@ "extra_labels_add": ["GD32E10X", "GD32E103VB"], "device_has_add": [ "RTC", - "I2C", "CAN", - "I2CSLAVE", "ANALOGOUT", "SPI", "SPISLAVE", From 6a51a72614355aeb309d052e64685f71940fc31c Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 29 Oct 2018 11:50:02 +0000 Subject: [PATCH 15/45] feat(hal-i2c): Update the HAL I2C header file to new API --- hal/i2c_api.h | 236 +++++++++++++++++++++++++++++++------------------- 1 file changed, 145 insertions(+), 91 deletions(-) diff --git a/hal/i2c_api.h b/hal/i2c_api.h index 3ade94ce778..6ce7dac71a5 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -44,13 +44,25 @@ /**@}*/ #if DEVICE_I2C_ASYNCH + +typedef struct i2c i2c_t; + +typedef struct i2c_async_event { + uint32_t transferred; + bool error; +} i2c_async_event_t; + +typedef void (*i2c_async_handler_f)(i2c_t* obj, i2c_async_event_t* event, void *ctx); + /** Asynch I2C HAL structure */ -typedef struct { - struct i2c_s i2c; /**< Target specific I2C structure */ +struct i2c { + struct i2c_s i2c; /**< Target specific I2C structure */ struct buffer_s tx_buff; /**< Tx buffer */ struct buffer_s rx_buff; /**< Rx buffer */ -} i2c_t; + i2c_async_handler_f handler; + void* ctx; +}; #else /** Non-asynch I2C HAL structure @@ -68,84 +80,153 @@ enum { extern "C" { #endif +typedef struct { + /**< Minimum frequency supported must be set by target device */ + uint32_t minimum_frequency; + /**< Maximum frequency supported must be set by target device */ + uint32_t maximum_frequency; + /**< If true, the device can handle I2C slave mode. */ + bool supports_slave_mode; + /**< If true, supports 10-bit addressing. */ + bool supports_10bit_addressing; + /**< If true, the device handle multimaster collisions and arbitration safely*/ + bool supports_multi_master; + /**< If true, supports configuring clock stretching. */ + bool supports_clock_stretching; +} i2c_capabilities_t; + /** * \defgroup hal_GeneralI2C I2C Configuration Functions * @{ */ +/** Fills structure indicating supported features and frequencies on the current + * platform. + * + * @param[out] capabilities Capabilities structure filled with supported + * configurations. + */ +void i2c_get_capabilities(i2c_capabilities_t *capabilities); + /** Initialize the I2C peripheral. It sets the default parameters for I2C - * peripheral, and configures its specifieds pins. + * peripheral, and configures its pins. * - * @param obj The I2C object - * @param sda The sda pin - * @param scl The scl pin + * @param obj The I2C object + * @param sda The sda pin + * @param scl The scl pin + * @param is_slave Choose whether the peripheral is initialised as master or + * slave. */ -void i2c_init(i2c_t *obj, PinName sda, PinName scl); +void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave); -/** Configure the I2C frequency +/** Configure the frequency in Hz the I2C peripheral should operate at. * - * @param obj The I2C object - * @param hz Frequency in Hz + * @param obj The I2C object + * @param frequency Frequency in Hz + * + * @returns The actual frequency that the peripheral will be generating to + * allow a user adjust its strategy in case the target cannot be + * reached. */ -void i2c_frequency(i2c_t *obj, int hz); +uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency); -/** Send START command +/** Configure the timeout duration in milliseconds the I2C peripheral should + * allow the slave peripheral to stretch the clock for before timing out. * - * @param obj The I2C object + * @param obj The I2C object + * @param timeout Clock stretching timeout in milliseconds. */ -int i2c_start(i2c_t *obj); +void i2c_timeout(i2c_t *obj, uint32_t timeout); -/** Send STOP command +/** Send START command * * @param obj The I2C object + * @returns True if slave responds with ACK, false otherwise. */ -int i2c_stop(i2c_t *obj); +bool i2c_start(i2c_t *obj); -/** Blocking reading data +/** Send STOP command * - * @param obj The I2C object - * @param address 7-bit address (last bit is 1) - * @param data The buffer for receiving - * @param length Number of bytes to read - * @param stop Stop to be generated after the transfer is done - * @return Number of read bytes + * @param obj The I2C object + * @returns True if slave responds with ACK, false otherwise. */ -int i2c_read(i2c_t *obj, int address, char *data, int length, int stop); +bool i2c_stop(i2c_t *obj); /** Blocking sending data + * + * This function transmits data, when the peripheral is configured as Master to + * the selected slave, and when configured as Slave transmits data to the + * Master. + * + * This function is blocking, it will return when the transfer is complete or a + * timeout event is triggered. The number of bytes transmitted is returned by + * the function after the operation is completed. Transmit operation cannot be + * cancelled or aborted. + * + * The data buffer must stay allocated during the duration of the transfer and + * the contents must not be modified. The value of the specified `address` is + * ignored when configured in slave mode, in master mode it contains the + * address of the target peripheral. This is a 7-bit value unless 10-bit + * addressing is configured and supported by the target. + * + * When in master mode the operation consists of: + * - Address the slave as a Master transmitter. + * - Transmit data to the addressed slave. + * - Generate a STOP condition if the specified `stop` field is true. * * @param obj The I2C object * @param address 7-bit address (last bit is 0) * @param data The buffer for sending * @param length Number of bytes to write - * @param stop Stop to be generated after the transfer is done + * @param stop If true, stop will be generated after the transfer is done + * + * @note If the current platform supports multimaster operation the transfer + * will block until the peripheral can gain arbitration of the bus and + * complete the transfer. If the device does not support multimaster + * operation this function is not safe to execute when the bus is shared + * with another device in master mode. + * * @return * zero or non-zero - Number of written bytes * negative - I2C_ERROR_XXX status */ -int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop); +int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, + uint32_t length, bool stop); -/** Reset I2C peripheral. TODO: The action here. Most of the implementation sends stop() +/** Blocking reading data * - * @param obj The I2C object - */ -void i2c_reset(i2c_t *obj); - -/** Read one byte + * This function receives data, when the peripheral is configured as Master + * from the selected slave, and when configured as Slave from the Master. * - * @param obj The I2C object - * @param last Acknoledge - * @return The read byte - */ -int i2c_byte_read(i2c_t *obj, int last); - -/** Write one byte + * This function is blocking, it will return when the transfer is complete or a + * timeout event is triggered. The number of bytes received is returned by + * the function after the operation is completed. Receive operation cannot be + * cancelled or aborted. * - * @param obj The I2C object - * @param data Byte to be written - * @return 0 if NAK was received, 1 if ACK was received, 2 for timeout. + * When in master mode the operation consists of: + * - Address the slave as a Master receiver. + * - Receive data from the addressed slave. + * - Generate a STOP condition if the specified `stop` field is true. + * + * @param obj The I2C object + * @param address 7-bit address (last bit is 1) + * @param data The buffer for receiving + * @param length Number of bytes to read + * @param last If true, indicates that the transfer contains the last byte + * to be sent. + * + * @note If the current platform supports multimaster operation the transfer + * will block until the peripheral can gain arbitration of the bus and + * complete the transfer. If the device does not support multimaster + * operation this function is not safe to execute when the bus is shared + * with another device in master mode. + * + * @return + * zero or non-zero - Number of written bytes + * negative - I2C_ERROR_XXX status */ -int i2c_byte_write(i2c_t *obj, int data); +int32_t i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, + bool last); /** Get the pins that support I2C SDA * @@ -192,43 +273,28 @@ const PinMap *i2c_slave_scl_pinmap(void); * @{ */ -/** Configure I2C as slave or master. - * @param obj The I2C object - * @param enable_slave Enable i2c hardware so you can receive events with ::i2c_slave_receive - * @return non-zero if a value is available - */ -void i2c_slave_mode(i2c_t *obj, int enable_slave); +typedef enum { + NO_ADDRESS = 0, + READ = 1, + BROADCAST = 2, + WRITE = 3 +} i2c_slave_status_t; /** Check to see if the I2C slave has been addressed. * @param obj The I2C object - * @return The status - 1 - read addresses, 2 - write to all slaves, - * 3 write addressed, 0 - the slave has not been addressed + * @return The status - i2c_slave_status_t indicating what mode the peripheral + * is configured in. */ -int i2c_slave_receive(i2c_t *obj); - -/** Configure I2C as slave or master. - * @param obj The I2C object - * @param data The buffer for receiving - * @param length Number of bytes to read - * @return non-zero if a value is available - */ -int i2c_slave_read(i2c_t *obj, char *data, int length); - -/** Configure I2C as slave or master. - * @param obj The I2C object - * @param data The buffer for sending - * @param length Number of bytes to write - * @return non-zero if a value is available - */ -int i2c_slave_write(i2c_t *obj, const char *data, int length); +i2c_slave_status_t i2c_slave_status(i2c_t *obj); /** Configure I2C address. + * + * @note This function does nothing when configured in master mode. + * * @param obj The I2C object - * @param idx Currently not used * @param address The address to be set - * @param mask Currently not used */ -void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask); +void i2c_slave_address(i2c_t *obj, uint16_t address); #endif @@ -251,31 +317,19 @@ void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask); * @param address The address to be set - 7bit or 9bit * @param stop If true, stop will be generated after the transfer is done * @param handler The I2C IRQ handler to be set - * @param event Event mask for the transfer. See \ref hal_I2CEvents - * @param hint DMA hint usage */ -void i2c_transfer_asynch(i2c_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint32_t address, uint32_t stop, uint32_t handler, uint32_t event, DMAUsage hint); - -/** The asynchronous IRQ handler - * - * @param obj The I2C object which holds the transfer information - * @return Event flags if a transfer termination condition was met, otherwise return 0. - */ -uint32_t i2c_irq_handler_asynch(i2c_t *obj); - -/** Attempts to determine if the I2C peripheral is already in use - * - * @param obj The I2C object - * @return Non-zero if the I2C module is active or zero if it is not - */ -uint8_t i2c_active(i2c_t *obj); +void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, + void *rx, uint32_t rx_length, uint16_t address, + bool stop, i2c_async_handler_f handler); /** Abort asynchronous transfer * - * This function does not perform any check - that should happen in upper layers. + * This function does not perform any check - that should happen in upper + * layers. + * * @param obj The I2C object */ -void i2c_abort_asynch(i2c_t *obj); +void i2c_abort_async(i2c_t *obj); #endif From c681eebce58fff0b66dcc014dce6279af6730f7c Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 29 Oct 2018 12:10:55 +0000 Subject: [PATCH 16/45] feat(hal-i2c): Update I2C driver API to new specification --- drivers/I2C.cpp | 41 ++++++++--------------------------------- drivers/I2C.h | 2 +- drivers/I2CSlave.cpp | 23 +++++++++++++---------- drivers/I2CSlave.h | 2 ++ hal/i2c_api.h | 2 ++ 5 files changed, 26 insertions(+), 44 deletions(-) diff --git a/drivers/I2C.cpp b/drivers/I2C.cpp index 3e06825c17d..e58ca1ed16c 100644 --- a/drivers/I2C.cpp +++ b/drivers/I2C.cpp @@ -32,7 +32,7 @@ SingletonPtr I2C::_mutex; I2C::I2C(PinName sda, PinName scl) : #if DEVICE_I2C_ASYNCH - _irq(this), _usage(DMA_USAGE_NEVER), _deep_sleep_locked(false), + _irq(this), _deep_sleep_locked(false), #endif _i2c(), _hz(100000) { @@ -41,7 +41,9 @@ I2C::I2C(PinName sda, PinName scl) : _sda = sda; _scl = scl; recover(sda, scl); - i2c_init(&_i2c, _sda, _scl); + i2c_init(&_i2c, sda, scl, false); + i2c_frequency(&_i2c, _hz); + // Used to avoid unnecessary frequency updates _owner = this; unlock(); @@ -50,7 +52,7 @@ I2C::I2C(PinName sda, PinName scl) : void I2C::frequency(int hz) { lock(); - _hz = hz; + _hz = (uint32_t)hz; // We want to update the frequency even if we are already the bus owners i2c_frequency(&_i2c, _hz); @@ -77,7 +79,7 @@ int I2C::write(int address, const char *data, int length, bool repeated) aquire(); int stop = (repeated) ? 0 : 1; - int written = i2c_write(&_i2c, address, data, length, stop); + int written = i2c_write(&_i2c, address, (void*)data, length, stop); unlock(); return length != written; @@ -86,7 +88,7 @@ int I2C::write(int address, const char *data, int length, bool repeated) int I2C::write(int data) { lock(); - int ret = i2c_byte_write(&_i2c, data); + int ret = i2c_write(&_i2c, 0, (void *)&data, 1, false); unlock(); return ret; } @@ -108,11 +110,7 @@ int I2C::read(int ack) { lock(); int ret; - if (ack) { - ret = i2c_byte_read(&_i2c, 0); - } else { - ret = i2c_byte_read(&_i2c, 1); - } + i2c_read(&_i2c, 0, &ret, 1, (ack == 0)); unlock(); return ret; } @@ -191,40 +189,17 @@ int I2C::recover(PinName sda, PinName scl) int I2C::transfer(int address, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, const event_callback_t &callback, int event, bool repeated) { - lock(); - if (i2c_active(&_i2c)) { - unlock(); - return -1; // transaction ongoing - } - lock_deep_sleep(); - aquire(); - - _callback = callback; - int stop = (repeated) ? 0 : 1; - _irq.callback(&I2C::irq_handler_asynch); - i2c_transfer_asynch(&_i2c, (void *)tx_buffer, tx_length, (void *)rx_buffer, rx_length, address, stop, _irq.entry(), event, _usage); - unlock(); return 0; } void I2C::abort_transfer(void) { lock(); - i2c_abort_asynch(&_i2c); - unlock_deep_sleep(); unlock(); } void I2C::irq_handler_asynch(void) { - int event = i2c_irq_handler_asynch(&_i2c); - if (_callback && event) { - _callback.call(event); - } - - if (event) { - unlock_deep_sleep(); - } } void I2C::lock_deep_sleep() diff --git a/drivers/I2C.h b/drivers/I2C.h index 9a19bd12474..928be89f059 100644 --- a/drivers/I2C.h +++ b/drivers/I2C.h @@ -221,7 +221,7 @@ class I2C : private NonCopyable { i2c_t _i2c; static I2C *_owner; - int _hz; + uint32_t _hz; static SingletonPtr _mutex; PinName _sda; PinName _scl; diff --git a/drivers/I2CSlave.cpp b/drivers/I2CSlave.cpp index f0697460be6..0fcfd619e6e 100644 --- a/drivers/I2CSlave.cpp +++ b/drivers/I2CSlave.cpp @@ -22,9 +22,8 @@ namespace mbed { I2CSlave::I2CSlave(PinName sda, PinName scl) : _i2c() { - i2c_init(&_i2c, sda, scl); + i2c_init(&_i2c, sda, scl, true); i2c_frequency(&_i2c, 100000); - i2c_slave_mode(&_i2c, 1); } void I2CSlave::frequency(int hz) @@ -35,32 +34,36 @@ void I2CSlave::frequency(int hz) void I2CSlave::address(int address) { int addr = (address & 0xFF) | 1; - i2c_slave_address(&_i2c, 0, addr, 0); + + i2c_slave_address(&_i2c, addr); } int I2CSlave::receive(void) { - return i2c_slave_receive(&_i2c); + return i2c_slave_status(&_i2c); } int I2CSlave::read(char *data, int length) { - return i2c_slave_read(&_i2c, data, length) != length; + return i2c_read(&_i2c, 0, data, length, false) != length; } int I2CSlave::read(void) { - return i2c_byte_read(&_i2c, 0); + int ret; + i2c_read(&_i2c, 0, &ret, 1, false); + + return ret; } int I2CSlave::write(const char *data, int length) { - return i2c_slave_write(&_i2c, data, length) != length; + return i2c_write(&_i2c, 0, data, length, false) != length; } int I2CSlave::write(int data) { - return i2c_byte_write(&_i2c, data); + return i2c_write(&_i2c, 0, (void *)&data, 1, false); } void I2CSlave::stop(void) @@ -68,6 +71,6 @@ void I2CSlave::stop(void) i2c_stop(&_i2c); } -} +} // namespace mbed -#endif +#endif // DEVICE_I2CSLAVE diff --git a/drivers/I2CSlave.h b/drivers/I2CSlave.h index 78715ba9345..1289b1fea08 100644 --- a/drivers/I2CSlave.h +++ b/drivers/I2CSlave.h @@ -154,6 +154,8 @@ class I2CSlave { #if !defined(DOXYGEN_ONLY) protected: + PinName _sda; + PinName _scl; /* Internal i2c object identifying the resources */ i2c_t _i2c; diff --git a/hal/i2c_api.h b/hal/i2c_api.h index 6ce7dac71a5..4ac0c1a8dc2 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -28,6 +28,8 @@ #include "hal/dma_api.h" #endif +#include + #if DEVICE_I2C /** From 211899397d8ec98790d3f4dcb2649cbac7436d80 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 29 Oct 2018 12:07:27 +0000 Subject: [PATCH 17/45] feat(hal-i2c): Add synchronous HAL I2C implementation for K64F --- .../TARGET_MCU_K64F/drivers/fsl_i2c.c | 12 +- .../TARGET_MCUXpresso_MCUS/api/i2c_api.c | 475 ++++++++++-------- .../TARGET_MCUXpresso_MCUS/api/objects.h | 1 + targets/targets.json | 1 + 4 files changed, 281 insertions(+), 208 deletions(-) diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/drivers/fsl_i2c.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/drivers/fsl_i2c.c index c3032d0026a..d5145dc821c 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/drivers/fsl_i2c.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/drivers/fsl_i2c.c @@ -1358,12 +1358,12 @@ void I2C_SlaveReadBlocking(I2C_Type *base, uint8_t *rxBuff, size_t rxSize) #endif /* FSL_FEATURE_I2C_HAS_START_STOP_DETECT */ /* Wait for address match and int pending flag. */ - while (!(base->S & kI2C_AddressMatchFlag)) - { - } - while (!(base->S & kI2C_IntPendingFlag)) - { - } + // while (!(base->S & kI2C_AddressMatchFlag)) + // { + // } + // while (!(base->S & kI2C_IntPendingFlag)) + // { + // } /* Read dummy to release bus. */ dummy = base->D; diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c index ff6fc2a856d..0e7a648d3aa 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c @@ -25,224 +25,282 @@ #include "peripheral_clock_defines.h" #include "PeripheralPins.h" -/* 7 bit IIC addr - R/W flag not included */ -static int i2c_address = 0; /* Array of I2C peripheral base address. */ static I2C_Type *const i2c_addrs[] = I2C_BASE_PTRS; /* Array of I2C bus clock frequencies */ static clock_name_t const i2c_clocks[] = I2C_CLOCK_FREQS; -void i2c_init(i2c_t *obj, PinName sda, PinName scl) +void i2c_get_capabilities(i2c_capabilities_t *capabilities) { - uint32_t i2c_sda = pinmap_peripheral(sda, PinMap_I2C_SDA); - uint32_t i2c_scl = pinmap_peripheral(scl, PinMap_I2C_SCL); - PORT_Type *port_addrs[] = PORT_BASE_PTRS; - PORT_Type *base = port_addrs[sda >> GPIO_PORT_SHIFT]; + if (capabilities == NULL) { + return; + } + + capabilities->minimum_frequency = 1; + capabilities->maximum_frequency = 1000000; + capabilities->supports_slave_mode = true; + capabilities->supports_10bit_addressing = true; + capabilities->supports_multi_master = true; + capabilities->supports_clock_stretching = false; +} + +void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave) +{ + uint32_t i2c_sda = pinmap_peripheral(sda, PinMap_I2C_SDA); + uint32_t i2c_scl = pinmap_peripheral(scl, PinMap_I2C_SCL); + + PORT_Type *port_addrs[] = PORT_BASE_PTRS; + PORT_Type *base = port_addrs[sda >> GPIO_PORT_SHIFT]; - obj->instance = pinmap_merge(i2c_sda, i2c_scl); - obj->next_repeated_start = 0; - MBED_ASSERT((int)obj->instance != NC); + obj->instance = pinmap_merge(i2c_sda, i2c_scl); + obj->next_repeated_start = 0; + obj->is_slave = is_slave; + MBED_ASSERT((int)obj->instance != NC); + + const uint32_t clock_frequency = CLOCK_GetFreq(i2c_clocks[obj->instance]); + + if (is_slave) { + i2c_slave_config_t slave_config; + + I2C_SlaveGetDefaultConfig(&slave_config); + slave_config.slaveAddress = 0; + slave_config.enableSlave = true; + +#if FSL_I2C_DRIVER_VERSION > MAKE_VERSION(2, 0, 1) + I2C_SlaveInit(i2c_addrs[obj->instance], &slave_config, clock_frequency); +#else + I2C_SlaveInit(i2c_addrs[obj->instance], &slave_config); +#endif + } else { i2c_master_config_t master_config; I2C_MasterGetDefaultConfig(&master_config); - I2C_MasterInit(i2c_addrs[obj->instance], &master_config, CLOCK_GetFreq(i2c_clocks[obj->instance])); - I2C_EnableInterrupts(i2c_addrs[obj->instance], kI2C_GlobalInterruptEnable); + master_config.enableMaster = true; - pinmap_pinout(sda, PinMap_I2C_SDA); - pinmap_pinout(scl, PinMap_I2C_SCL); + I2C_MasterInit(i2c_addrs[obj->instance], &master_config, clock_frequency); + I2C_EnableInterrupts(i2c_addrs[obj->instance], kI2C_GlobalInterruptEnable); + } - /* Enable internal pullup resistor */ - base->PCR[sda & 0xFF] |= (PORT_PCR_PE_MASK | PORT_PCR_PS_MASK); - base->PCR[scl & 0xFF] |= (PORT_PCR_PE_MASK | PORT_PCR_PS_MASK); + pinmap_pinout(sda, PinMap_I2C_SDA); + pinmap_pinout(scl, PinMap_I2C_SCL); #if defined(FSL_FEATURE_PORT_HAS_OPEN_DRAIN) && FSL_FEATURE_PORT_HAS_OPEN_DRAIN - base->PCR[sda & 0xFF] |= PORT_PCR_ODE_MASK; - base->PCR[scl & 0xFF] |= PORT_PCR_ODE_MASK; + base->PCR[sda & 0xFF] |= PORT_PCR_ODE_MASK; + base->PCR[scl & 0xFF] |= PORT_PCR_ODE_MASK; +#else + /* Enable internal pullup resistor */ + base->PCR[sda & 0xFF] |= (PORT_PCR_PE_MASK | PORT_PCR_PS_MASK); + base->PCR[scl & 0xFF] |= (PORT_PCR_PE_MASK | PORT_PCR_PS_MASK); #endif } -int i2c_start(i2c_t *obj) +void i2c_free(i2c_t *obj) { - I2C_Type *base = i2c_addrs[obj->instance]; - uint32_t statusFlags = I2C_MasterGetStatusFlags(base); + I2C_Type *base = i2c_addrs[obj->instance]; - /* Check if the bus is already in use. */ - if (statusFlags & kI2C_BusBusyFlag) { - /* Send a repeat START signal. */ - base->C1 |= I2C_C1_RSTA_MASK; - } else { - /* Send the START signal. */ - base->C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK; - } + if (obj->is_slave) { + I2C_SlaveDeinit(base); + } else { + I2C_MasterDeinit(base); + } +} + +bool i2c_start(i2c_t *obj) +{ + I2C_Type *base = i2c_addrs[obj->instance]; + + const uint32_t statusFlags = I2C_MasterGetStatusFlags(base); + + /* Check if the bus is already in use. */ + if ((statusFlags & kI2C_BusBusyFlag) != 0) { + /* Send a repeat START signal. */ + base->C1 |= I2C_C1_RSTA_MASK; + } else { + /* Send the START signal. */ + base->C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK; + } #if defined(FSL_FEATURE_I2C_HAS_DOUBLE_BUFFERING) && FSL_FEATURE_I2C_HAS_DOUBLE_BUFFERING - while (!(base->S2 & I2C_S2_EMPTY_MASK)) - { - } + while (!(base->S2 & I2C_S2_EMPTY_MASK)) + { + } #endif /* FSL_FEATURE_I2C_HAS_DOUBLE_BUFFERING */ - return 0; + return 0; } -int i2c_stop(i2c_t *obj) +bool i2c_stop(i2c_t *obj) { - if (I2C_MasterStop(i2c_addrs[obj->instance]) != kStatus_Success) { - return 1; - } + I2C_Type *base = i2c_addrs[obj->instance]; - return 0; + return ((I2C_MasterStop(base)) != kStatus_Success) ? true : false; } -void i2c_frequency(i2c_t *obj, int hz) +uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) { - uint32_t busClock; + I2C_Type *base = i2c_addrs[obj->instance]; + + const uint32_t busClock = CLOCK_GetFreq(i2c_clocks[obj->instance]); - busClock = CLOCK_GetFreq(i2c_clocks[obj->instance]); - I2C_MasterSetBaudRate(i2c_addrs[obj->instance], hz, busClock); + I2C_MasterSetBaudRate(base, frequency, busClock); + + return frequency; } -int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) +void i2c_timeout(i2c_t *obj, uint32_t timeout) { - I2C_Type *base = i2c_addrs[obj->instance]; - i2c_master_transfer_t master_xfer; - - i2c_address = address >> 1; - memset(&master_xfer, 0, sizeof(master_xfer)); - master_xfer.slaveAddress = address >> 1; - master_xfer.direction = kI2C_Read; - master_xfer.data = (uint8_t *)data; - master_xfer.dataSize = length; - if (obj->next_repeated_start) { - master_xfer.flags |= kI2C_TransferRepeatedStartFlag; - } - if (!stop) { - master_xfer.flags |= kI2C_TransferNoStopFlag; - } - obj->next_repeated_start = master_xfer.flags & kI2C_TransferNoStopFlag ? 1 : 0; + (void)obj; + (void)timeout; +} - /* The below function will issue a STOP signal at the end of the transfer. - * This is required by the hardware in order to receive the last byte - */ - if (I2C_MasterTransferBlocking(base, &master_xfer) != kStatus_Success) { - return I2C_ERROR_NO_SLAVE; - } +static int i2c_block_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool last) +{ + I2C_Type *base = i2c_addrs[obj->instance]; + + i2c_master_transfer_t transfer; + memset(&transfer, 0, sizeof(transfer)); + transfer.slaveAddress = (address >> 1); + transfer.direction = kI2C_Read; + transfer.data = (uint8_t *)data; + transfer.dataSize = length; + + if (obj->next_repeated_start) + transfer.flags |= kI2C_TransferRepeatedStartFlag; + + if (!last) + transfer.flags |= kI2C_TransferNoStopFlag; - return length; + obj->next_repeated_start = (transfer.flags & kI2C_TransferNoStopFlag) ? 1 : 0; + + // The below function will issue a STOP signal at the end of the transfer. + // This is required by the hardware in order to receive the last byte + if (I2C_MasterTransferBlocking(base, &transfer) != kStatus_Success) + return I2C_ERROR_NO_SLAVE; + + return length; } -int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) +static int i2c_byte_read(i2c_t *obj, void *data, uint32_t length, bool last) { - I2C_Type *base = i2c_addrs[obj->instance]; - i2c_master_transfer_t master_xfer; - - if (length == 0) { - if (I2C_MasterStart(base, address >> 1, kI2C_Write) != kStatus_Success) { - return I2C_ERROR_NO_SLAVE; - } - - while (!(base->S & kI2C_IntPendingFlag)) { - } - - base->S = kI2C_IntPendingFlag; - - if (base->S & kI2C_ReceiveNakFlag) { - i2c_stop(obj); - return I2C_ERROR_NO_SLAVE; - } else { - i2c_stop(obj); - return length; - } - } + MBED_ASSERT(length == 1); - memset(&master_xfer, 0, sizeof(master_xfer)); - master_xfer.slaveAddress = address >> 1; - master_xfer.direction = kI2C_Write; - master_xfer.data = (uint8_t *)data; - master_xfer.dataSize = length; - if (obj->next_repeated_start) { - master_xfer.flags |= kI2C_TransferRepeatedStartFlag; - } - if (!stop) { - master_xfer.flags |= kI2C_TransferNoStopFlag; - } - obj->next_repeated_start = master_xfer.flags & kI2C_TransferNoStopFlag ? 1 : 0; + I2C_Type *base = i2c_addrs[obj->instance]; - if (I2C_MasterTransferBlocking(base, &master_xfer) != kStatus_Success) { - return I2C_ERROR_NO_SLAVE; - } + /* Setup the I2C peripheral to receive data. */ + base->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK); + + if (last) + base->C1 |= I2C_C1_TXAK_MASK; // NACK - return length; + *(char*)data = (base->D & 0xFF); + + /* Change direction to Tx to avoid extra clocks. */ + base->C1 |= I2C_C1_TX_MASK; + + /* Wait until data transfer complete. */ + while (!(base->S & kI2C_IntPendingFlag)) + { + } + + /* Clear the IICIF flag. */ + base->S = kI2C_IntPendingFlag; + + return length; } -void i2c_reset(i2c_t *obj) +#if DEVICE_I2CSLAVE + +static int i2c_slave_read(i2c_t *obj, void *data, uint32_t length) { - i2c_stop(obj); + I2C_Type *base = i2c_addrs[obj->instance]; + + if (base->S & kI2C_AddressMatchFlag) { + /* Slave receive, master writing to slave. */ + base->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK); + /* Read dummy to release the bus. */ + base->D; + } + + I2C_SlaveReadBlocking(base, (uint8_t *)data, length); + + return length; } -int i2c_byte_read(i2c_t *obj, int last) +i2c_slave_status_t i2c_slave_status(i2c_t *obj) { - uint8_t data; - I2C_Type *base = i2c_addrs[obj->instance]; + uint32_t status_flags = I2C_SlaveGetStatusFlags(i2c_addrs[obj->instance]); - /* Setup the I2C peripheral to receive data. */ - base->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK); + if ((status_flags & kI2C_AddressMatchFlag) == 0) { + return NO_ADDRESS; + } - if (last) { - base->C1 |= I2C_C1_TXAK_MASK; // NACK - } + return ((status_flags & kI2C_TransferDirectionFlag) != 0) ? READ : WRITE; +} - data = (base->D & 0xFF); +static int i2c_slave_write(i2c_t *obj, const void *data, uint32_t length) +{ + I2C_Type *base = i2c_addrs[obj->instance]; - /* Change direction to Tx to avoid extra clocks. */ - base->C1 |= I2C_C1_TX_MASK; + I2C_SlaveWriteBlocking(base, (uint8_t *)data, length); - /* Wait until data transfer complete. */ - while (!(base->S & kI2C_IntPendingFlag)) - { - } + /* Switch to receive mode. */ + base->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK); + /* Read dummy to release bus. */ + base->D; - /* Clear the IICIF flag. */ - base->S = kI2C_IntPendingFlag; + return length; +} - return data; +void i2c_slave_address(i2c_t *obj, uint16_t address) +{ + i2c_addrs[obj->instance]->A1 = address & 0xfe; } +#endif // DEVICE_I2CSLAVE -int i2c_byte_write(i2c_t *obj, int data) +int32_t i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, + bool last) { - int ret_value = 1; - uint8_t statusFlags = 0; - I2C_Type *base = i2c_addrs[obj->instance]; + if ((length == 0) || (data == NULL)) { + return 0; + } - /* Setup the I2C peripheral to transmit data. */ - base->C1 |= I2C_C1_TX_MASK; +#if DEVICE_I2CSLAVE + /* Slave block read */ + if (obj->is_slave) { + return i2c_slave_read(obj, data, length); + } +#endif // DEVICE_I2CSLAVE + + /* Master byte read */ + if (length == 1) { + return i2c_byte_read(obj, data, length, last); + } + + /* Master block read */ + return i2c_block_read(obj, address, data, length, last); +} - /* Send a byte of data. */ - base->D = data; +static int i2c_byte_write(i2c_t *obj, char data, bool stop) +{ + I2C_Type *base = i2c_addrs[obj->instance]; - /* Wait until data transfer complete. */ - while (!(base->S & kI2C_IntPendingFlag)) { - } + int ret_value = 1; - statusFlags = base->S; + /* Setup the I2C peripheral to transmit data. */ + base->C1 |= I2C_C1_TX_MASK; - /* Clear the IICIF flag. */ - base->S = kI2C_IntPendingFlag; + /* Send a byte of data. */ + base->D = data; - /* Check if arbitration lost */ - if (statusFlags & kI2C_ArbitrationLostFlag) { - base->S = kI2C_ArbitrationLostFlag; - ret_value = 2; - } + /* Wait until data transfer complete. */ + while (!(base->S & kI2C_IntPendingFlag)) { + } - /* Check if no acknowledgement (NAK) */ - if (statusFlags & kI2C_ReceiveNakFlag) { - base->S = kI2C_ReceiveNakFlag; - ret_value = 0; - } + const uint8_t statusFlags = base->S; - return ret_value; -} + /* Clear the IICIF flag. */ + base->S = kI2C_IntPendingFlag; const PinMap *i2c_master_sda_pinmap() { @@ -265,72 +323,85 @@ const PinMap *i2c_slave_scl_pinmap() } -#if DEVICE_I2CSLAVE -void i2c_slave_mode(i2c_t *obj, int enable_slave) -{ - i2c_slave_config_t slave_config; - I2C_SlaveGetDefaultConfig(&slave_config); - slave_config.slaveAddress = 0; - slave_config.enableSlave = (bool)enable_slave; -#if FSL_I2C_DRIVER_VERSION > MAKE_VERSION(2, 0, 1) - I2C_SlaveInit(i2c_addrs[obj->instance], &slave_config, CLOCK_GetFreq(i2c_clocks[obj->instance])); -#else - I2C_SlaveInit(i2c_addrs[obj->instance], &slave_config); -#endif + /* Check if no acknowledgement (NAK) */ + if (statusFlags & kI2C_ReceiveNakFlag) { + base->S = kI2C_ReceiveNakFlag; + ret_value = 0; + } + + return ret_value; } -int i2c_slave_receive(i2c_t *obj) +int i2c_block_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length, bool stop) { - uint32_t status_flags = I2C_SlaveGetStatusFlags(i2c_addrs[obj->instance]); - - if (status_flags & kI2C_AddressMatchFlag) { - if (status_flags & kI2C_TransferDirectionFlag) { - // read addressed - return 1; - } else { - // write addressed - return 3; - } - } else { - // slave not addressed - return 0; + I2C_Type *base = i2c_addrs[obj->instance]; + + i2c_master_transfer_t master_xfer; + + if (length == 0) { + if (I2C_MasterStart(base, address >> 1, kI2C_Write) != kStatus_Success) { + return I2C_ERROR_NO_SLAVE; } -} -int i2c_slave_read(i2c_t *obj, char *data, int length) -{ - I2C_Type *base = i2c_addrs[obj->instance]; + while (!(base->S & kI2C_IntPendingFlag)) { + } + + base->S = kI2C_IntPendingFlag; - if (base->S & kI2C_AddressMatchFlag) { - /* Slave receive, master writing to slave. */ - base->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK); - /* Read dummy to release the bus. */ - base->D; + if (base->S & kI2C_ReceiveNakFlag) { + i2c_stop(obj); + return I2C_ERROR_NO_SLAVE; + } else { + i2c_stop(obj); + return length; } + } - I2C_SlaveReadBlocking(base, (uint8_t *)data, length); + memset(&master_xfer, 0, sizeof(master_xfer)); + master_xfer.slaveAddress = address >> 1; + master_xfer.direction = kI2C_Write; + master_xfer.data = (uint8_t *)data; + master_xfer.dataSize = length; - return length; -} + if (obj->next_repeated_start) { + master_xfer.flags |= kI2C_TransferRepeatedStartFlag; + } -int i2c_slave_write(i2c_t *obj, const char *data, int length) -{ - I2C_Type *base = i2c_addrs[obj->instance]; + if (!stop) { + master_xfer.flags |= kI2C_TransferNoStopFlag; + } - I2C_SlaveWriteBlocking(base, (uint8_t *)data, length); + obj->next_repeated_start = + master_xfer.flags & kI2C_TransferNoStopFlag ? 1 : 0; - /* Switch to receive mode. */ - base->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK); - /* Read dummy to release bus. */ - base->D; + if (I2C_MasterTransferBlocking(base, &master_xfer) != kStatus_Success) { + return I2C_ERROR_NO_SLAVE; + } - return length; + return length; } -void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask) +int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, + uint32_t length, bool stop) { - i2c_addrs[obj->instance]->A1 = address & 0xfe; + if ((length == 0) || (data == NULL)) { + return 0; + } + +#if DEVICE_I2CSLAVE + /* Slave block write */ + if (obj->is_slave) { + return i2c_slave_write(obj, data, length); + } +#endif // DEVICE_I2CSLAVE + + /* Master byte write */ + if (length == 1) { + return i2c_byte_write(obj, *(char*)data, stop); + } + + /* Master block write */ + return i2c_block_write(obj, address, data, length, stop); } -#endif -#endif +#endif // DEVICE_I2C diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/objects.h b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/objects.h index 405f2e17785..3200a5dca56 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/objects.h +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/objects.h @@ -68,6 +68,7 @@ struct analogin_s { struct i2c_s { uint32_t instance; uint8_t next_repeated_start; + bool is_slave; }; struct spi_s { diff --git a/targets/targets.json b/targets/targets.json index 3d5e15b2a68..b0ca8457de4 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -1408,6 +1408,7 @@ "ANALOGIN", "ANALOGOUT", "EMAC", + "I2C", "INTERRUPTIN", "PORTIN", "PORTINOUT", From 832221658903a42634e48d153dfad5a7194d4774 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 29 Oct 2018 13:14:40 +0000 Subject: [PATCH 18/45] feat(hal-i2c): Add synchronous HAL I2C implementation for STM32 --- targets/TARGET_STM/i2c_api.c | 298 +++++++++++++++++++++-------------- targets/targets.json | 3 + 2 files changed, 186 insertions(+), 115 deletions(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 121981e11e0..aca791c871f 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -261,7 +261,77 @@ void i2c_sw_reset(i2c_t *obj) handle->Instance->CR1 |= I2C_CR1_PE; } -void i2c_init(i2c_t *obj, PinName sda, PinName scl) +#ifdef DEVICE_I2CSLAVE + +static int i2c_slave_read(i2c_t *obj, char *data, int length) +{ + struct i2c_s *obj_s = I2C_S(obj); + I2C_HandleTypeDef *handle = &(obj_s->handle); + int count = 0; + int ret = 0; + uint32_t timeout = 0; + + /* Always use I2C_NEXT_FRAME as slave will just adapt to master requests */ + ret = HAL_I2C_Slave_Sequential_Receive_IT(handle, (uint8_t *) data, length, I2C_NEXT_FRAME); + + if (ret == HAL_OK) { + timeout = BYTE_TIMEOUT_US * (length + 1); + while (obj_s->pending_slave_rx_maxter_tx && (--timeout != 0)) { + wait_us(1); + } + + if (timeout != 0) { + count = length; + } else { + DEBUG_PRINTF("TIMEOUT or error in i2c_slave_read\r\n"); + } + } + return count; +} + +static int i2c_slave_write(i2c_t *obj, const char *data, int length) +{ + struct i2c_s *obj_s = I2C_S(obj); + I2C_HandleTypeDef *handle = &(obj_s->handle); + int count = 0; + int ret = 0; + uint32_t timeout = 0; + + /* Always use I2C_NEXT_FRAME as slave will just adapt to master requests */ + ret = HAL_I2C_Slave_Sequential_Transmit_IT(handle, (uint8_t *) data, length, I2C_NEXT_FRAME); + + if (ret == HAL_OK) { + timeout = BYTE_TIMEOUT_US * (length + 1); + while (obj_s->pending_slave_tx_master_rx && (--timeout != 0)) { + wait_us(1); + } + + if (timeout != 0) { + count = length; + } else { + DEBUG_PRINTF("TIMEOUT or error in i2c_slave_write\r\n"); + } + } + + return count; +} + +#endif + +void i2c_get_capabilities(i2c_capabilities_t *capabilities) +{ + if (capabilities == NULL) { + return; + } + + capabilities->minimum_frequency = 1; + capabilities->maximum_frequency = 1000000; + capabilities->supports_slave_mode = true; + capabilities->supports_10bit_addressing = true; + capabilities->supports_multi_master = true; +} + +void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave) { memset(obj, 0, sizeof(*obj)); i2c_init_internal(obj, sda, scl); @@ -345,7 +415,7 @@ void i2c_init_internal(i2c_t *obj, PinName sda, PinName scl) #if DEVICE_I2CSLAVE // I2C master by default - obj_s->slave = 0; + obj_s->slave = (is_slave ? 1 : 0); obj_s->pending_slave_tx_master_rx = 0; obj_s->pending_slave_rx_maxter_tx = 0; #endif @@ -356,9 +426,26 @@ void i2c_init_internal(i2c_t *obj, PinName sda, PinName scl) #ifdef I2C_IP_VERSION_V2 obj_s->pending_start = 0; #endif + + I2C_HandleTypeDef *handle = &(obj_s->handle); + + if (is_slave) { + HAL_I2C_EnableListen_IT(handle); + } else { + HAL_I2C_DisableListen_IT(handle); + } +} + +void i2c_free(i2c_t *obj) +{ + struct i2c_s *obj_s = I2C_S(obj); + + I2C_HandleTypeDef *handle = &(obj_s->handle); + + HAL_I2C_DeInit(handle); } -void i2c_frequency(i2c_t *obj, int hz) +uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) { int timeout; struct i2c_s *obj_s = I2C_S(obj); @@ -369,16 +456,17 @@ void i2c_frequency(i2c_t *obj, int hz) while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY)) && (--timeout != 0)); #ifdef I2C_IP_VERSION_V1 - handle->Init.ClockSpeed = hz; + handle->Init.ClockSpeed = frequency; handle->Init.DutyCycle = I2C_DUTYCYCLE_2; #endif #ifdef I2C_IP_VERSION_V2 /* Only predefined timing for below frequencies are supported */ - MBED_ASSERT((hz == 100000) || (hz == 400000) || (hz == 1000000)); - handle->Init.Timing = get_i2c_timing(hz); + MBED_ASSERT((frequency == 100000) || (frequency == 400000) || + (frequency == 1000000)); + handle->Init.Timing = get_i2c_timing(frequency); // Enable the Fast Mode Plus capability - if (hz == 1000000) { + if (frequency == 1000000) { #if defined(I2C1_BASE) && defined(__HAL_SYSCFG_FASTMODEPLUS_ENABLE) && defined (I2C_FASTMODEPLUS_I2C1) if (obj_s->i2c == I2C_1) { HAL_I2CEx_EnableFastModePlus(I2C_FASTMODEPLUS_I2C1); @@ -439,7 +527,27 @@ void i2c_frequency(i2c_t *obj, int hz) HAL_I2C_Init(handle); /* store frequency for timeout computation */ - obj_s->hz = hz; + obj_s->hz = frequency; + + return obj_s->hz; +} + +void i2c_timeout(i2c_t *obj, uint32_t timeout) +{ + struct i2c_s *obj_s = I2C_S(obj); + I2C_HandleTypeDef *handle = &(obj_s->handle); + + // wait before init + int wait = BYTE_TIMEOUT; + while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY)) && (--wait != 0)) {} + + // I2C configuration + handle->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + handle->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; + handle->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; + handle->Init.NoStretchMode = (timeout == 0) ? I2C_NOSTRETCH_DISABLE + : I2C_NOSTRETCH_ENABLE; + HAL_I2C_Init(handle); } i2c_t *get_i2c_obj(I2C_HandleTypeDef *hi2c) @@ -456,22 +564,13 @@ i2c_t *get_i2c_obj(I2C_HandleTypeDef *hi2c) return (obj); } -void i2c_reset(i2c_t *obj) -{ - struct i2c_s *obj_s = I2C_S(obj); - /* As recommended in i2c_api.h, mainly send stop */ - i2c_stop(obj); - /* then re-init */ - i2c_init_internal(obj, obj_s->sda, obj_s->scl); -} - /* * UNITARY APIS. * For very basic operations, direct registers access is needed * There are 2 different IPs version that need to be supported */ #ifdef I2C_IP_VERSION_V1 -int i2c_start(i2c_t *obj) +bool i2c_start(i2c_t *obj) { int timeout; @@ -486,7 +585,7 @@ int i2c_start(i2c_t *obj) timeout = FLAG_TIMEOUT; while ((handle->Instance->CR1 & I2C_CR1_STOP) == I2C_CR1_STOP) { if ((timeout--) == 0) { - return 1; + return true; } } @@ -497,14 +596,14 @@ int i2c_start(i2c_t *obj) timeout = FLAG_TIMEOUT; while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_SB) == RESET) { if ((timeout--) == 0) { - return 1; + return true; } } - return 0; + return false; } -int i2c_stop(i2c_t *obj) +bool i2c_stop(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); I2C_TypeDef *i2c = (I2C_TypeDef *)obj_s->i2c; @@ -516,10 +615,17 @@ int i2c_stop(i2c_t *obj) * re-init HAL state */ if (obj_s->XferOperation != I2C_FIRST_AND_LAST_FRAME) { - i2c_init_internal(obj, obj_s->sda, obj_s->scl); + +#ifdef DEVICE_I2CSLAVE + const bool is_slave = obj_s->slave ? true : false; +#else + const bool is_slave = false; +#endif + + i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); } - return 0; + return false; } int i2c_byte_read(i2c_t *obj, int last) @@ -576,24 +682,25 @@ int i2c_byte_write(i2c_t *obj, int data) #endif //I2C_IP_VERSION_V1 #ifdef I2C_IP_VERSION_V2 -int i2c_start(i2c_t *obj) +bool i2c_start(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); /* This I2C IP doesn't */ obj_s->pending_start = 1; - return 0; + return false; } -int i2c_stop(i2c_t *obj) +bool i2c_stop(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); int timeout = FLAG_TIMEOUT; #if DEVICE_I2CSLAVE if (obj_s->slave) { + const bool is_slave = obj_s->slave ? true : false; /* re-init slave when stop is requested */ - i2c_init_internal(obj, obj_s->sda, obj_s->scl); - return 0; + i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); + return false; } #endif // Disable reload mode @@ -635,10 +742,10 @@ int i2c_stop(i2c_t *obj) /* In case of mixed usage of the APIs (unitary + SYNC) * re-init HAL state */ if (obj_s->XferOperation != I2C_FIRST_AND_LAST_FRAME) { - i2c_init_internal(obj, obj_s->sda, obj_s->scl); + i2c_init(obj, obj_s->sda, obj_s->scl, false); } - return 0; + return false; } int i2c_byte_read(i2c_t *obj, int last) @@ -753,10 +860,17 @@ int i2c_byte_write(i2c_t *obj, int data) /* * SYNC APIS */ -int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) +int32_t i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool last) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); + +#ifdef DEVICE_I2CSLAVE + if (obj_s->slave == 1) { + return i2c_slave_read(obj, data, length); + } +#endif + int count = I2C_ERROR_BUS_BUSY, ret = 0; uint32_t timeout = 0; @@ -764,14 +878,14 @@ int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) uint32_t op1 = I2C_FIRST_AND_LAST_FRAME; uint32_t op2 = I2C_LAST_FRAME; if ((obj_s->XferOperation == op1) || (obj_s->XferOperation == op2)) { - if (stop) { + if (last) { obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; } else { obj_s->XferOperation = I2C_FIRST_FRAME; } } else if ((obj_s->XferOperation == I2C_FIRST_FRAME) || (obj_s->XferOperation == I2C_NEXT_FRAME)) { - if (stop) { + if (last) { obj_s->XferOperation = I2C_LAST_FRAME; } else { obj_s->XferOperation = I2C_NEXT_FRAME; @@ -799,7 +913,12 @@ int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) if ((timeout == 0) || (obj_s->event != I2C_EVENT_TRANSFER_COMPLETE)) { DEBUG_PRINTF(" TIMEOUT or error in i2c_read\r\n"); /* re-init IP to try and get back in a working state */ - i2c_init_internal(obj, obj_s->sda, obj_s->scl); +#ifdef DEVICE_I2CSLAVE + const bool is_slave = obj_s->slave ? true : false; +#else + const bool is_slave = false; +#endif + i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); } else { count = length; } @@ -810,10 +929,17 @@ int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) return count; } -int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) +int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length, bool stop) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); + +#ifdef DEVICE_I2CSLAVE + if (obj_s->slave == 1) { + return i2c_slave_write(obj, data, length); + } +#endif + int count = I2C_ERROR_BUS_BUSY, ret = 0; uint32_t timeout = 0; @@ -853,7 +979,12 @@ int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) if ((timeout == 0) || (obj_s->event != I2C_EVENT_TRANSFER_COMPLETE)) { DEBUG_PRINTF(" TIMEOUT or error in i2c_write\r\n"); /* re-init IP to try and get back in a working state */ - i2c_init_internal(obj, obj_s->sda, obj_s->scl); +#ifdef DEVICE_I2CSLAVE + const bool is_slave = obj_s->slave ? true : false; +#else + const bool is_slave = false; +#endif + i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); } else { count = length; } @@ -905,7 +1036,7 @@ void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) struct i2c_s *obj_s = I2C_S(obj); #if DEVICE_I2CSLAVE I2C_HandleTypeDef *handle = &(obj_s->handle); - uint32_t address = 0; + uint16_t address = 0; /* Store address to handle it after reset */ if (obj_s->slave) { address = handle->Init.OwnAddress1; @@ -915,13 +1046,18 @@ void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) DEBUG_PRINTF("HAL_I2C_ErrorCallback:%d, index=%d\r\n", (int) hi2c->ErrorCode, obj_s->index); /* re-init IP to try and get back in a working state */ - i2c_init_internal(obj, obj_s->sda, obj_s->scl); +#ifdef DEVICE_I2CSLAVE + const bool is_slave = obj_s->slave ? true : false; +#else + const bool is_slave = false; +#endif + i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); #if DEVICE_I2CSLAVE /* restore slave address */ if (address != 0) { obj_s->slave = 1; - i2c_slave_address(obj, 0, address, 0); + i2c_slave_address(obj, address); } #endif @@ -951,13 +1087,13 @@ const PinMap *i2c_slave_scl_pinmap() #if DEVICE_I2CSLAVE /* SLAVE API FUNCTIONS */ -void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask) +void i2c_slave_address(i2c_t *obj, uint16_t address) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); // I2C configuration - handle->Init.OwnAddress1 = address; + handle->Init.OwnAddress1 = address; HAL_I2C_Init(handle); i2c_ev_err_enable(obj, i2c_get_irq_handler(obj)); @@ -965,21 +1101,6 @@ void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask) HAL_I2C_EnableListen_IT(handle); } -void i2c_slave_mode(i2c_t *obj, int enable_slave) -{ - - struct i2c_s *obj_s = I2C_S(obj); - I2C_HandleTypeDef *handle = &(obj_s->handle); - - if (enable_slave) { - obj_s->slave = 1; - HAL_I2C_EnableListen_IT(handle); - } else { - obj_s->slave = 0; - HAL_I2C_DisableListen_IT(handle); - } -} - // See I2CSlave.h #define NoData 0 // the slave has not been addressed #define ReadAddressed 1 // the master has requested a read from this slave (slave = transmitter) @@ -1025,75 +1146,22 @@ void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c) HAL_I2C_EnableListen_IT(hi2c); } -int i2c_slave_receive(i2c_t *obj) +i2c_slave_status_t i2c_slave_status(i2c_t *obj) { - struct i2c_s *obj_s = I2C_S(obj); - int retValue = NoData; + + i2c_slave_status_t retValue = NO_ADDRESS; if (obj_s->pending_slave_rx_maxter_tx) { - retValue = WriteAddressed; + retValue = WRITE; } if (obj_s->pending_slave_tx_master_rx) { - retValue = ReadAddressed; + retValue = READ; } return (retValue); } - -int i2c_slave_read(i2c_t *obj, char *data, int length) -{ - struct i2c_s *obj_s = I2C_S(obj); - I2C_HandleTypeDef *handle = &(obj_s->handle); - int count = 0; - int ret = 0; - uint32_t timeout = 0; - - /* Always use I2C_NEXT_FRAME as slave will just adapt to master requests */ - ret = HAL_I2C_Slave_Sequential_Receive_IT(handle, (uint8_t *) data, length, I2C_NEXT_FRAME); - - if (ret == HAL_OK) { - timeout = BYTE_TIMEOUT_US * (length + 1); - while (obj_s->pending_slave_rx_maxter_tx && (--timeout != 0)) { - wait_us(1); - } - - if (timeout != 0) { - count = length; - } else { - DEBUG_PRINTF("TIMEOUT or error in i2c_slave_read\r\n"); - } - } - return count; -} - -int i2c_slave_write(i2c_t *obj, const char *data, int length) -{ - struct i2c_s *obj_s = I2C_S(obj); - I2C_HandleTypeDef *handle = &(obj_s->handle); - int count = 0; - int ret = 0; - uint32_t timeout = 0; - - /* Always use I2C_NEXT_FRAME as slave will just adapt to master requests */ - ret = HAL_I2C_Slave_Sequential_Transmit_IT(handle, (uint8_t *) data, length, I2C_NEXT_FRAME); - - if (ret == HAL_OK) { - timeout = BYTE_TIMEOUT_US * (length + 1); - while (obj_s->pending_slave_tx_master_rx && (--timeout != 0)) { - wait_us(1); - } - - if (timeout != 0) { - count = length; - } else { - DEBUG_PRINTF("TIMEOUT or error in i2c_slave_write\r\n"); - } - } - - return count; -} #endif // DEVICE_I2CSLAVE #if DEVICE_I2C_ASYNCH diff --git a/targets/targets.json b/targets/targets.json index b0ca8457de4..b3aa2df582c 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -1409,6 +1409,7 @@ "ANALOGOUT", "EMAC", "I2C", + "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", @@ -1787,6 +1788,8 @@ "LPTICKER", "RTC", "ANALOGIN", + "I2C", + "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", From 9b54bc0d5362bbe7f1980a47f1b56ae8fedbf60f Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 5 Nov 2018 17:03:47 +0000 Subject: [PATCH 19/45] feat(hal-i2c): Remove unused DMAUsage member from I2C class --- drivers/I2C.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/I2C.h b/drivers/I2C.h index 928be89f059..e501774ac7d 100644 --- a/drivers/I2C.h +++ b/drivers/I2C.h @@ -210,7 +210,6 @@ class I2C : private NonCopyable { void irq_handler_asynch(void); event_callback_t _callback; CThunk _irq; - DMAUsage _usage; bool _deep_sleep_locked; #endif #endif From 79185d36eaae2ff460dae0d07333c9138e620f75 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Wed, 17 Oct 2018 14:33:39 +0100 Subject: [PATCH 20/45] feat(hal-i2c): Add asynchronous HAL I2C implementation for STM32 --- hal/i2c_api.h | 2 +- targets/TARGET_STM/i2c_api.c | 115 +++++++++++++++++++++++------------ targets/targets.json | 1 + 3 files changed, 78 insertions(+), 40 deletions(-) diff --git a/hal/i2c_api.h b/hal/i2c_api.h index 4ac0c1a8dc2..3d383cad044 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -322,7 +322,7 @@ void i2c_slave_address(i2c_t *obj, uint16_t address); */ void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, void *rx, uint32_t rx_length, uint16_t address, - bool stop, i2c_async_handler_f handler); + bool stop, i2c_async_handler_f handler, void *ctx); /** Abort asynchronous transfer * diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index aca791c871f..c462fce7277 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -1000,6 +1000,7 @@ void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) /* Get object ptr based on handler ptr */ i2c_t *obj = get_i2c_obj(hi2c); struct i2c_s *obj_s = I2C_S(obj); + I2C_HandleTypeDef *handle = &(obj_s->handle); #if DEVICE_I2C_ASYNCH /* Handle potential Tx/Rx use case */ @@ -1017,6 +1018,22 @@ void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) /* Set event flag */ obj_s->event = I2C_EVENT_TRANSFER_COMPLETE; } + +#if DEVICE_I2C_ASYNCH + /* */ + if (obj->handler == NULL) { + return; + } + + i2c_async_event_t event; + event.transferred = handle->XferCount; + event.error = false; + + obj->handler(obj, &event, obj->ctx); + + obj->handler = NULL; + obj->ctx = NULL; +#endif // DEVICE_I2C_ASYNCH } void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) @@ -1024,9 +1041,26 @@ void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) /* Get object ptr based on handler ptr */ i2c_t *obj = get_i2c_obj(hi2c); struct i2c_s *obj_s = I2C_S(obj); + I2C_HandleTypeDef *handle = &(obj_s->handle); /* Set event flag */ obj_s->event = I2C_EVENT_TRANSFER_COMPLETE; + +#if DEVICE_I2C_ASYNCH + /* */ + if (obj->handler == NULL) { + return; + } + + i2c_async_event_t event; + event.transferred = handle->XferCount; + event.error = false; + + obj->handler(obj, &event, obj->ctx); + + obj->handler = NULL; + obj->ctx = NULL; +#endif // DEVICE_I2C_ASYNCH } void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) @@ -1063,6 +1097,22 @@ void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) /* Keep Set event flag */ obj_s->event = I2C_EVENT_ERROR; + +#if DEVICE_I2C_ASYNCH + /* */ + if (obj->handler == NULL) { + return; + } + + i2c_async_event_t event; + event.transferred = handle->XferCount; + event.error = true; + + obj->handler(obj, &event, obj->ctx); + + obj->handler = NULL; + obj->ctx = NULL; +#endif // DEVICE_I2C_ASYNCH } const PinMap *i2c_master_sda_pinmap() @@ -1179,34 +1229,48 @@ void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c) /* Set event flag */ obj_s->event = I2C_EVENT_ERROR; -} -void i2c_transfer_asynch(i2c_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint32_t address, uint32_t stop, uint32_t handler, uint32_t event, DMAUsage hint) -{ + /* */ + if (obj->handler == NULL) { + return; + } - // TODO: DMA usage is currently ignored by this way - (void) hint; + i2c_async_event_t event; + event.transferred = handle->XferCount; + event.error = false; + obj->handler(obj, &event, obj->ctx); + + obj->handler = NULL; + obj->ctx = NULL; +} + +void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, + void *rx, uint32_t rx_length, uint16_t address, + bool stop, i2c_async_handler_f handler, void *ctx) +{ struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); /* Update object */ obj->tx_buff.buffer = (void *)tx; obj->tx_buff.length = tx_length; - obj->tx_buff.pos = 0; - obj->tx_buff.width = 8; + obj->tx_buff.pos = 0; + obj->tx_buff.width = 8; obj->rx_buff.buffer = (void *)rx; obj->rx_buff.length = rx_length; - obj->rx_buff.pos = SIZE_MAX; - obj->rx_buff.width = 8; + obj->rx_buff.pos = SIZE_MAX; + obj->rx_buff.width = 8; + + obj->handler = handler; + obj->ctx = ctx; - obj_s->available_events = event; obj_s->event = 0; obj_s->address = address; obj_s->stop = stop; - i2c_ev_err_enable(obj, handler); + i2c_ev_err_enable(obj, i2c_get_irq_handler(obj)); /* Set operation step depending if stop sending required or not */ if ((tx_length && !rx_length) || (!tx_length && rx_length)) { @@ -1248,34 +1312,7 @@ void i2c_transfer_asynch(i2c_t *obj, const void *tx, size_t tx_length, void *rx, } } - -uint32_t i2c_irq_handler_asynch(i2c_t *obj) -{ - - struct i2c_s *obj_s = I2C_S(obj); - I2C_HandleTypeDef *handle = &(obj_s->handle); - - HAL_I2C_EV_IRQHandler(handle); - HAL_I2C_ER_IRQHandler(handle); - - /* Return I2C event status */ - return (obj_s->event & obj_s->available_events); -} - -uint8_t i2c_active(i2c_t *obj) -{ - - struct i2c_s *obj_s = I2C_S(obj); - I2C_HandleTypeDef *handle = &(obj_s->handle); - - if (handle->State == HAL_I2C_STATE_READY) { - return 0; - } else { - return 1; - } -} - -void i2c_abort_asynch(i2c_t *obj) +void i2c_abort_async(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); diff --git a/targets/targets.json b/targets/targets.json index b3aa2df582c..2fb7777615a 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -1790,6 +1790,7 @@ "ANALOGIN", "I2C", "I2CSLAVE", + "I2C_ASYNCH", "INTERRUPTIN", "PORTIN", "PORTINOUT", From b8fce70ba9bd6b5b23aa87810f74dd3ccf79a4db Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Tue, 13 Nov 2018 11:58:26 +0000 Subject: [PATCH 21/45] fix(hal-i2c): Fix issue with master byte read/write failing on K64F Remove byte_read/byte_write functions and use block read and writes for single byte accesses. --- .../TARGET_MCU_K64F/drivers/fsl_i2c.c | 12 ++-- .../TARGET_MCUXpresso_MCUS/api/i2c_api.c | 69 ------------------- 2 files changed, 6 insertions(+), 75 deletions(-) diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/drivers/fsl_i2c.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/drivers/fsl_i2c.c index d5145dc821c..c3032d0026a 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/drivers/fsl_i2c.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/drivers/fsl_i2c.c @@ -1358,12 +1358,12 @@ void I2C_SlaveReadBlocking(I2C_Type *base, uint8_t *rxBuff, size_t rxSize) #endif /* FSL_FEATURE_I2C_HAS_START_STOP_DETECT */ /* Wait for address match and int pending flag. */ - // while (!(base->S & kI2C_AddressMatchFlag)) - // { - // } - // while (!(base->S & kI2C_IntPendingFlag)) - // { - // } + while (!(base->S & kI2C_AddressMatchFlag)) + { + } + while (!(base->S & kI2C_IntPendingFlag)) + { + } /* Read dummy to release bus. */ dummy = base->D; diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c index 0e7a648d3aa..8bfd4efe0e1 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c @@ -181,34 +181,6 @@ static int i2c_block_read(i2c_t *obj, uint16_t address, void *data, uint32_t len return length; } -static int i2c_byte_read(i2c_t *obj, void *data, uint32_t length, bool last) -{ - MBED_ASSERT(length == 1); - - I2C_Type *base = i2c_addrs[obj->instance]; - - /* Setup the I2C peripheral to receive data. */ - base->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK); - - if (last) - base->C1 |= I2C_C1_TXAK_MASK; // NACK - - *(char*)data = (base->D & 0xFF); - - /* Change direction to Tx to avoid extra clocks. */ - base->C1 |= I2C_C1_TX_MASK; - - /* Wait until data transfer complete. */ - while (!(base->S & kI2C_IntPendingFlag)) - { - } - - /* Clear the IICIF flag. */ - base->S = kI2C_IntPendingFlag; - - return length; -} - #if DEVICE_I2CSLAVE static int i2c_slave_read(i2c_t *obj, void *data, uint32_t length) @@ -272,36 +244,10 @@ int32_t i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, } #endif // DEVICE_I2CSLAVE - /* Master byte read */ - if (length == 1) { - return i2c_byte_read(obj, data, length, last); - } - /* Master block read */ return i2c_block_read(obj, address, data, length, last); } -static int i2c_byte_write(i2c_t *obj, char data, bool stop) -{ - I2C_Type *base = i2c_addrs[obj->instance]; - - int ret_value = 1; - - /* Setup the I2C peripheral to transmit data. */ - base->C1 |= I2C_C1_TX_MASK; - - /* Send a byte of data. */ - base->D = data; - - /* Wait until data transfer complete. */ - while (!(base->S & kI2C_IntPendingFlag)) { - } - - const uint8_t statusFlags = base->S; - - /* Clear the IICIF flag. */ - base->S = kI2C_IntPendingFlag; - const PinMap *i2c_master_sda_pinmap() { return PinMap_I2C_SDA; @@ -322,16 +268,6 @@ const PinMap *i2c_slave_scl_pinmap() return PinMap_I2C_SCL; } - - /* Check if no acknowledgement (NAK) */ - if (statusFlags & kI2C_ReceiveNakFlag) { - base->S = kI2C_ReceiveNakFlag; - ret_value = 0; - } - - return ret_value; -} - int i2c_block_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length, bool stop) { I2C_Type *base = i2c_addrs[obj->instance]; @@ -395,11 +331,6 @@ int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, } #endif // DEVICE_I2CSLAVE - /* Master byte write */ - if (length == 1) { - return i2c_byte_write(obj, *(char*)data, stop); - } - /* Master block write */ return i2c_block_write(obj, address, data, length, stop); } From 5722179d305d1a2aadaddc5509bf76355c1d5f13 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Tue, 13 Nov 2018 13:29:35 +0000 Subject: [PATCH 22/45] fix(hal-i2c): Add missing i2c_free function to API --- hal/i2c_api.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hal/i2c_api.h b/hal/i2c_api.h index 3d383cad044..27793b3acf0 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -121,6 +121,12 @@ void i2c_get_capabilities(i2c_capabilities_t *capabilities); */ void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave); +/** Release the I2C object. + * + * @param obj The I2C object + */ +void i2c_free(i2c_t *obj); + /** Configure the frequency in Hz the I2C peripheral should operate at. * * @param obj The I2C object From 02721276ddf4dd287f7edbb1ff02185c39762008 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Wed, 28 Nov 2018 14:11:49 +0000 Subject: [PATCH 23/45] fix(hal-i2c): Fix incorrect minimum frequency in STM I2C implementation --- targets/TARGET_STM/i2c_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index c462fce7277..8e9264ab43f 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -324,7 +324,7 @@ void i2c_get_capabilities(i2c_capabilities_t *capabilities) return; } - capabilities->minimum_frequency = 1; + capabilities->minimum_frequency = 100000; capabilities->maximum_frequency = 1000000; capabilities->supports_slave_mode = true; capabilities->supports_10bit_addressing = true; From a7ce674afaafb2702d8b0ca414d6700327d73e01 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Wed, 28 Nov 2018 14:19:43 +0000 Subject: [PATCH 24/45] fix(hal-i2c): Fix STM implementation calling event handler too early An async transfer involving a write and a read is issue in two stages, the write is invoked, then in the write complete callback the read is invoked followed by the read complete callback. Currently the read and write complete callbacks invoke the HAL API event handler, but this will be incorrectly called halfway through the transfer after the write is completed. To fix, only call the HAL event handler in the write complete callback if there is no pending read. --- targets/TARGET_STM/i2c_api.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 8e9264ab43f..a82b393cc68 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -1017,23 +1017,23 @@ void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) { /* Set event flag */ obj_s->event = I2C_EVENT_TRANSFER_COMPLETE; - } #if DEVICE_I2C_ASYNCH - /* */ - if (obj->handler == NULL) { - return; - } + /* */ + if (obj->handler == NULL) { + return; + } - i2c_async_event_t event; - event.transferred = handle->XferCount; - event.error = false; + i2c_async_event_t event; + event.transferred = handle->XferCount; + event.error = false; - obj->handler(obj, &event, obj->ctx); + obj->handler(obj, &event, obj->ctx); - obj->handler = NULL; - obj->ctx = NULL; + obj->handler = NULL; + obj->ctx = NULL; #endif // DEVICE_I2C_ASYNCH + } } void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) From e837039d7ae5068f2d0455639c825085b3f62274 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 3 Dec 2018 16:48:59 +0000 Subject: [PATCH 25/45] refactor(hal-i2c): Rename HAL slave status enum to match Driver API --- hal/i2c_api.h | 8 ++++---- .../TARGET_MCUXpresso_MCUS/api/i2c_api.c | 5 +++-- targets/TARGET_STM/i2c_api.c | 15 ++++----------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/hal/i2c_api.h b/hal/i2c_api.h index 27793b3acf0..2cce09a9fd0 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -282,10 +282,10 @@ const PinMap *i2c_slave_scl_pinmap(void); */ typedef enum { - NO_ADDRESS = 0, - READ = 1, - BROADCAST = 2, - WRITE = 3 + NoData = 0, // Slave has not been addressed. + ReadAddressed = 1, // Master has requested a read from this slave. + WriteGeneral = 2, // Master is writing to all slaves. + WriteAddressed = 3 // Master is writing to this slave. } i2c_slave_status_t; /** Check to see if the I2C slave has been addressed. diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c index 8bfd4efe0e1..850e1dd6a91 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c @@ -204,10 +204,11 @@ i2c_slave_status_t i2c_slave_status(i2c_t *obj) uint32_t status_flags = I2C_SlaveGetStatusFlags(i2c_addrs[obj->instance]); if ((status_flags & kI2C_AddressMatchFlag) == 0) { - return NO_ADDRESS; + return NoData; } - return ((status_flags & kI2C_TransferDirectionFlag) != 0) ? READ : WRITE; + return ((status_flags & kI2C_TransferDirectionFlag) != 0) ? ReadAddressed + : WriteAddressed; } static int i2c_slave_write(i2c_t *obj, const void *data, uint32_t length) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index a82b393cc68..6fa5856a89e 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -1151,13 +1151,6 @@ void i2c_slave_address(i2c_t *obj, uint16_t address) HAL_I2C_EnableListen_IT(handle); } -// See I2CSlave.h -#define NoData 0 // the slave has not been addressed -#define ReadAddressed 1 // the master has requested a read from this slave (slave = transmitter) -#define WriteGeneral 2 // the master is writing to all slave -#define WriteAddressed 3 // the master is writing to this slave (slave = receiver) - - void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode) { /* Get object ptr based on handler ptr */ @@ -1200,17 +1193,17 @@ i2c_slave_status_t i2c_slave_status(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); - i2c_slave_status_t retValue = NO_ADDRESS; + i2c_slave_status_t retValue = NoData; if (obj_s->pending_slave_rx_maxter_tx) { - retValue = WRITE; + retValue = WriteAddressed; } if (obj_s->pending_slave_tx_master_rx) { - retValue = READ; + retValue = ReadAddressed; } - return (retValue); + return retValue; } #endif // DEVICE_I2CSLAVE From 2d0d9529233a86a6b44fd0ba7a5a8a6c72615859 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 3 Dec 2018 16:21:36 +0000 Subject: [PATCH 26/45] fix(hal-i2c): Return the correct number of transferred bytes in STM32 The STM32 attribute XferCount does not contain the number of transferred bytes, it contains the number of bytes remaining to be sent. Calculate the number of bytes that have been sent by subtracting this number from the length of the respective buffer. To fix, add an additional attribute to the asynchronous event object. For transfers that include sending and receiving data only counting the number of bytes sent isn't sufficient as unlike SPI the size of the send and receive buffer may be different. --- hal/i2c_api.h | 3 ++- targets/TARGET_STM/i2c_api.c | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/hal/i2c_api.h b/hal/i2c_api.h index 2cce09a9fd0..f04865445fe 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -50,7 +50,8 @@ typedef struct i2c i2c_t; typedef struct i2c_async_event { - uint32_t transferred; + uint32_t sent_bytes; + uint32_t received_bytes; bool error; } i2c_async_event_t; diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 6fa5856a89e..690bce28449 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -1025,8 +1025,9 @@ void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) } i2c_async_event_t event; - event.transferred = handle->XferCount; - event.error = false; + event.sent_bytes = (obj->tx_buff.length - handle->XferCount); + event.received_bytes = 0; + event.error = false; obj->handler(obj, &event, obj->ctx); @@ -1053,8 +1054,9 @@ void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) } i2c_async_event_t event; - event.transferred = handle->XferCount; - event.error = false; + event.sent_bytes = obj->tx_buff.length; + event.received_bytes = (obj->rx_buff.length - handle->XferCount); + event.error = false; obj->handler(obj, &event, obj->ctx); @@ -1105,8 +1107,9 @@ void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) } i2c_async_event_t event; - event.transferred = handle->XferCount; - event.error = true; + event.sent_bytes = (obj->tx_buff.length - handle->XferCount); + event.received_bytes = 0; + event.error = true; obj->handler(obj, &event, obj->ctx); @@ -1229,8 +1232,9 @@ void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c) } i2c_async_event_t event; - event.transferred = handle->XferCount; - event.error = false; + event.sent_bytes = (obj->tx_buff.length - handle->XferCount); + event.received_bytes = 0; + event.error = false; obj->handler(obj, &event, obj->ctx); From 1510f5a5975ea89dfcd520bfda41d5436a462c1e Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 3 Dec 2018 18:16:40 +0000 Subject: [PATCH 27/45] fix(hal-i2c): Fix incorrect byte counts during aborted R/W transfers The async event callback returns the number of bytes sent and received during a transfer. For failed transfers that involve a write and read we don't easily know which part of the transfer fails, only the number of bytes remaining in the current operation. To fix, set a flag when the write completes successfully, a failure when this flag is set means the read stopped early and we can calculate the number of bytes sent for each operation. --- .../TARGET_STM32F0/common_objects.h | 1 + .../TARGET_STM32F1/common_objects.h | 1 + targets/TARGET_STM/TARGET_STM32F2/objects.h | 1 + .../TARGET_STM32F3/common_objects.h | 1 + .../TARGET_STM32F4/common_objects.h | 1 + .../TARGET_STM32F7/common_objects.h | 1 + .../TARGET_STM32L0/common_objects.h | 1 + .../TARGET_STM32L1/common_objects.h | 1 + .../TARGET_STM32L4/common_objects.h | 1 + targets/TARGET_STM/i2c_api.c | 33 +++++++++++++++---- 10 files changed, 36 insertions(+), 6 deletions(-) diff --git a/targets/TARGET_STM/TARGET_STM32F0/common_objects.h b/targets/TARGET_STM/TARGET_STM32F0/common_objects.h index 1491acc4a82..de70da37c50 100644 --- a/targets/TARGET_STM/TARGET_STM32F0/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F0/common_objects.h @@ -108,6 +108,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; + uint8_t tx_complete; #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32F1/common_objects.h b/targets/TARGET_STM/TARGET_STM32F1/common_objects.h index 827aa644ded..900f39f54c4 100644 --- a/targets/TARGET_STM/TARGET_STM32F1/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F1/common_objects.h @@ -107,6 +107,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; + uint8_t tx_complete #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32F2/objects.h b/targets/TARGET_STM/TARGET_STM32F2/objects.h index b463dea41ac..cd863e5a797 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F2/objects.h @@ -124,6 +124,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; + uint8_t tx_complete #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32F3/common_objects.h b/targets/TARGET_STM/TARGET_STM32F3/common_objects.h index c2a227901b7..87a4e132478 100644 --- a/targets/TARGET_STM/TARGET_STM32F3/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F3/common_objects.h @@ -108,6 +108,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; + uint8_t tx_complete #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32F4/common_objects.h b/targets/TARGET_STM/TARGET_STM32F4/common_objects.h index f4a7ef74f4c..0b172323505 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F4/common_objects.h @@ -107,6 +107,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; + uint8_t tx_complete #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32F7/common_objects.h b/targets/TARGET_STM/TARGET_STM32F7/common_objects.h index a59d41630b8..cf897f9ece9 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F7/common_objects.h @@ -108,6 +108,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; + uint8_t tx_complete #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32L0/common_objects.h b/targets/TARGET_STM/TARGET_STM32L0/common_objects.h index 5f0acc6132e..956f9aca67a 100644 --- a/targets/TARGET_STM/TARGET_STM32L0/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L0/common_objects.h @@ -108,6 +108,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; + uint8_t tx_complete #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32L1/common_objects.h b/targets/TARGET_STM/TARGET_STM32L1/common_objects.h index e25271a44d8..6d819788c81 100644 --- a/targets/TARGET_STM/TARGET_STM32L1/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L1/common_objects.h @@ -107,6 +107,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; + uint8_t tx_complete #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32L4/common_objects.h b/targets/TARGET_STM/TARGET_STM32L4/common_objects.h index 8f1beef3ad1..0713021c15b 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L4/common_objects.h @@ -108,6 +108,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; + uint8_t tx_complete #endif }; diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 690bce28449..343c55cc040 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -420,6 +420,10 @@ void i2c_init_internal(i2c_t *obj, PinName sda, PinName scl) obj_s->pending_slave_rx_maxter_tx = 0; #endif +#if DEVICE_I2C_ASYNCH + obj_s->tx_complete = 0; +#endif // DEVICE_I2C_ASYNCH + // I2C Xfer operation init obj_s->event = 0; obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; @@ -1002,6 +1006,8 @@ void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); + obj_s->tx_complete = 1; + #if DEVICE_I2C_ASYNCH /* Handle potential Tx/Rx use case */ if ((obj->tx_buff.length) && (obj->rx_buff.length)) { @@ -1107,9 +1113,15 @@ void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) } i2c_async_event_t event; - event.sent_bytes = (obj->tx_buff.length - handle->XferCount); - event.received_bytes = 0; - event.error = true; + event.error = true; + + if (!obj_s->tx_complete) { + event.sent_bytes = (obj->tx_buff.length - handle->XferCount); + event.received_bytes = 0; + } else { + event.sent_bytes = (obj->tx_buff.length); + event.received_bytes = (obj->rx_buff.length - handle->XferCount); + } obj->handler(obj, &event, obj->ctx); @@ -1231,15 +1243,23 @@ void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c) return; } +#if DEVICE_I2C_ASYNCH i2c_async_event_t event; - event.sent_bytes = (obj->tx_buff.length - handle->XferCount); - event.received_bytes = 0; - event.error = false; + event.error = true; + + if (!obj_s->tx_complete) { + event.sent_bytes = (obj->tx_buff.length - handle->XferCount); + event.received_bytes = 0; + } else { + event.sent_bytes = (obj->tx_buff.length); + event.received_bytes = (obj->rx_buff.length - handle->XferCount); + } obj->handler(obj, &event, obj->ctx); obj->handler = NULL; obj->ctx = NULL; +#endif // DEVICE_I2C_ASYNCH } void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, @@ -1266,6 +1286,7 @@ void i2c_transfer_async(i2c_t *obj, const void *tx, uint32_t tx_length, obj_s->event = 0; obj_s->address = address; obj_s->stop = stop; + obj_s->tx_complete = 0; i2c_ev_err_enable(obj, i2c_get_irq_handler(obj)); From 42cd01cf328933d04524da61f8f23159b1c1c836 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Wed, 5 Dec 2018 18:24:43 +0000 Subject: [PATCH 28/45] refactor(hal-i2c): Change STOP function to return success or failure --- hal/i2c_api.h | 2 +- .../TARGET_MCUXpresso_MCUS/api/i2c_api.c | 2 +- targets/TARGET_STM/i2c_api.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hal/i2c_api.h b/hal/i2c_api.h index f04865445fe..7e66695a8f1 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -157,7 +157,7 @@ bool i2c_start(i2c_t *obj); /** Send STOP command * * @param obj The I2C object - * @returns True if slave responds with ACK, false otherwise. + * @returns True if STOP command was sent succesfully, false otherwise. */ bool i2c_stop(i2c_t *obj); diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c index 850e1dd6a91..95bfd199d12 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c @@ -134,7 +134,7 @@ bool i2c_stop(i2c_t *obj) { I2C_Type *base = i2c_addrs[obj->instance]; - return ((I2C_MasterStop(base)) != kStatus_Success) ? true : false; + return ((I2C_MasterStop(base)) == kStatus_Success); } uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 343c55cc040..64dc2b91236 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -629,7 +629,7 @@ bool i2c_stop(i2c_t *obj) i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); } - return false; + return true; } int i2c_byte_read(i2c_t *obj, int last) @@ -704,7 +704,7 @@ bool i2c_stop(i2c_t *obj) const bool is_slave = obj_s->slave ? true : false; /* re-init slave when stop is requested */ i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); - return false; + return true; } #endif // Disable reload mode @@ -715,7 +715,7 @@ bool i2c_stop(i2c_t *obj) timeout = FLAG_TIMEOUT; while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXIS)) { if ((timeout--) == 0) { - return I2C_ERROR_BUS_BUSY; + return false; } } } @@ -726,7 +726,7 @@ bool i2c_stop(i2c_t *obj) timeout = FLAG_TIMEOUT; while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_STOPF)) { if ((timeout--) == 0) { - return I2C_ERROR_BUS_BUSY; + return false; } } @@ -749,7 +749,7 @@ bool i2c_stop(i2c_t *obj) i2c_init(obj, obj_s->sda, obj_s->scl, false); } - return false; + return true; } int i2c_byte_read(i2c_t *obj, int last) From e82a4753a0e44a0f193a40903e3a5c0eb5ee41c3 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Wed, 5 Dec 2018 18:42:10 +0000 Subject: [PATCH 29/45] fix(hal-i2c): Add missing semicolon to STM32 I2C attribute 'tx_count' --- targets/TARGET_STM/TARGET_STM32F1/common_objects.h | 2 +- targets/TARGET_STM/TARGET_STM32F2/objects.h | 2 +- targets/TARGET_STM/TARGET_STM32F3/common_objects.h | 2 +- targets/TARGET_STM/TARGET_STM32F4/common_objects.h | 2 +- targets/TARGET_STM/TARGET_STM32F7/common_objects.h | 2 +- targets/TARGET_STM/TARGET_STM32L0/common_objects.h | 2 +- targets/TARGET_STM/TARGET_STM32L1/common_objects.h | 2 +- targets/TARGET_STM/TARGET_STM32L4/common_objects.h | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/targets/TARGET_STM/TARGET_STM32F1/common_objects.h b/targets/TARGET_STM/TARGET_STM32F1/common_objects.h index 900f39f54c4..f81d4bacacc 100644 --- a/targets/TARGET_STM/TARGET_STM32F1/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F1/common_objects.h @@ -107,7 +107,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; - uint8_t tx_complete + uint8_t tx_complete; #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32F2/objects.h b/targets/TARGET_STM/TARGET_STM32F2/objects.h index cd863e5a797..4ad2ab7b749 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F2/objects.h @@ -124,7 +124,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; - uint8_t tx_complete + uint8_t tx_complete; #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32F3/common_objects.h b/targets/TARGET_STM/TARGET_STM32F3/common_objects.h index 87a4e132478..55b49cd3a35 100644 --- a/targets/TARGET_STM/TARGET_STM32F3/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F3/common_objects.h @@ -108,7 +108,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; - uint8_t tx_complete + uint8_t tx_complete; #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32F4/common_objects.h b/targets/TARGET_STM/TARGET_STM32F4/common_objects.h index 0b172323505..c9e1c2645f0 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F4/common_objects.h @@ -107,7 +107,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; - uint8_t tx_complete + uint8_t tx_complete; #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32F7/common_objects.h b/targets/TARGET_STM/TARGET_STM32F7/common_objects.h index cf897f9ece9..d1722f3707b 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F7/common_objects.h @@ -108,7 +108,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; - uint8_t tx_complete + uint8_t tx_complete; #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32L0/common_objects.h b/targets/TARGET_STM/TARGET_STM32L0/common_objects.h index 956f9aca67a..4ef665a6026 100644 --- a/targets/TARGET_STM/TARGET_STM32L0/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L0/common_objects.h @@ -108,7 +108,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; - uint8_t tx_complete + uint8_t tx_complete; #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32L1/common_objects.h b/targets/TARGET_STM/TARGET_STM32L1/common_objects.h index 6d819788c81..5e2723e136b 100644 --- a/targets/TARGET_STM/TARGET_STM32L1/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L1/common_objects.h @@ -107,7 +107,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; - uint8_t tx_complete + uint8_t tx_complete; #endif }; diff --git a/targets/TARGET_STM/TARGET_STM32L4/common_objects.h b/targets/TARGET_STM/TARGET_STM32L4/common_objects.h index 0713021c15b..eb905bd68e3 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L4/common_objects.h @@ -108,7 +108,7 @@ struct i2c_s { uint32_t address; uint8_t stop; uint8_t available_events; - uint8_t tx_complete + uint8_t tx_complete; #endif }; From 16b41ddeed5de5d1f14be35618a48a926091fd20 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Wed, 5 Dec 2018 19:05:36 +0000 Subject: [PATCH 30/45] fix(hal-i2c): STM: Return the num of bytes sent for incomplete transfers --- targets/TARGET_STM/i2c_api.c | 92 +++++++++++++++++------------------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 64dc2b91236..74fea085a17 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -875,9 +875,6 @@ int32_t i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool } #endif - int count = I2C_ERROR_BUS_BUSY, ret = 0; - uint32_t timeout = 0; - // Trick to remove compiler warning "left and right operands are identical" in some cases uint32_t op1 = I2C_FIRST_AND_LAST_FRAME; uint32_t op2 = I2C_LAST_FRAME; @@ -903,34 +900,35 @@ int32_t i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool */ i2c_ev_err_enable(obj, i2c_get_irq_handler(obj)); - ret = HAL_I2C_Master_Sequential_Receive_IT(handle, address, (uint8_t *) data, length, obj_s->XferOperation); + const HAL_StatusTypeDef status = HAL_I2C_Master_Sequential_Receive_IT( + handle, address, (uint8_t *)data, length, obj_s->XferOperation); - if (ret == HAL_OK) { - timeout = BYTE_TIMEOUT_US * (length + 1); - /* transfer started : wait completion or timeout */ - while (!(obj_s->event & I2C_EVENT_ALL) && (--timeout != 0)) { - wait_us(1); - } + if (status != HAL_OK) { + DEBUG_PRINTF("ERROR in i2c_read:%d\r\n", status); - i2c_ev_err_disable(obj); + return I2C_ERROR_BUS_BUSY; + } + + uint32_t timeout = (BYTE_TIMEOUT_US * (length + 1)); + + /* transfer started : wait completion or timeout */ + while (!(obj_s->event & I2C_EVENT_ALL) && (--timeout != 0)) { + wait_us(1); + } + + i2c_ev_err_disable(obj); + + if ((timeout == 0) || (obj_s->event != I2C_EVENT_TRANSFER_COMPLETE)) { + DEBUG_PRINTF(" TIMEOUT or error in i2c_read\r\n"); - if ((timeout == 0) || (obj_s->event != I2C_EVENT_TRANSFER_COMPLETE)) { - DEBUG_PRINTF(" TIMEOUT or error in i2c_read\r\n"); - /* re-init IP to try and get back in a working state */ #ifdef DEVICE_I2CSLAVE - const bool is_slave = obj_s->slave ? true : false; + i2c_init(obj, obj_s->sda, obj_s->scl, obj_s->slave); #else - const bool is_slave = false; -#endif - i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); - } else { - count = length; - } - } else { - DEBUG_PRINTF("ERROR in i2c_read:%d\r\n", ret); + i2c_init(obj, obj_s->sda, obj_s->scl, false); +#endif // DEVICE_I2CSLAVE } - return count; + return (length - handle->XferCount); } int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t length, bool stop) @@ -944,9 +942,6 @@ int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t lengt } #endif - int count = I2C_ERROR_BUS_BUSY, ret = 0; - uint32_t timeout = 0; - // Trick to remove compiler warning "left and right operands are identical" in some cases uint32_t op1 = I2C_FIRST_AND_LAST_FRAME; uint32_t op2 = I2C_LAST_FRAME; @@ -969,34 +964,35 @@ int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t lengt i2c_ev_err_enable(obj, i2c_get_irq_handler(obj)); - ret = HAL_I2C_Master_Sequential_Transmit_IT(handle, address, (uint8_t *) data, length, obj_s->XferOperation); + const HAL_StatusTypeDef status = HAL_I2C_Master_Sequential_Transmit_IT( + handle, address, (uint8_t *)data, length, obj_s->XferOperation); - if (ret == HAL_OK) { - timeout = BYTE_TIMEOUT_US * (length + 1); - /* transfer started : wait completion or timeout */ - while (!(obj_s->event & I2C_EVENT_ALL) && (--timeout != 0)) { - wait_us(1); - } + if (status != HAL_OK) { + DEBUG_PRINTF("ERROR in i2c_write\r\n"); - i2c_ev_err_disable(obj); + return I2C_ERROR_BUS_BUSY; + } + + uint32_t timeout = (BYTE_TIMEOUT_US * (length + 1)); + + /* transfer started : wait completion or timeout */ + while (!(obj_s->event & I2C_EVENT_ALL) && (--timeout != 0)) { + wait_us(1); + } + + i2c_ev_err_disable(obj); + + if ((timeout == 0) || (obj_s->event != I2C_EVENT_TRANSFER_COMPLETE)) { + DEBUG_PRINTF(" TIMEOUT or error in i2c_write\r\n"); - if ((timeout == 0) || (obj_s->event != I2C_EVENT_TRANSFER_COMPLETE)) { - DEBUG_PRINTF(" TIMEOUT or error in i2c_write\r\n"); - /* re-init IP to try and get back in a working state */ #ifdef DEVICE_I2CSLAVE - const bool is_slave = obj_s->slave ? true : false; + i2c_init(obj, obj_s->sda, obj_s->scl, obj_s->slave); #else - const bool is_slave = false; -#endif - i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); - } else { - count = length; - } - } else { - DEBUG_PRINTF("ERROR in i2c_read\r\n"); + i2c_init(obj, obj_s->sda, obj_s->scl, false); +#endif // DEVICE_I2CSLAVE } - return count; + return (length - handle->XferCount); } void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) From 75909dffd7c6d2197f0d65fe8623204bebb270da Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Tue, 11 Dec 2018 18:00:32 +0000 Subject: [PATCH 31/45] refactor(hal-i2c): Remove return status from I2C start/stop functions The return status doesn't make sense in this context, and most platforms can't detect failure at this point. Platforms that do support detecting failure here can handle it during read/write anyway. --- hal/i2c_api.h | 8 +++--- .../TARGET_MCUXpresso_MCUS/api/i2c_api.c | 8 +++--- targets/TARGET_STM/i2c_api.c | 25 ++++++------------- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/hal/i2c_api.h b/hal/i2c_api.h index 7e66695a8f1..fc84de617c3 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -149,17 +149,15 @@ void i2c_timeout(i2c_t *obj, uint32_t timeout); /** Send START command * - * @param obj The I2C object - * @returns True if slave responds with ACK, false otherwise. + * @param obj The I2C object. */ -bool i2c_start(i2c_t *obj); +void i2c_start(i2c_t *obj); /** Send STOP command * * @param obj The I2C object - * @returns True if STOP command was sent succesfully, false otherwise. */ -bool i2c_stop(i2c_t *obj); +void i2c_stop(i2c_t *obj); /** Blocking sending data * diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c index 95bfd199d12..03533616519 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c @@ -106,7 +106,7 @@ void i2c_free(i2c_t *obj) } } -bool i2c_start(i2c_t *obj) +void i2c_start(i2c_t *obj) { I2C_Type *base = i2c_addrs[obj->instance]; @@ -126,15 +126,13 @@ bool i2c_start(i2c_t *obj) { } #endif /* FSL_FEATURE_I2C_HAS_DOUBLE_BUFFERING */ - - return 0; } -bool i2c_stop(i2c_t *obj) +void i2c_stop(i2c_t *obj) { I2C_Type *base = i2c_addrs[obj->instance]; - return ((I2C_MasterStop(base)) == kStatus_Success); + I2C_MasterStop(base); } uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 74fea085a17..3157c53be6b 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -574,9 +574,8 @@ i2c_t *get_i2c_obj(I2C_HandleTypeDef *hi2c) * There are 2 different IPs version that need to be supported */ #ifdef I2C_IP_VERSION_V1 -bool i2c_start(i2c_t *obj) +void i2c_start(i2c_t *obj) { - int timeout; struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); @@ -589,7 +588,7 @@ bool i2c_start(i2c_t *obj) timeout = FLAG_TIMEOUT; while ((handle->Instance->CR1 & I2C_CR1_STOP) == I2C_CR1_STOP) { if ((timeout--) == 0) { - return true; + return; } } @@ -600,14 +599,12 @@ bool i2c_start(i2c_t *obj) timeout = FLAG_TIMEOUT; while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_SB) == RESET) { if ((timeout--) == 0) { - return true; + return; } } - - return false; } -bool i2c_stop(i2c_t *obj) +void i2c_stop(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); I2C_TypeDef *i2c = (I2C_TypeDef *)obj_s->i2c; @@ -628,8 +625,6 @@ bool i2c_stop(i2c_t *obj) i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); } - - return true; } int i2c_byte_read(i2c_t *obj, int last) @@ -686,15 +681,14 @@ int i2c_byte_write(i2c_t *obj, int data) #endif //I2C_IP_VERSION_V1 #ifdef I2C_IP_VERSION_V2 -bool i2c_start(i2c_t *obj) +void i2c_start(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); /* This I2C IP doesn't */ obj_s->pending_start = 1; - return false; } -bool i2c_stop(i2c_t *obj) +void i2c_stop(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); @@ -704,7 +698,6 @@ bool i2c_stop(i2c_t *obj) const bool is_slave = obj_s->slave ? true : false; /* re-init slave when stop is requested */ i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); - return true; } #endif // Disable reload mode @@ -715,7 +708,7 @@ bool i2c_stop(i2c_t *obj) timeout = FLAG_TIMEOUT; while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXIS)) { if ((timeout--) == 0) { - return false; + return; } } } @@ -726,7 +719,7 @@ bool i2c_stop(i2c_t *obj) timeout = FLAG_TIMEOUT; while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_STOPF)) { if ((timeout--) == 0) { - return false; + return; } } @@ -748,8 +741,6 @@ bool i2c_stop(i2c_t *obj) if (obj_s->XferOperation != I2C_FIRST_AND_LAST_FRAME) { i2c_init(obj, obj_s->sda, obj_s->scl, false); } - - return true; } int i2c_byte_read(i2c_t *obj, int last) From 837cf18f9879ef4c3c63b4fc3030dbe01c875cdf Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Thu, 20 Dec 2018 16:49:13 +0000 Subject: [PATCH 32/45] fix(hal-i2c): STM: Fix frequency function checking the wrong variable The frequency function checks the frequency requested by the caller, rather than the actual supported frequency selected by the function. --- targets/TARGET_STM/i2c_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 3157c53be6b..bee4d1c99aa 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -470,7 +470,7 @@ uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) handle->Init.Timing = get_i2c_timing(frequency); // Enable the Fast Mode Plus capability - if (frequency == 1000000) { + if (selected_frequency == 1000000) { #if defined(I2C1_BASE) && defined(__HAL_SYSCFG_FASTMODEPLUS_ENABLE) && defined (I2C_FASTMODEPLUS_I2C1) if (obj_s->i2c == I2C_1) { HAL_I2CEx_EnableFastModePlus(I2C_FASTMODEPLUS_I2C1); From 9c6f7fddb3327e181612ba082819dc06b99af8b0 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Thu, 20 Dec 2018 16:50:33 +0000 Subject: [PATCH 33/45] style(hal-i2c): STM: Remove empty comment block from code --- targets/TARGET_STM/i2c_api.c | 1 - 1 file changed, 1 deletion(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index bee4d1c99aa..ba54a1f1002 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -1225,7 +1225,6 @@ void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c) /* Set event flag */ obj_s->event = I2C_EVENT_ERROR; - /* */ if (obj->handler == NULL) { return; } From 1c26c91ace87b175d262f49693a1952500bc0837 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 7 Jan 2019 14:08:07 +0000 Subject: [PATCH 34/45] fix(hal-i2c): STM: Add missing return statement when re-initing as slave - Stop function should exit early if the device is reinitialised in slave mode. - Remove slave mode lookup when reinitialising, this block is only hit if the peripheral is in slave mode, we don't need to check again. --- targets/TARGET_STM/i2c_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index ba54a1f1002..8cfcc50da70 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -695,9 +695,9 @@ void i2c_stop(i2c_t *obj) int timeout = FLAG_TIMEOUT; #if DEVICE_I2CSLAVE if (obj_s->slave) { - const bool is_slave = obj_s->slave ? true : false; /* re-init slave when stop is requested */ - i2c_init(obj, obj_s->sda, obj_s->scl, is_slave); + i2c_init(obj, obj_s->sda, obj_s->scl, true); + return; } #endif // Disable reload mode From 3887f85c6572757fc3c726f19a9db2d27b0062f2 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 7 Jan 2019 16:17:18 +0000 Subject: [PATCH 35/45] refactor(hal-i2c): Add an API function to configure clock stretching - Remove clock stretching behavior from `i2c_timeout` function and define a new function to enable/disable clock stretching behavior. - Add implementation for STM32 boards and track configuration in the platform specific I2C struct. --- hal/i2c_api.h | 14 ++++++++++++++ .../TARGET_MCUXpresso_MCUS/api/i2c_api.c | 6 ++++++ .../TARGET_STM32F0/common_objects.h | 1 + .../TARGET_STM32F1/common_objects.h | 1 + targets/TARGET_STM/TARGET_STM32F2/objects.h | 1 + .../TARGET_STM32F3/common_objects.h | 1 + .../TARGET_STM32F4/common_objects.h | 1 + .../TARGET_STM32F7/common_objects.h | 1 + .../TARGET_STM32L0/common_objects.h | 1 + .../TARGET_STM32L1/common_objects.h | 1 + .../TARGET_STM32L4/common_objects.h | 1 + targets/TARGET_STM/i2c_api.c | 19 +++++++++++++++---- 12 files changed, 44 insertions(+), 4 deletions(-) diff --git a/hal/i2c_api.h b/hal/i2c_api.h index fc84de617c3..15a9ba7e05b 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -147,6 +147,20 @@ uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency); */ void i2c_timeout(i2c_t *obj, uint32_t timeout); + +/** Enable or disable clock stretching for the I2C peripheral. + * + * The behaviour is undefined unless `obj` points to a valid 'i2c_t' object + * and the target supports configuring clock stretching, indicated by the + * 'supports_clock_stretching' attribute returned by the 'i2c_get_capabilities' + * function. + * + * @param obj The I2C object + * @param enabled If 'true' enable clock stretching on the given I2C peripheral, + * otherwise disable it. + */ +void i2c_set_clock_stretching(i2c_t *obj, const bool enabled); + /** Send START command * * @param obj The I2C object. diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c index 03533616519..1176362ea41 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c @@ -152,6 +152,12 @@ void i2c_timeout(i2c_t *obj, uint32_t timeout) (void)timeout; } +void i2c_set_clock_stretching(i2c_t *obj, const bool enabled) +{ + (void)obj; + (void)enabled; +} + static int i2c_block_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool last) { I2C_Type *base = i2c_addrs[obj->instance]; diff --git a/targets/TARGET_STM/TARGET_STM32F0/common_objects.h b/targets/TARGET_STM/TARGET_STM32F0/common_objects.h index de70da37c50..30f55d48867 100644 --- a/targets/TARGET_STM/TARGET_STM32F0/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F0/common_objects.h @@ -97,6 +97,7 @@ struct i2c_s { IRQn_Type event_i2cIRQ; IRQn_Type error_i2cIRQ; uint32_t XferOperation; + uint32_t clock_stretching_enabled; volatile uint8_t event; volatile int pending_start; #if DEVICE_I2CSLAVE diff --git a/targets/TARGET_STM/TARGET_STM32F1/common_objects.h b/targets/TARGET_STM/TARGET_STM32F1/common_objects.h index f81d4bacacc..853bea772ab 100644 --- a/targets/TARGET_STM/TARGET_STM32F1/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F1/common_objects.h @@ -97,6 +97,7 @@ struct i2c_s { IRQn_Type event_i2cIRQ; IRQn_Type error_i2cIRQ; uint32_t XferOperation; + uint32_t clock_stretching_enabled; volatile uint8_t event; #if DEVICE_I2CSLAVE uint8_t slave; diff --git a/targets/TARGET_STM/TARGET_STM32F2/objects.h b/targets/TARGET_STM/TARGET_STM32F2/objects.h index 4ad2ab7b749..64771d636db 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F2/objects.h @@ -114,6 +114,7 @@ struct i2c_s { IRQn_Type event_i2cIRQ; IRQn_Type error_i2cIRQ; uint8_t XferOperation; + uint32_t clock_stretching_enabled; volatile uint8_t event; #if DEVICE_I2CSLAVE uint8_t slave; diff --git a/targets/TARGET_STM/TARGET_STM32F3/common_objects.h b/targets/TARGET_STM/TARGET_STM32F3/common_objects.h index 55b49cd3a35..f941cc50462 100644 --- a/targets/TARGET_STM/TARGET_STM32F3/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F3/common_objects.h @@ -97,6 +97,7 @@ struct i2c_s { IRQn_Type event_i2cIRQ; IRQn_Type error_i2cIRQ; uint32_t XferOperation; + uint32_t clock_stretching_enabled; volatile uint8_t event; volatile int pending_start; #if DEVICE_I2CSLAVE diff --git a/targets/TARGET_STM/TARGET_STM32F4/common_objects.h b/targets/TARGET_STM/TARGET_STM32F4/common_objects.h index c9e1c2645f0..e65314311ac 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F4/common_objects.h @@ -97,6 +97,7 @@ struct i2c_s { IRQn_Type event_i2cIRQ; IRQn_Type error_i2cIRQ; uint8_t XferOperation; + uint32_t clock_stretching_enabled; volatile uint8_t event; #if DEVICE_I2CSLAVE uint8_t slave; diff --git a/targets/TARGET_STM/TARGET_STM32F7/common_objects.h b/targets/TARGET_STM/TARGET_STM32F7/common_objects.h index d1722f3707b..d1fbcc2fff7 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F7/common_objects.h @@ -97,6 +97,7 @@ struct i2c_s { IRQn_Type event_i2cIRQ; IRQn_Type error_i2cIRQ; uint32_t XferOperation; + uint32_t clock_stretching_enabled; volatile uint8_t event; volatile int pending_start; #if DEVICE_I2CSLAVE diff --git a/targets/TARGET_STM/TARGET_STM32L0/common_objects.h b/targets/TARGET_STM/TARGET_STM32L0/common_objects.h index 4ef665a6026..6f84f98ab0b 100644 --- a/targets/TARGET_STM/TARGET_STM32L0/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L0/common_objects.h @@ -97,6 +97,7 @@ struct i2c_s { IRQn_Type event_i2cIRQ; IRQn_Type error_i2cIRQ; uint32_t XferOperation; + uint32_t clock_stretching_enabled; volatile uint8_t event; volatile int pending_start; #if DEVICE_I2CSLAVE diff --git a/targets/TARGET_STM/TARGET_STM32L1/common_objects.h b/targets/TARGET_STM/TARGET_STM32L1/common_objects.h index 5e2723e136b..6256cab5139 100644 --- a/targets/TARGET_STM/TARGET_STM32L1/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L1/common_objects.h @@ -97,6 +97,7 @@ struct i2c_s { IRQn_Type event_i2cIRQ; IRQn_Type error_i2cIRQ; uint32_t XferOperation; + uint32_t clock_stretching_enabled; volatile uint8_t event; #if DEVICE_I2CSLAVE uint8_t slave; diff --git a/targets/TARGET_STM/TARGET_STM32L4/common_objects.h b/targets/TARGET_STM/TARGET_STM32L4/common_objects.h index eb905bd68e3..6309c535c22 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L4/common_objects.h @@ -97,6 +97,7 @@ struct i2c_s { IRQn_Type event_i2cIRQ; IRQn_Type error_i2cIRQ; uint32_t XferOperation; + uint32_t clock_stretching_enabled; volatile uint8_t event; volatile int pending_start; #if DEVICE_I2CSLAVE diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 8cfcc50da70..80bd59d15a9 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -427,6 +427,7 @@ void i2c_init_internal(i2c_t *obj, PinName sda, PinName scl) // I2C Xfer operation init obj_s->event = 0; obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; + obj_s->clock_stretching_enabled = I2C_NOSTRETCH_DISABLE; #ifdef I2C_IP_VERSION_V2 obj_s->pending_start = 0; #endif @@ -525,7 +526,7 @@ uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) handle->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; handle->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; handle->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; - handle->Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; + handle->Init.NoStretchMode = obj_s->clock_stretching_enabled; handle->Init.OwnAddress1 = 0; handle->Init.OwnAddress2 = 0; HAL_I2C_Init(handle); @@ -537,23 +538,33 @@ uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) } void i2c_timeout(i2c_t *obj, uint32_t timeout) +{ + (void) obj; + (void) timeout; +} + +void i2c_set_clock_stretching(i2c_t *obj, const bool enabled) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); + obj_s->clock_stretching_enabled = + enabled ? I2C_NOSTRETCH_DISABLE : I2C_NOSTRETCH_ENABLE; + // wait before init int wait = BYTE_TIMEOUT; - while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY)) && (--wait != 0)) {} + while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY)) && (--wait != 0)); // I2C configuration handle->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; handle->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; handle->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; - handle->Init.NoStretchMode = (timeout == 0) ? I2C_NOSTRETCH_DISABLE - : I2C_NOSTRETCH_ENABLE; + handle->Init.NoStretchMode = obj_s->clock_stretching_enabled; + HAL_I2C_Init(handle); } + i2c_t *get_i2c_obj(I2C_HandleTypeDef *hi2c) { /* Aim of the function is to get i2c_s pointer using hi2c pointer */ From 216b1f355be11fe7fdcc603c9613021cddb4899e Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 7 Jan 2019 17:15:12 +0000 Subject: [PATCH 36/45] refactor(hal-i2c): Remove i2c_timeout function from the API --- hal/i2c_api.h | 9 --------- .../TARGET_MCUXpresso_MCUS/api/i2c_api.c | 6 ------ targets/TARGET_STM/i2c_api.c | 6 ------ 3 files changed, 21 deletions(-) diff --git a/hal/i2c_api.h b/hal/i2c_api.h index 15a9ba7e05b..fcc4327dcae 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -139,15 +139,6 @@ void i2c_free(i2c_t *obj); */ uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency); -/** Configure the timeout duration in milliseconds the I2C peripheral should - * allow the slave peripheral to stretch the clock for before timing out. - * - * @param obj The I2C object - * @param timeout Clock stretching timeout in milliseconds. - */ -void i2c_timeout(i2c_t *obj, uint32_t timeout); - - /** Enable or disable clock stretching for the I2C peripheral. * * The behaviour is undefined unless `obj` points to a valid 'i2c_t' object diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c index 1176362ea41..b5faf4fcb17 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c @@ -146,12 +146,6 @@ uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) return frequency; } -void i2c_timeout(i2c_t *obj, uint32_t timeout) -{ - (void)obj; - (void)timeout; -} - void i2c_set_clock_stretching(i2c_t *obj, const bool enabled) { (void)obj; diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 80bd59d15a9..90b9957ac4f 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -537,12 +537,6 @@ uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) return obj_s->hz; } -void i2c_timeout(i2c_t *obj, uint32_t timeout) -{ - (void) obj; - (void) timeout; -} - void i2c_set_clock_stretching(i2c_t *obj, const bool enabled) { struct i2c_s *obj_s = I2C_S(obj); From 77e0cba1da0d60c8bfe94a8fe593126d5e8552aa Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 28 Jan 2019 14:41:23 +0000 Subject: [PATCH 37/45] style(hal-i2c): Fix astyle code formatting failures --- drivers/I2C.cpp | 2 +- hal/i2c_api.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/I2C.cpp b/drivers/I2C.cpp index e58ca1ed16c..17a059f434a 100644 --- a/drivers/I2C.cpp +++ b/drivers/I2C.cpp @@ -79,7 +79,7 @@ int I2C::write(int address, const char *data, int length, bool repeated) aquire(); int stop = (repeated) ? 0 : 1; - int written = i2c_write(&_i2c, address, (void*)data, length, stop); + int written = i2c_write(&_i2c, address, (void *)data, length, stop); unlock(); return length != written; diff --git a/hal/i2c_api.h b/hal/i2c_api.h index fcc4327dcae..eff5135dabb 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -55,7 +55,7 @@ typedef struct i2c_async_event { bool error; } i2c_async_event_t; -typedef void (*i2c_async_handler_f)(i2c_t* obj, i2c_async_event_t* event, void *ctx); +typedef void (*i2c_async_handler_f)(i2c_t *obj, i2c_async_event_t *event, void *ctx); /** Asynch I2C HAL structure */ @@ -64,7 +64,7 @@ struct i2c { struct buffer_s tx_buff; /**< Tx buffer */ struct buffer_s rx_buff; /**< Rx buffer */ i2c_async_handler_f handler; - void* ctx; + void *ctx; }; #else @@ -88,7 +88,7 @@ typedef struct { uint32_t minimum_frequency; /**< Maximum frequency supported must be set by target device */ uint32_t maximum_frequency; - /**< If true, the device can handle I2C slave mode. */ + /**< If true, the device can handle I2C slave mode. */ bool supports_slave_mode; /**< If true, supports 10-bit addressing. */ bool supports_10bit_addressing; From 4bda684ef0b30796b5b782cf0a1e46e9a9771b91 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Wed, 30 Jan 2019 14:35:15 +0000 Subject: [PATCH 38/45] fix(hal-i2c): K64F: Ignore calls to set slave address in master mode --- .../TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c index b5faf4fcb17..9f9c638cb26 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c @@ -225,7 +225,11 @@ static int i2c_slave_write(i2c_t *obj, const void *data, uint32_t length) void i2c_slave_address(i2c_t *obj, uint16_t address) { - i2c_addrs[obj->instance]->A1 = address & 0xfe; + if (!obj->is_slave) { + return; + } + + i2c_addrs[obj->instance]->A1 = (address & 0xFEU); } #endif // DEVICE_I2CSLAVE From c2390bbae6b59c81e45b9f0560a249d13e479b15 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 25 Feb 2019 15:10:03 +0000 Subject: [PATCH 39/45] fix(hal-i2c): Disable `I2CSLAVE` feature flag on K64F devices --- targets/targets.json | 1 - 1 file changed, 1 deletion(-) diff --git a/targets/targets.json b/targets/targets.json index 2fb7777615a..98e48119fdc 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -1409,7 +1409,6 @@ "ANALOGOUT", "EMAC", "I2C", - "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", From 19ae834750699b4c0abc56c35134db4feffc693b Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 4 Mar 2019 14:16:17 +0000 Subject: [PATCH 40/45] fix(hal-i2c): Return the num bytes transferred in STM slave read/write --- targets/TARGET_STM/i2c_api.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 90b9957ac4f..7415ec59639 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -286,7 +286,8 @@ static int i2c_slave_read(i2c_t *obj, char *data, int length) DEBUG_PRINTF("TIMEOUT or error in i2c_slave_read\r\n"); } } - return count; + + return (length - handle->XferCount); } static int i2c_slave_write(i2c_t *obj, const char *data, int length) @@ -313,7 +314,7 @@ static int i2c_slave_write(i2c_t *obj, const char *data, int length) } } - return count; + return (length - handle->XferCount); } #endif From 51fc9693e76799c72f5a2767848d9db1b2d90a01 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 4 Mar 2019 15:53:49 +0000 Subject: [PATCH 41/45] fix(hal-i2c): Disable slave mode and 10-bit from I2C capabilities on K64 --- targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c index 9f9c638cb26..2839dcbc6b7 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/i2c_api.c @@ -38,8 +38,8 @@ void i2c_get_capabilities(i2c_capabilities_t *capabilities) capabilities->minimum_frequency = 1; capabilities->maximum_frequency = 1000000; - capabilities->supports_slave_mode = true; - capabilities->supports_10bit_addressing = true; + capabilities->supports_slave_mode = false; + capabilities->supports_10bit_addressing = false; capabilities->supports_multi_master = true; capabilities->supports_clock_stretching = false; } From c57fb11910d48ada149f40c7ae3954aa850a01fb Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Tue, 12 Mar 2019 15:08:39 +0000 Subject: [PATCH 42/45] fix(hal-i2c): Disable 10-bit addressing I2C capabilities on STM32 --- targets/TARGET_STM/i2c_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 7415ec59639..2298c51801e 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -328,7 +328,7 @@ void i2c_get_capabilities(i2c_capabilities_t *capabilities) capabilities->minimum_frequency = 100000; capabilities->maximum_frequency = 1000000; capabilities->supports_slave_mode = true; - capabilities->supports_10bit_addressing = true; + capabilities->supports_10bit_addressing = false; capabilities->supports_multi_master = true; } From d150905d32046bc53d4dd749f4d5bbd596e67330 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Thu, 14 Mar 2019 11:38:10 +0000 Subject: [PATCH 43/45] fix(hal-i2c): Fix compiler warning from unused variable on STM32 --- targets/TARGET_STM/i2c_api.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 2298c51801e..c9b79e72271 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -267,7 +267,6 @@ static int i2c_slave_read(i2c_t *obj, char *data, int length) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); - int count = 0; int ret = 0; uint32_t timeout = 0; @@ -280,9 +279,7 @@ static int i2c_slave_read(i2c_t *obj, char *data, int length) wait_us(1); } - if (timeout != 0) { - count = length; - } else { + if (timeout == 0) { DEBUG_PRINTF("TIMEOUT or error in i2c_slave_read\r\n"); } } @@ -294,7 +291,6 @@ static int i2c_slave_write(i2c_t *obj, const char *data, int length) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); - int count = 0; int ret = 0; uint32_t timeout = 0; @@ -307,9 +303,7 @@ static int i2c_slave_write(i2c_t *obj, const char *data, int length) wait_us(1); } - if (timeout != 0) { - count = length; - } else { + if (timeout == 0) { DEBUG_PRINTF("TIMEOUT or error in i2c_slave_write\r\n"); } } From c1ba83f4a4707de24860fcc0357afb28e48ef6f7 Mon Sep 17 00:00:00 2001 From: Steve Cartmell Date: Mon, 25 Mar 2019 16:57:12 +0000 Subject: [PATCH 44/45] fix(hal-i2c): Fix return value for transfers that timeout in STM --- hal/i2c_api.h | 3 ++- targets/TARGET_STM/i2c_api.c | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hal/i2c_api.h b/hal/i2c_api.h index eff5135dabb..6ccf06a210d 100644 --- a/hal/i2c_api.h +++ b/hal/i2c_api.h @@ -76,7 +76,8 @@ typedef struct i2c_s i2c_t; enum { I2C_ERROR_NO_SLAVE = -1, - I2C_ERROR_BUS_BUSY = -2 + I2C_ERROR_BUS_BUSY = -2, + I2C_ERROR_TIMEOUT = -3 }; #ifdef __cplusplus diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index c9b79e72271..30cd2340ac7 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -917,6 +917,9 @@ int32_t i2c_read(i2c_t *obj, uint16_t address, void *data, uint32_t length, bool #else i2c_init(obj, obj_s->sda, obj_s->scl, false); #endif // DEVICE_I2CSLAVE + + + return I2C_ERROR_TIMEOUT; } return (length - handle->XferCount); @@ -981,6 +984,8 @@ int32_t i2c_write(i2c_t *obj, uint16_t address, const void *data, uint32_t lengt #else i2c_init(obj, obj_s->sda, obj_s->scl, false); #endif // DEVICE_I2CSLAVE + + return I2C_ERROR_TIMEOUT; } return (length - handle->XferCount); From 7d9de669e33e1dc6f14971998af46b5661243e38 Mon Sep 17 00:00:00 2001 From: Maciej Bocianski Date: Wed, 27 Mar 2019 11:18:03 +0100 Subject: [PATCH 45/45] rebase fixes --- targets/TARGET_STM/i2c_api.c | 42 +++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 30cd2340ac7..25c659e2c2b 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -40,6 +40,10 @@ #include "PeripheralPins.h" #include "i2c_device.h" // family specific defines +#ifdef I2C_IP_VERSION_V2 +#include +#endif // I2C_IP_VERSION_V2 + #ifndef DEBUG_STDIO # define DEBUG_STDIO 0 #endif @@ -79,8 +83,8 @@ static I2C_HandleTypeDef *i2c_handles[I2C_NUM]; */ #define FLAG_TIMEOUT ((int)0x1000) -/* Declare i2c_init_internal to be used in this file */ -void i2c_init_internal(i2c_t *obj, PinName sda, PinName scl); +#define MINIMUM_FREQUENCY 100000 +#define MAXIMUM_FREQUENCY 1000000 /* GENERIC INIT and HELPERS FUNCTIONS */ @@ -319,8 +323,8 @@ void i2c_get_capabilities(i2c_capabilities_t *capabilities) return; } - capabilities->minimum_frequency = 100000; - capabilities->maximum_frequency = 1000000; + capabilities->minimum_frequency = MINIMUM_FREQUENCY; + capabilities->maximum_frequency = MAXIMUM_FREQUENCY; capabilities->supports_slave_mode = true; capabilities->supports_10bit_addressing = false; capabilities->supports_multi_master = true; @@ -328,12 +332,7 @@ void i2c_get_capabilities(i2c_capabilities_t *capabilities) void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave) { - memset(obj, 0, sizeof(*obj)); - i2c_init_internal(obj, sda, scl); -} -void i2c_init_internal(i2c_t *obj, PinName sda, PinName scl) -{ struct i2c_s *obj_s = I2C_S(obj); // Determine the I2C to use @@ -448,6 +447,7 @@ void i2c_free(i2c_t *obj) uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) { int timeout; + uint32_t selected_frequency = frequency; struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); @@ -456,14 +456,26 @@ uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY)) && (--timeout != 0)); #ifdef I2C_IP_VERSION_V1 - handle->Init.ClockSpeed = frequency; + handle->Init.ClockSpeed = selected_frequency; handle->Init.DutyCycle = I2C_DUTYCYCLE_2; #endif #ifdef I2C_IP_VERSION_V2 - /* Only predefined timing for below frequencies are supported */ - MBED_ASSERT((frequency == 100000) || (frequency == 400000) || - (frequency == 1000000)); - handle->Init.Timing = get_i2c_timing(frequency); + // Find the closest supported frequency + static const int supported_hz[] = {100000, 400000, 1000000}; + uint32_t minimum_delta = -1; + + for (size_t i = 0; i < (sizeof(supported_hz) / sizeof(int)); ++i) + { + const uint32_t current_delta = abs(supported_hz[i] - frequency); + + if (current_delta < minimum_delta) + continue; + + selected_frequency = supported_hz[i]; + minimum_delta = current_delta; + } + + handle->Init.Timing = get_i2c_timing(selected_frequency); // Enable the Fast Mode Plus capability if (selected_frequency == 1000000) { @@ -527,7 +539,7 @@ uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency) HAL_I2C_Init(handle); /* store frequency for timeout computation */ - obj_s->hz = frequency; + obj_s->hz = selected_frequency; return obj_s->hz; }