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

drivers: Implemented i2c bitbang driver #12616

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
20 changes: 20 additions & 0 deletions docs/i2c_driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,23 @@ Reads from a register with a 16-bit address (big endian) on the I2C device.
### `i2c_status_t i2c_stop(void)`

Stop the current I2C transaction.

## I2C bitbang driver

The I2C bitbang driver can be enabled on avr and chibios targets, with varying performance.
To enable it set `I2C_MASTER_DRIVER = bitbang` in `rules.mk`

### Configuration options

|`config.h` Overrride |Description |Default |
|---------------------------|-----------------------------------------------------------------------------------------------------------------|-------------------------------------------------|
|`I2C_BITBANG_SDA_PIN` | Selects which GPIO the SDA line is connected to | not defined |
|`I2C_BITBANG_SCL_PIN` | Selects which GPIO the SCL line is connected to | not defined |
|`I2C_BITBANG_STANDARD_MODE`| Defining this will configure the driver for 100kHz standard mode. If not defined, it will be in 400kHz fast mode| not defined |
|`I2C_BITBANG_FREQUENCY_KHZ`| The target SCL clock frequency in kHz | 100 or 400 (depending on what mode is selected) |

### Performance

- On STM32 MCUs with RT clock (where `PORT_SUPPORTS_RT == TRUE`), the i2c bitbang driver is fairly efficient, and in fast mode it can achieve above 390kHz.
- On STM32 MCUs without an RT clock, no optimisation has been attempted, and due to the granularity of the wait_us() functions, the driver will operate only around 25kHz. This may be improved by tweaking `CH_CFG_ST_FREQUENCY` and `CH_CFG_ST_TIMEDELTA`.
- On AVR MCUs running at 16MHz, optimized routines have been implemented, that can achieve exactly 400kHz in fast mode, as long as there are no interrupts, and clock stretching is never requested by the device.
93 changes: 93 additions & 0 deletions drivers/i2c_bitbang/define_avr_delay.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// clang-format off
#ifndef AVR_OUTER_NAME
# define _AVR_OUTER_NAME(NAME) NAME ## _outer
# define AVR_OUTER_NAME(NAME) _AVR_OUTER_NAME(NAME)
# define _AVR_INNER_NAME(NAME) #NAME
# define AVR_INNER_NAME(NAME) _AVR_INNER_NAME(NAME)
#endif

void __attribute__ ((noinline)) AVR_OUTER_NAME(AVR_DELAY_NAME)(void) {
asm volatile (
AVR_INNER_NAME(AVR_DELAY_NAME) ":" "\n\t"
#if (ARCH == ARCH_XMEGA)
"nop" "\n\t" /* 1 cycle: xmega call instruction is 1 cycle faster */
#endif
#if ((!defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == 8) || ((defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == (8 + 2))
NOPS_0 "\n\t" /* 0 cycles */
"ret" /* 4 cycles on 16-bit PC devices */
:
:
: "memory");
#elif ((!defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == 9) || ((defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == (9 + 2))
NOPS_1 "\n\t" /* 1 cycles */
"ret" /* 4 cycles on 16-bit PC devices */
:
:
: "memory");
#elif ((!defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == 10) || ((defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == (10 + 2))
NOPS_2 "\n\t" /* 2 cycles */
"ret" /* 4 cycles on 16-bit PC devices */
:
:
: "memory");
#elif ((!defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == 11) || ((defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == (11 + 2))
NOPS_3 "\n\t" /* 3 cycles */
"ret" /* 4 cycles on 16-bit PC devices */
:
:
: "memory");
#elif ((!defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == 12) || ((defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == (12 + 2))
NOPS_4 "\n\t" /* 4 cycles */
"ret" /* 4 cycles on 16-bit PC devices */
:
:
: "memory");
#elif ((!defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == 13) || ((defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == (13 + 2))
NOPS_5 "\n\t" /* 5 cycles */
"ret" /* 4 cycles on 16-bit PC devices */
:
:
: "memory");
#elif ((!defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == 14) || ((defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT == (14 + 2))
NOPS_6 "\n\t" /* 6 cycles */
"ret" /* 4 cycles on 16-bit PC devices */
:
:
: "memory");
#elif ((!defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT >= 15) || ((defined(__AVR_3_BYTE_PC__)) && AVR_DELAY_AMOUNT >= (15 + 2))
# if (ARCH == ARCH_XMEGA)
"push r16" "\n\t" /* 1 cycle */
"ldi r16, %[totalcnt]" "\n\t" /* 1 cycle */
"1:" "dec r16" "\n\t" /* 1 cycle */
"brne 1b" "\n\t" /* 2 cycles when jumping, 1 cycle when not */
# if ((AVR_DELAY_AMOUNT - 8 - 2) % 3) >= 1
"nop" "\n\t" /* 1 cycle */
# endif
# if ((AVR_DELAY_AMOUNT - 8 - 2) % 3) >= 2
"nop" "\n\t" /* 1 cycle */
# endif
"pop r16" "\n\t" /* 1 cycle */
"ret" /* 4 cycles on 16-bit PC devices */
:
: [totalcnt] "M" ((AVR_DELAY_AMOUNT - 8 - 2)/3)
: "memory");
# else
"push r16" "\n\t" /* 2 cycles */
"ldi r16, %[totalcnt]" "\n\t" /* 1 cycle */
"1:" "dec r16" "\n\t" /* 1 cycle */
"brne 1b" "\n\t" /* 2 cycles when jumping, 1 cycle when not */
# if ((AVR_DELAY_AMOUNT - 8 - 4) % 3) >= 1
"nop" "\n\t" /* 1 cycle */
# endif
# if ((AVR_DELAY_AMOUNT - 8 - 4) % 3) >= 2
"nop" "\n\t" /* 1 cycle */
# endif
"pop r16" "\n\t" /* 2 cycles */
"ret" /* 4 cycles on 16-bit PC devices */
:
: [totalcnt] "M" ((AVR_DELAY_AMOUNT - 8 - 4)/3)
: "memory");
# endif
#endif
}
// clang-format on
Loading