Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2C driver docs #4298

Merged
merged 6 commits into from
Nov 10, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
* [Hand Wiring Guide](hand_wire.md)
* [ISP Flashing Guide](isp_flashing_guide.md)
* [ARM Debugging Guide](arm_debugging.md)
* [I2C Driver](i2c_driver.md)

* For a Deeper Understanding
* [How Keyboards Work](how_keyboards_work.md)
Expand Down
1 change: 1 addition & 0 deletions docs/_summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
* [Hand Wiring Guide](hand_wire.md)
* [ISP Flashing Guide](isp_flashing_guide.md)
* [ARM Debugging Guide](arm_debugging.md)
* [I2C Driver](i2c_driver.md)

* For a Deeper Understanding
* [How Keyboards Work](how_keyboards_work.md)
Expand Down
82 changes: 82 additions & 0 deletions docs/i2c_driver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# I2C Master Driver

The I2C Master drivers used in QMK have a set of common functions to allow portability between MCUs.

## Available functions

|Function |Description |
|------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|`void i2c_init(void);` |Initializes the I2C driver. This function should be called once before any transaction is initiated. |
|`uint8_t i2c_start(uint8_t address);` |Starts an I2C transaction. Address is the 7-bit slave address without the direction bit. |
|`uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);` |Transmit data over I2C. Address is the 7-bit slave address without the direction. |
|`uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);` |Transmit data over I2C. Address is the 7-bit slave address without the direction. Returns status of transaction. |
|`uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);` |Receive data over I2C. Address is the 7-bit slave address without the direction. Saves number of bytes specified by `length` in `data` array. Returns status of transaction. |
|`uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);` |Same as the `i2c_transmit` function but `regaddr` sets where in the slave the data will be written. |
|`uint8_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);` |Same as the `i2c_receive` function but `regaddr` sets from where in the slave the data will be read. |
|`uint8_t i2c_stop(uint16_t timeout);` |Stops the I2C driver. |

### Function Return

All the above functions, except `void i2c_init(void);` can return the following truth table:
yiancar marked this conversation as resolved.
Show resolved Hide resolved

|Return Value |Description |
|---------------|---------------------------------------------------|
|0 |Operation executed successfully. |
|-1 |Operation failed. |
|-2 |Operation timed out. |


## AVR

### Configuration

The following defines can be used to configure the I2C master driver.

|Variable |Description |Default|
|------------------|---------------------------------------------------|-------|
|`#F_SCL` |Clock frequency in Hz |400KHz |
|`#Prescaler` |Divides master clock to aid in I2C clock selection |1 |

AVRs usually have set GPIO which turn into I2C pins, therefore no further configuration is required.

## ARM

For ARM the Chibios I2C HAL driver is under the hood.
This section assumes an STM32 MCU.

### Configuration

The configuration for ARM MCUs can be quite complex as often there are multiple I2C drivers which can be assigned to a variety of ports.

Firstly the `mcuconf.h` file must be setup to enable the necessary hardware drivers.

|Variable |Description |Default|
|------------------------------|------------------------------------------------------------------------------------|-------|
|`#STM32_I2C_USE_XXX` |Enable/Disable the hardware driver XXX (each driver should be explicitly listed) |FALSE |
|`#STM32_I2C_BUSY_TIMEOUT` |Time in ms until the I2C command is aborted if no response is received |50 |
|`#STM32_I2C_XXX_IRQ_PRIORITY` |Interrupt priority for hardware driver XXX (THIS IS AN EXPERT SETTING) |10 |
|`#STM32_I2C_USE_DMA` |Enable/Disable the ability of the MCU to offload the data transfer to the DMA unit |TRUE |
|`#STM32_I2C_XXX_DMA_PRIORITY` |Priority of DMA unit for hardware driver XXX (THIS IS AN EXPERT SETTING) |1 |

Secondly in the `halconf.h` file `#define HAL_USE_I2C` must be set to `TRUE`. This allows Chibios to load its I2C driver.
yiancar marked this conversation as resolved.
Show resolved Hide resolved

Lastly we need to assign the correct GPIO pins depending on the I2C hardware driver we want to use.
yiancar marked this conversation as resolved.
Show resolved Hide resolved

By default the I2C1 hardware driver is assumed to be used. If another hardware driver is used, in the `config.h` file `#define I2C_DRIVER I2CDX` should be added with X being the number of hardware driver used. For example is I2C3 is enabled, the `config.h` file should contain `#define I2C_DRIVER I2CD3`. This aligns the QMK I2C driver with the Chibios I2C driver.
yiancar marked this conversation as resolved.
Show resolved Hide resolved

STM32 MCUs allows a variety of pins to be configured as I2C pins depending on the hardware driver used. By default B6 and B7 are set to I2C.

This can be changed by declaring the `i2c_init` function which intentionally has a weak attribute. Please consult the datasheet of your MCU for the available GPIO configurations. The following is an example initialization function:

```C
void i2c_init(void)
{
palSetGroupMode(GPIOB, GPIOB_PIN6 | GPIOB_PIN7, 0, PAL_MODE_INPUT); // Try releasing special pins for a short time
chThdSleepMilliseconds(10); // Wait for the release to happen

palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B6 to I2C function
palSetPadMode(GPIOB, 7, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B7 to I2C function
}
```


1 change: 1 addition & 0 deletions drivers/arm/i2c_master.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ static const I2CConfig i2cconfig = {
0
};

__attribute__ ((weak))
void i2c_init(void)
{
palSetGroupMode(GPIOB, GPIOB_PIN6 | GPIOB_PIN7, 0, PAL_MODE_INPUT); // Try releasing special pins for a short time
Expand Down