aboutsummaryrefslogtreecommitdiff
path: root/docs/i2c_driver.md
diff options
context:
space:
mode:
authorRyan <fauxpark@gmail.com>2021-02-08 07:45:59 +1100
committerGitHub <noreply@github.com>2021-02-08 07:45:59 +1100
commit5d5cbb877da3a7f27f1c96948d240317e6263388 (patch)
tree599f61edd4da6ff64a5515bc20ffc49a2c65747d /docs/i2c_driver.md
parent0355cd0f72899b554c180b7fe6ca61e140e2f908 (diff)
downloadqmk_firmware-5d5cbb877da3a7f27f1c96948d240317e6263388.tar.gz
qmk_firmware-5d5cbb877da3a7f27f1c96948d240317e6263388.zip
Rework I2C driver docs (#11658)
Diffstat (limited to 'docs/i2c_driver.md')
-rw-r--r--docs/i2c_driver.md286
1 files changed, 195 insertions, 91 deletions
diff --git a/docs/i2c_driver.md b/docs/i2c_driver.md
index 0103e1b4c..3ec34a0f8 100644
--- a/docs/i2c_driver.md
+++ b/docs/i2c_driver.md
@@ -2,132 +2,236 @@
2 2
3The I2C Master drivers used in QMK have a set of common functions to allow portability between MCUs. 3The I2C Master drivers used in QMK have a set of common functions to allow portability between MCUs.
4 4
5## An important note on I2C Addresses :id=note-on-i2c-addresses 5## I2C Addressing :id=note-on-i2c-addresses
6 6
7All of the addresses expected by this driver should be pushed to the upper 7 bits of the address byte. Setting 7All of the addresses expected by this driver should be pushed to the upper 7 bits of the address byte. Setting
8the lower bit (indicating read/write) will be done by the respective functions. Almost all I2C addresses listed 8the lower bit (indicating read/write) will be done by the respective functions. Almost all I2C addresses listed
9on datasheets and the internet will be represented as 7 bits occupying the lower 7 bits and will need to be 9on datasheets and the internet will be represented as 7 bits occupying the lower 7 bits and will need to be
10shifted to the left (more significant) by one bit. This is easy to do via the bitwise shift operator `<< 1`. 10shifted to the left (more significant) by one bit. This is easy to do via the bitwise shift operator `<< 1`.
11 11
12You can either do this on each call to the functions below, or once in your definition of the address. For example if your device has an address of `0x18`: 12You can either do this on each call to the functions below, or once in your definition of the address. For example, if your device has an address of `0x18`:
13 13
14`#define MY_I2C_ADDRESS (0x18 << 1)` 14```c
15#define MY_I2C_ADDRESS (0x18 << 1)
16```
15 17
16See https://www.robot-electronics.co.uk/i2c-tutorial for more information about I2C addressing and other technical details. 18See https://www.robot-electronics.co.uk/i2c-tutorial for more information about I2C addressing and other technical details.
17 19
18## Available functions :id=available-functions 20## AVR Configuration :id=avr-configuration
19
20|Function |Description |
21|------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
22|`void i2c_init(void);` |Initializes the I2C driver. This function should be called once before any transaction is initiated. |
23|`i2c_status_t i2c_start(uint8_t address, uint16_t timeout);` |Starts an I2C transaction. Address is the 7-bit slave address without the direction bit. |
24|`i2c_status_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. |
25|`i2c_status_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. |
26|`i2c_status_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. |
27|`i2c_status_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. |
28|`i2c_status_t i2c_stop(void);` |Ends an I2C transaction. |
29 21
30### Function Return :id=function-return 22The following defines can be used to configure the I2C master driver:
31 23
32All the above functions, except `void i2c_init(void);` return the following truth table: 24|`config.h` Override|Description |Default |
25|-------------------|---------------------|--------|
26|`F_SCL` |Clock frequency in Hz|`400000`|
33 27
34|Return Constant |Value|Description | 28No further setup is required - just connect the `SDA` and `SCL` pins of your I2C devices to the matching pins on the MCU:
35|--------------------|-----|--------------------------------|
36|`I2C_STATUS_SUCCESS`|0 |Operation executed successfully.|
37|`I2C_STATUS_ERROR` |-1 |Operation failed. |
38|`I2C_STATUS_TIMEOUT`|-2 |Operation timed out. |
39 29
30|MCU |`SCL`|`SDA`|
31|------------------|-----|-----|
32|ATmega16/32U4 |`D0` |`D1` |
33|AT90USB64/128 |`D0` |`D1` |
34|ATmega32A |`C0` |`C1` |
35|ATmega328/P |`C5` |`C4` |
40 36
41## AVR :id=avr 37?> The ATmega16/32U2 does not possess I2C functionality, and so cannot use this driver.
42 38
43### Configuration :id=avr-configuration 39## ChibiOS/ARM Configuration :id=arm-configuration
44 40
45The following defines can be used to configure the I2C master driver. 41You'll need to determine which pins can be used for I2C -- a an example, STM32 parts generally have multiple I2C peripherals, labeled I2C1, I2C2, I2C3 etc.
46 42
47|Variable |Description |Default| 43To enable I2C, modify your board's `halconf.h` to enable I2C:
48|------------------|---------------------------------------------------|-------|
49|`F_SCL` |Clock frequency in Hz |400KHz |
50 44
51AVRs usually have set GPIO which turn into I2C pins, therefore no further configuration is required. 45```c
52 46#define HAL_USE_I2C TRUE
53## ARM :id=arm 47```
54
55For ARM the Chibios I2C HAL driver is under the hood.
56This section assumes an STM32 MCU.
57 48
58### Configuration :id=arm-configuration 49Then, modify your board's `mcuconf.h` to enable the peripheral you've chosen, for example:
59 50
60The configuration for ARM MCUs can be quite complex as often there are multiple I2C drivers which can be assigned to a variety of ports. 51```c
52#undef STM32_I2C_USE_I2C2
53#define STM32_I2C_USE_I2C2 TRUE
54```
61 55
62Firstly the `mcuconf.h` file must be setup to enable the necessary hardware drivers. 56|`mcuconf.h` Setting |Description |Default|
57|----------------------------|----------------------------------------------------------------------------------|-------|
58|`STM32_I2C_BUSY_TIMEOUT` |Time in milliseconds until the I2C command is aborted if no response is received |`50` |
59|`STM32_I2C_XXX_IRQ_PRIORITY`|Interrupt priority for hardware driver XXX (THIS IS AN EXPERT SETTING) |`10` |
60|`STM32_I2C_USE_DMA` |Enable/Disable the ability of the MCU to offload the data transfer to the DMA unit|`TRUE` |
61|`STM32_I2C_XXX_DMA_PRIORITY`|Priority of DMA unit for hardware driver XXX (THIS IS AN EXPERT SETTING) |`1` |
63 62
64|Variable |Description |Default| 63Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303.
65|------------------------------|------------------------------------------------------------------------------------|-------|
66|`#STM32_I2C_USE_XXX` |Enable/Disable the hardware driver XXX (each driver should be explicitly listed) |FALSE |
67|`#STM32_I2C_BUSY_TIMEOUT` |Time in ms until the I2C command is aborted if no response is received |50 |
68|`#STM32_I2C_XXX_IRQ_PRIORITY` |Interrupt priority for hardware driver XXX (THIS IS AN EXPERT SETTING) |10 |
69|`#STM32_I2C_USE_DMA` |Enable/Disable the ability of the MCU to offload the data transfer to the DMA unit |TRUE |
70|`#STM32_I2C_XXX_DMA_PRIORITY` |Priority of DMA unit for hardware driver XXX (THIS IS AN EXPERT SETTING) |1 |
71 64
72Secondly, in the `halconf.h` file, `#define HAL_USE_I2C` must be set to `TRUE`. This allows ChibiOS to load its I2C driver. 65|`config.h` Overrride |Description |Default|
66|------------------------|-------------------------------------------------------------------------------------------|-------|
67|`I2C_DRIVER` |I2C peripheral to use - I2C1 -> `I2CD1`, I2C2 -> `I2CD2` etc. |`I2CD1`|
68|`I2C1_BANK` (deprecated)|The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`), superseded by `I2C1_SCL_BANK`/`I2C1_SDA_BANK`|`GPIOB`|
69|`I2C1_SCL_BANK` |The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`) to use for SCL |`GPIOB`|
70|`I2C1_SCL` |The pin number for SCL (0-15) |`6` |
71|`I2C1_SCL_PAL_MODE` |The alternate function mode for SCL |`4` |
72|`I2C1_SDA_BANK` |The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`) to use for SDA |`GPIOB`|
73|`I2C1_SDA` |The pin number for SDA (0-15) |`7` |
74|`I2C1_SDA_PAL_MODE` |The alternate function mode for SDA |`4` |
73 75
74Lastly, we need to assign the correct GPIO pins depending on the I2C hardware driver we want to use. 76The following configuration values depend on the specific MCU in use.
75 77
76By default the I2C1 hardware driver is assumed to be used. If another hardware driver is used, `#define I2C_DRIVER I2CDX` should be added to the `config.h` file 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. 78### I2Cv1 :id=i2cv1
77 79
78STM32 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. You can use these defines to set your i2c pins: 80* STM32F1xx
81* STM32F2xx
82* STM32F4xx
83* STM32L0xx
84* STM32L1xx
79 85
80| Variable | Description | Default | 86See [this page](https://www.playembedded.org/blog/stm32-i2c-chibios/#7_I2Cv1_configuration_structure) for the I2Cv1 configuration structure.
81|--------------------------|----------------------------------------------------------------------------------------------|---------|
82| `I2C1_SCL_BANK` | The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`) to use for SCL | `GPIOB` |
83| `I2C1_SDA_BANK` | The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`) to use for SDA | `GPIOB` |
84| `I2C1_SCL` | The pin number for the SCL pin (0-15) | `6` |
85| `I2C1_SDA` | The pin number for the SDA pin (0-15) | `7` |
86| `I2C1_BANK` (deprecated) | The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`), superceded by `I2C1_SCL_BANK`, `I2C1_SDA_BANK` | `GPIOB` |
87 87
88The ChibiOS I2C driver configuration depends on STM32 MCU: 88|`config.h` Override|Default |
89|-------------------|----------------|
90|`I2C1_OPMODE` |`OPMODE_I2C` |
91|`I2C1_CLOCK_SPEED` |`100000` |
92|`I2C1_DUTY_CYCLE` |`STD_DUTY_CYCLE`|
89 93
90 STM32F1xx, STM32F2xx, STM32F4xx, STM32L0xx and STM32L1xx use I2Cv1; 94### I2Cv2 :id=i2cv2
91 STM32F0xx, STM32F3xx, STM32F7xx and STM32L4xx use I2Cv2;
92 95
93#### I2Cv1 :id=i2cv1 96* STM32F0xx
94STM32 MCUs allow for different clock and duty parameters when configuring I2Cv1. These can be modified using the following parameters, using <https://www.playembedded.org/blog/stm32-i2c-chibios/#I2Cv1_configuration_structure> as a reference: 97* STM32F3xx
98* STM32F7xx
99* STM32L4xx
95 100
96| Variable | Default | 101See [this page](https://www.playembedded.org/blog/stm32-i2c-chibios/#8_I2Cv2_I2Cv3_configuration_structure) for the I2Cv2 configuration structure.
97|--------------------|------------------|
98| `I2C1_OPMODE` | `OPMODE_I2C` |
99| `I2C1_CLOCK_SPEED` | `100000` |
100| `I2C1_DUTY_CYCLE` | `STD_DUTY_CYCLE` |
101 102
102#### I2Cv2 :id=i2cv2 103|`config.h` Override |Default|
103STM32 MCUs allow for different timing parameters when configuring I2Cv2. These can be modified using the following parameters, using <https://www.st.com/en/embedded-software/stsw-stm32126.html> as a reference: 104|---------------------|-------|
105|`I2C1_TIMINGR_PRESC` |`0U` |
106|`I2C1_TIMINGR_SCLDEL`|`7U` |
107|`I2C1_TIMINGR_SDADEL`|`0U` |
108|`I2C1_TIMINGR_SCLH` |`38U` |
109|`I2C1_TIMINGR_SCLL` |`129U` |
104 110
105| Variable | Default | 111## Functions :id=functions
106|-----------------------|---------|
107| `I2C1_TIMINGR_PRESC` | `15U` |
108| `I2C1_TIMINGR_SCLDEL` | `4U` |
109| `I2C1_TIMINGR_SDADEL` | `2U` |
110| `I2C1_TIMINGR_SCLH` | `15U` |
111| `I2C1_TIMINGR_SCLL` | `21U` |
112 112
113STM32 MCUs allow for different "alternate function" modes when configuring GPIO pins. These are required to switch the pins used to I2Cv2 mode. See the respective datasheet for the appropriate values for your MCU. 113### `void i2c_init(void)`
114 114
115| Variable | Default | 115Initialize the I2C driver. This function must be called only once, before any of the below functions can be called.
116|---------------------|---------|
117| `I2C1_SCL_PAL_MODE` | `4` |
118| `I2C1_SDA_PAL_MODE` | `4` |
119 116
120#### Other :id=other 117This function is weakly defined, meaning it can be overridden if necessary for your particular use case:
121You can also overload the `void i2c_init(void)` function, which has a weak attribute. If you do this the configuration variables above will not be used. Please consult the datasheet of your MCU for the available GPIO configurations. The following is an example initialization function:
122 118
123```c 119```c
124void i2c_init(void) 120void i2c_init(void) {
125{ 121 setPinInput(B6); // Try releasing special pins for a short time
126 setPinInput(B6); // Try releasing special pins for a short time 122 setPinInput(B7);
127 setPinInput(B7); 123 wait_ms(10); // Wait for the release to happen
128 wait_ms(10); // Wait for the release to happen 124
129 125 palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B6 to I2C function
130 palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B6 to I2C function 126 palSetPadMode(GPIOB, 7, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B7 to I2C function
131 palSetPadMode(GPIOB, 7, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B7 to I2C function
132} 127}
133``` 128```
129
130---
131
132### `i2c_status_t i2c_start(uint8_t address, uint16_t timeout)`
133
134Start an I2C transaction.
135
136#### Arguments
137
138 - `uint8_t address`
139 The 7-bit I2C address of the device (ie. without the read/write bit - this will be set automatically).
140 - `uint16_t timeout`
141 The time in milliseconds to wait for a response from the target device.
142
143#### Return Value
144
145`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
146
147---
148
149### `i2c_status_t i2c_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout)`
150
151Send multiple bytes to the selected I2C device.
152
153#### Arguments
154
155 - `uint8_t address`
156 The 7-bit I2C address of the device.
157 - `uint8_t *data`
158 A pointer to the data to transmit.
159 - `uint16_t length`
160 The number of bytes to write. Take care not to overrun the length of `data`.
161 - `uint16_t timeout`
162 The time in milliseconds to wait for a response from the target device.
163
164#### Return Value
165
166`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
167
168---
169
170### `i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout)`
171
172Receive multiple bytes from the selected SPI device.
173
174#### Arguments
175
176 - `uint8_t address`
177 The 7-bit I2C address of the device.
178 - `uint8_t *data`
179 A pointer to the buffer to read into.
180 - `uint16_t length`
181 The number of bytes to read. Take care not to overrun the length of `data`.
182 - `uint16_t timeout`
183 The time in milliseconds to wait for a response from the target device.
184
185#### Return Value
186
187`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
188
189---
190
191### `i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)`
192
193Writes to a register on the I2C device.
194
195#### Arguments
196
197 - `uint8_t devaddr`
198 The 7-bit I2C address of the device.
199 - `uint8_t regaddr`
200 The register address to write to.
201 - `uint8_t *data`
202 A pointer to the data to transmit.
203 - `uint16_t length`
204 The number of bytes to write. Take care not to overrun the length of `data`.
205 - `uint16_t timeout`
206 The time in milliseconds to wait for a response from the target device.
207
208#### Return Value
209
210`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
211
212---
213
214### `i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)`
215
216Reads from a register on the I2C device.
217
218#### Arguments
219
220 - `uint8_t devaddr`
221 The 7-bit I2C address of the device.
222 - `uint8_t regaddr`
223 The register address to read from.
224 - `uint16_t length`
225 The number of bytes to read. Take care not to overrun the length of `data`.
226 - `uint16_t timeout`
227 The time in milliseconds to wait for a response from the target device.
228
229#### Return Value
230
231`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
232
233---
234
235### `i2c_status_t i2c_stop(void)`
236
237Stop the current I2C transaction.