diff --git a/conf/firmwares/demo.makefile b/conf/firmwares/demo.makefile index c0070243a8..645aa5eab5 100644 --- a/conf/firmwares/demo.makefile +++ b/conf/firmwares/demo.makefile @@ -69,7 +69,7 @@ COMMON_DEMO_SRCS += $(SRC_ARCH)/led_hw.c COMMON_DEMO_SRCS += $(SRC_ARCH)/mcu_periph/gpio_arch.c endif -COMMON_DEMO_SRCS += mcu_periph/i2c.c $(SRC_ARCH)/mcu_periph/i2c_arch.c +COMMON_DEMO_SRCS += mcu_periph/i2c.c mcu_periph/softi2c.c $(SRC_ARCH)/mcu_periph/i2c_arch.c COMMON_DEMO_SRCS += mcu_periph/uart.c COMMON_DEMO_SRCS += $(SRC_ARCH)/mcu_periph/uart_arch.c ifeq ($(ARCH), linux) diff --git a/conf/firmwares/rotorcraft.makefile b/conf/firmwares/rotorcraft.makefile index b2c6833a61..ba2600189b 100644 --- a/conf/firmwares/rotorcraft.makefile +++ b/conf/firmwares/rotorcraft.makefile @@ -136,6 +136,7 @@ endif # TARGET == fbw ifneq ($(TARGET), fbw) $(TARGET).srcs += mcu_periph/i2c.c +$(TARGET).srcs += mcu_periph/softi2c.c $(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.c endif diff --git a/conf/firmwares/rover.makefile b/conf/firmwares/rover.makefile index 4c592e35a5..34412ce0d5 100644 --- a/conf/firmwares/rover.makefile +++ b/conf/firmwares/rover.makefile @@ -120,6 +120,7 @@ endif ## $(TARGET).srcs += mcu_periph/i2c.c +$(TARGET).srcs += mcu_periph/softi2c.c $(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.c include $(CFG_SHARED)/uart.makefile diff --git a/conf/firmwares/subsystems/fixedwing/autopilot.makefile b/conf/firmwares/subsystems/fixedwing/autopilot.makefile index 65cc3106c8..33b9c0ed48 100644 --- a/conf/firmwares/subsystems/fixedwing/autopilot.makefile +++ b/conf/firmwares/subsystems/fixedwing/autopilot.makefile @@ -91,6 +91,7 @@ endif # ifneq ($(TARGET),fbw) $(TARGET).srcs += mcu_periph/i2c.c +$(TARGET).srcs += mcu_periph/softi2c.c $(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.c endif diff --git a/conf/modules/i2c.xml b/conf/modules/i2c.xml index b6c0b59fc3..1f0b46bc38 100644 --- a/conf/modules/i2c.xml +++ b/conf/modules/i2c.xml @@ -12,6 +12,7 @@ + diff --git a/sw/airborne/arch/chibios/mcu_periph/i2c_arch.c b/sw/airborne/arch/chibios/mcu_periph/i2c_arch.c index 51e557bd41..d25e694aae 100644 --- a/sw/airborne/arch/chibios/mcu_periph/i2c_arch.c +++ b/sw/airborne/arch/chibios/mcu_periph/i2c_arch.c @@ -46,6 +46,12 @@ #define I2C_THREAD_STACK_SIZE 512 #endif + +static bool i2c_chibios_idle(struct i2c_periph *p) __attribute__((unused)); +static bool i2c_chibios_submit(struct i2c_periph *p, struct i2c_transaction *t) __attribute__((unused)); +static void i2c_chibios_setbitrate(struct i2c_periph *p, int bitrate) __attribute__((unused)); + + #if USE_I2C1 || USE_I2C2 || USE_I2C3 || USE_I2C4 // private I2C init structure @@ -209,6 +215,10 @@ static THD_WORKING_AREA(wa_thd_i2c1, I2C_THREAD_STACK_SIZE); */ void i2c1_hw_init(void) { + i2c1.idle = i2c_chibios_idle; + i2c1.submit = i2c_chibios_submit; + i2c1.setbitrate = i2c_chibios_setbitrate; + i2cStart(&I2CD1, &i2cfg1); i2c1.reg_addr = &I2CD1; i2c1.errors = &i2c1_errors; @@ -263,6 +273,10 @@ static THD_WORKING_AREA(wa_thd_i2c2, I2C_THREAD_STACK_SIZE); */ void i2c2_hw_init(void) { + i2c2.idle = i2c_chibios_idle; + i2c2.submit = i2c_chibios_submit; + i2c2.setbitrate = i2c_chibios_setbitrate; + i2cStart(&I2CD2, &i2cfg2); i2c2.reg_addr = &I2CD2; i2c2.errors = &i2c2_errors; @@ -317,6 +331,10 @@ static THD_WORKING_AREA(wa_thd_i2c3, I2C_THREAD_STACK_SIZE); */ void i2c3_hw_init(void) { + i2c3.idle = i2c_chibios_idle; + i2c3.submit = i2c_chibios_submit; + i2c3.setbitrate = i2c_chibios_setbitrate; + i2cStart(&I2CD3, &i2cfg3); i2c3.reg_addr = &I2CD3; i2c3.init_struct = NULL; @@ -372,6 +390,10 @@ static THD_WORKING_AREA(wa_thd_i2c4, 128); */ void i2c4_hw_init(void) { + i2c4.idle = i2c_chibios_idle; + i2c4.submit = i2c_chibios_submit; + i2c4.setbitrate = i2c_chibios_setbitrate; + i2cStart(&I2CD4, &i2cfg4); i2c4.reg_addr = &I2CD4; i2c4.init_struct = NULL; @@ -411,7 +433,7 @@ void i2c_event(void) {} * Empty, for paparazzi compatibility only. Bitrate is already * set in i2cX_hw_init() */ -void i2c_setbitrate(struct i2c_periph *p __attribute__((unused)), int bitrate __attribute__((unused))) {} +static void i2c_chibios_setbitrate(struct i2c_periph *p __attribute__((unused)), int bitrate __attribute__((unused))) {} /** * i2c_submit() function @@ -430,7 +452,7 @@ void i2c_setbitrate(struct i2c_periph *p __attribute__((unused)), int bitrate __ * @param[in] p pointer to a @p i2c_periph struct * @param[in] t pointer to a @p i2c_transaction struct */ -bool i2c_submit(struct i2c_periph *p, struct i2c_transaction *t) +static bool i2c_chibios_submit(struct i2c_periph *p, struct i2c_transaction *t) { #if USE_I2C1 || USE_I2C2 || USE_I2C3 || USE_I2C4 // sys lock @@ -469,7 +491,7 @@ bool i2c_submit(struct i2c_periph *p, struct i2c_transaction *t) * * Empty, for paparazzi compatibility only */ -bool i2c_idle(struct i2c_periph *p __attribute__((unused))) +static bool i2c_chibios_idle(struct i2c_periph *p __attribute__((unused))) { return FALSE; } diff --git a/sw/airborne/arch/linux/mcu_periph/i2c_arch.c b/sw/airborne/arch/linux/mcu_periph/i2c_arch.c index 5254bd4a42..706a05ace3 100644 --- a/sw/airborne/arch/linux/mcu_periph/i2c_arch.c +++ b/sw/airborne/arch/linux/mcu_periph/i2c_arch.c @@ -42,6 +42,12 @@ #define I2C_THREAD_PRIO 10 #endif + +static bool i2c_linux_idle(struct i2c_periph *p __attribute__((unused))) __attribute__((unused)); +static bool i2c_linux_submit(struct i2c_periph *p, struct i2c_transaction *t) __attribute__((unused)); +static void i2c_linux_setbitrate(struct i2c_periph *p __attribute__((unused)), int bitrate __attribute__((unused))) __attribute__((unused)); + + static void *i2c_thread(void *thread_data); // private I2C init structure @@ -66,16 +72,16 @@ void i2c_event(void) { } -void i2c_setbitrate(struct i2c_periph *p __attribute__((unused)), int bitrate __attribute__((unused))) +static void i2c_linux_setbitrate(struct i2c_periph *p __attribute__((unused)), int bitrate __attribute__((unused))) { } -bool i2c_idle(struct i2c_periph *p __attribute__((unused))) +static bool i2c_linux_idle(struct i2c_periph *p __attribute__((unused))) { return true; } -bool i2c_submit(struct i2c_periph *p, struct i2c_transaction *t) +static bool i2c_linux_submit(struct i2c_periph *p, struct i2c_transaction *t) { // get mutex lock and condition pthread_mutex_t *mutex = &(((struct i2c_thread_t *)(p->init_struct))->mutex); @@ -204,6 +210,10 @@ struct i2c_thread_t i2c0_thread; void i2c0_hw_init(void) { + i2c0.idle = i2c_linux_idle; + i2c0.submit = i2c_linux_submit; + i2c0.setbitrate = i2c_linux_setbitrate; + i2c0.reg_addr = (void *)open("/dev/i2c-0", O_RDWR); i2c0.errors = &i2c0_errors; @@ -224,6 +234,10 @@ struct i2c_thread_t i2c1_thread; void i2c1_hw_init(void) { + i2c1.idle = i2c_linux_idle; + i2c1.submit = i2c_linux_submit; + i2c1.setbitrate = i2c_linux_setbitrate; + i2c1.reg_addr = (void *)open("/dev/i2c-1", O_RDWR); i2c1.errors = &i2c1_errors; @@ -244,6 +258,10 @@ struct i2c_thread_t i2c2_thread; void i2c2_hw_init(void) { + i2c2.idle = i2c_linux_idle; + i2c2.submit = i2c_linux_submit; + i2c2.setbitrate = i2c_linux_setbitrate; + i2c2.reg_addr = (void *)open("/dev/i2c-2", O_RDWR); i2c2.errors = &i2c2_errors; @@ -264,6 +282,10 @@ struct i2c_thread_t i2c3_thread; void i2c3_hw_init(void) { + i2c3.idle = i2c_linux_idle; + i2c3.submit = i2c_linux_submit; + i2c3.setbitrate = i2c_linux_setbitrate; + i2c3.reg_addr = (void *)open("/dev/i2c-3", O_RDWR); i2c3.errors = &i2c3_errors; diff --git a/sw/airborne/arch/lpc21/mcu_periph/i2c_arch.c b/sw/airborne/arch/lpc21/mcu_periph/i2c_arch.c index 5e7d024814..2b13db730d 100644 --- a/sw/airborne/arch/lpc21/mcu_periph/i2c_arch.c +++ b/sw/airborne/arch/lpc21/mcu_periph/i2c_arch.c @@ -33,6 +33,12 @@ #include BOARD_CONFIG #include "armVIC.h" + +static bool i2c_lpc21_idle(struct i2c_periph *p) __attribute__((unused)); +static bool i2c_lpc21_submit(struct i2c_periph *p, struct i2c_transaction *t) __attribute__((unused)); +static void i2c_lpc21_setbitrate(struct i2c_periph *p, int bitrate) __attribute__((unused)); + + /////////////////// // I2C Automaton // /////////////////// @@ -231,6 +237,9 @@ uint8_t i2c0_vic_channel; /* SCL0 on P0.2 */ void i2c0_hw_init(void) { + i2c0.idle = i2c_lpc21_idle; + i2c0.submit = i2c_lpc21_submit; + i2c0.setbitrate = i2c_lpc21_setbitrate; i2c0.reg_addr = I2C0; i2c0_vic_channel = VIC_I2C0; @@ -316,6 +325,9 @@ uint8_t i2c1_vic_channel; /* SCL1 on P0.11 */ void i2c1_hw_init(void) { + i2c1.idle = i2c_lpc21_idle; + i2c1.submit = i2c_lpc21_submit; + i2c1.setbitrate = i2c_lpc21_setbitrate; i2c1.reg_addr = I2C1; i2c1_vic_channel = VIC_I2C1; @@ -341,12 +353,12 @@ void i2c1_hw_init(void) #endif /* USE_I2C1 */ -bool i2c_idle(struct i2c_periph *p) +static bool i2c_lpc21_idle(struct i2c_periph *p) { return p->status == I2CIdle; } -bool i2c_submit(struct i2c_periph *p, struct i2c_transaction *t) +static bool i2c_lpc21_submit(struct i2c_periph *p, struct i2c_transaction *t) { uint8_t idx; @@ -383,7 +395,7 @@ bool i2c_submit(struct i2c_periph *p, struct i2c_transaction *t) void i2c_event(void) { } -void i2c_setbitrate(struct i2c_periph *p, int bitrate) +static void i2c_lpc21_setbitrate(struct i2c_periph *p, int bitrate) { int period = 15000000 / 2 / bitrate; // Max 400kpbs diff --git a/sw/airborne/arch/sim/mcu_periph/gpio_arch.h b/sw/airborne/arch/sim/mcu_periph/gpio_arch.h index b027593dc1..1392b3aef4 100644 --- a/sw/airborne/arch/sim/mcu_periph/gpio_arch.h +++ b/sw/airborne/arch/sim/mcu_periph/gpio_arch.h @@ -77,5 +77,6 @@ static inline void gpio_setup_input(uint32_t port __attribute__((unused)), uint1 static inline void gpio_set(uint32_t port __attribute__((unused)), uint16_t pin __attribute__((unused))) {} static inline void gpio_clear(uint32_t port __attribute__((unused)), uint16_t pin __attribute__((unused))) {} static inline void gpio_toggle(uint32_t port __attribute__((unused)), uint16_t pin __attribute__((unused))) {} +static inline uint16_t gpio_get(uint32_t gpioport __attribute__((unused)), uint16_t gpios __attribute__((unused))) { return FALSE; } #endif /* GPIO_ARCH_H */ diff --git a/sw/airborne/arch/sim/mcu_periph/i2c_arch.c b/sw/airborne/arch/sim/mcu_periph/i2c_arch.c index 9dcbbcc78e..907b9e1e9b 100644 --- a/sw/airborne/arch/sim/mcu_periph/i2c_arch.c +++ b/sw/airborne/arch/sim/mcu_periph/i2c_arch.c @@ -27,16 +27,19 @@ #include "mcu_periph/i2c.h" -bool i2c_idle(struct i2c_periph *p __attribute__((unused))) { return true; } -bool i2c_submit(struct i2c_periph *p __attribute__((unused)), struct i2c_transaction *t __attribute__((unused))) { return true;} +static bool __attribute__((unused)) i2c_sim_idle(struct i2c_periph *p __attribute__((unused))) { return true; } +static bool __attribute__((unused)) i2c_sim_submit(struct i2c_periph *p __attribute__((unused)), struct i2c_transaction *t __attribute__((unused))) { return true;} +static void __attribute__((unused)) i2c_sim_setbitrate(struct i2c_periph *p __attribute__((unused)), int bitrate __attribute__((unused))) { } void i2c_event(void) { } -void i2c_setbitrate(struct i2c_periph *p __attribute__((unused)), int bitrate __attribute__((unused))) { } #if USE_I2C0 struct i2c_errors i2c0_errors; void i2c0_hw_init(void) { + i2c0.idle = i2c_sim_idle; + i2c0.submit = i2c_sim_submit; + i2c0.setbitrate = i2c_sim_setbitrate; i2c0.errors = &i2c0_errors; ZEROS_ERR_COUNTER(i2c0_errors); } @@ -48,6 +51,9 @@ struct i2c_errors i2c1_errors; void i2c1_hw_init(void) { + i2c1.idle = i2c_sim_idle; + i2c1.submit = i2c_sim_submit; + i2c1.setbitrate = i2c_sim_setbitrate; i2c1.errors = &i2c1_errors; ZEROS_ERR_COUNTER(i2c1_errors); } @@ -59,6 +65,9 @@ struct i2c_errors i2c2_errors; void i2c2_hw_init(void) { + i2c2.idle = i2c_sim_idle; + i2c2.submit = i2c_sim_submit; + i2c2.setbitrate = i2c_sim_setbitrate; i2c2.errors = &i2c2_errors; ZEROS_ERR_COUNTER(i2c2_errors); } diff --git a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c index 2f1d56b1e8..8337fbc100 100644 --- a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c @@ -38,6 +38,11 @@ #include "mcu_periph/gpio.h" +static bool i2c_stm32_idle(struct i2c_periph *periph) __attribute__((unused)); +static bool i2c_stm32_submit(struct i2c_periph *periph, struct i2c_transaction *t) __attribute__((unused)); +static void i2c_stm32_setbitrate(struct i2c_periph *periph, int bitrate) __attribute__((unused)); + + ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// @@ -845,6 +850,9 @@ struct i2c_errors i2c1_errors; void i2c1_hw_init(void) { + i2c1.idle = i2c_stm32_idle; + i2c1.submit = i2c_stm32_submit; + i2c1.setbitrate = i2c_stm32_setbitrate; i2c1.reg_addr = (void *)I2C1; i2c1.init_struct = NULL; @@ -916,6 +924,9 @@ struct i2c_errors i2c2_errors; void i2c2_hw_init(void) { + i2c2.idle = i2c_stm32_idle; + i2c2.submit = i2c_stm32_submit; + i2c2.setbitrate = i2c_stm32_setbitrate; i2c2.reg_addr = (void *)I2C2; i2c2.init_struct = NULL; @@ -989,6 +1000,9 @@ struct i2c_errors i2c3_errors; void i2c3_hw_init(void) { + i2c3.idle = i2c_stm32_idle; + i2c3.submit = i2c_stm32_submit; + i2c3.setbitrate = i2c_stm32_setbitrate; i2c3.reg_addr = (void *)I2C3; i2c3.init_struct = NULL; @@ -1054,7 +1068,7 @@ void i2c3_er_isr(void) // -short wires, low capacitance bus: IMU: high speed // -long wires with a lot of capacitance: motor controller: put speed as low as possible -void i2c_setbitrate(struct i2c_periph *periph, int bitrate) +static void i2c_stm32_setbitrate(struct i2c_periph *periph, int bitrate) { // If NOT Busy if (i2c_idle(periph)) { @@ -1312,7 +1326,7 @@ void i2c_event(void) ///////////////////////////////////////////////////////// // Implement Interface Functions -bool i2c_submit(struct i2c_periph *periph, struct i2c_transaction *t) +static bool i2c_stm32_submit(struct i2c_periph *periph, struct i2c_transaction *t) { if (periph->watchdog > WD_DELAY) { return false; @@ -1349,7 +1363,7 @@ bool i2c_submit(struct i2c_periph *periph, struct i2c_transaction *t) return true; } -bool i2c_idle(struct i2c_periph *periph) +static bool i2c_stm32_idle(struct i2c_periph *periph) { // This is actually a difficult function: // -simply reading the status flags can clear bits and corrupt the transaction diff --git a/sw/airborne/mcu.c b/sw/airborne/mcu.c index 50afbee51c..063ff87c80 100644 --- a/sw/airborne/mcu.c +++ b/sw/airborne/mcu.c @@ -43,10 +43,14 @@ #define USING_UART 1 #include "mcu_periph/uart.h" #endif -#if USE_I2C0 || USE_I2C1 || USE_I2C2 || USE_I2C3 || USE_I2C4 +#if USE_I2C0 || USE_I2C1 || USE_I2C2 || USE_I2C3 || USE_I2C4 || USE_SOFTI2C0 || USE_SOFTI2C1 #define USING_I2C 1 #include "mcu_periph/i2c.h" #endif +#if USE_SOFTI2C0 || USE_SOFTI2C1 +#define USING_SOFTI2C 1 +#include "mcu_periph/softi2c.h" +#endif #if USE_ADC #include "mcu_periph/adc.h" #endif @@ -176,6 +180,12 @@ void mcu_init(void) #ifdef USE_I2C4 i2c4_init(); #endif +#ifdef USE_SOFTI2C0 + softi2c0_init(); +#endif +#ifdef USE_SOFTI2C1 + softi2c1_init(); +#endif #if USE_ADC adc_init(); #endif @@ -250,6 +260,9 @@ void mcu_event(void) #if USING_I2C i2c_event(); #endif +#if USING_SOFTI2C + softi2c_event(); +#endif #if USE_USB_SERIAL VCOM_event(); diff --git a/sw/airborne/mcu_periph/i2c.c b/sw/airborne/mcu_periph/i2c.c index 3a79804ff6..8d0a0a20b5 100644 --- a/sw/airborne/mcu_periph/i2c.c +++ b/sw/airborne/mcu_periph/i2c.c @@ -249,6 +249,14 @@ static void send_i2c4_err(struct transport_tx *trans, struct link_device *dev) #endif /* USE_I2C4 */ +#if USE_SOFTI2C0 +extern void send_softi2c0_err(struct transport_tx *trans, struct link_device *dev); +#endif /* USE_SOFTI2C0 */ + +#if USE_SOFTI2C1 +extern void send_softi2c1_err(struct transport_tx *trans, struct link_device *dev); +#endif /* USE_SOFTI2C1 */ + #if PERIODIC_TELEMETRY static void send_i2c_err(struct transport_tx *trans __attribute__((unused)), struct link_device *dev __attribute__((unused))) @@ -278,13 +286,21 @@ static void send_i2c_err(struct transport_tx *trans __attribute__((unused)), case 4: #if USE_I2C4 send_i2c4_err(trans, dev); +#endif + case 5: +#if USE_SOFTI2C0 + send_softi2c0_err(trans, dev); +#endif + case 6: +#if USE_SOFTI2C1 + send_softi2c1_err(trans, dev); #endif break; default: break; } _i2c_nb_cnt++; - if (_i2c_nb_cnt == 5) { + if (_i2c_nb_cnt == 7) { _i2c_nb_cnt = 0; } } @@ -354,6 +370,7 @@ bool i2c_blocking_transmit(struct i2c_periph *p, struct i2c_transaction *t, // Wait for transaction to complete float start_t = get_sys_time_float(); while (t->status == I2CTransPending || t->status == I2CTransRunning) { + if (p->spin) p->spin(p); if (get_sys_time_float() - start_t > I2C_BLOCKING_TIMEOUT) { break; // timeout after 1 second } @@ -375,6 +392,7 @@ bool i2c_blocking_receive(struct i2c_periph *p, struct i2c_transaction *t, // Wait for transaction to complete float start_t = get_sys_time_float(); while (t->status == I2CTransPending || t->status == I2CTransRunning) { + if (p->spin) p->spin(p); if (get_sys_time_float() - start_t > I2C_BLOCKING_TIMEOUT) { break; // timeout after 1 second } @@ -396,6 +414,7 @@ bool i2c_blocking_transceive(struct i2c_periph *p, struct i2c_transaction *t, // Wait for transaction to complete float start_t = get_sys_time_float(); while (t->status == I2CTransPending || t->status == I2CTransRunning) { + if (p->spin) p->spin(p); if (get_sys_time_float() - start_t > I2C_BLOCKING_TIMEOUT) { break; // timeout after 1 second } diff --git a/sw/airborne/mcu_periph/i2c.h b/sw/airborne/mcu_periph/i2c.h index 352a0ac266..27ef685cde 100644 --- a/sw/airborne/mcu_periph/i2c.h +++ b/sw/airborne/mcu_periph/i2c.h @@ -135,7 +135,18 @@ struct i2c_transaction { /** I2C peripheral structure. */ +struct i2c_periph; +typedef bool i2c_idle_fn_t(struct i2c_periph *p); +typedef bool i2c_submit_fn_t(struct i2c_periph *p, struct i2c_transaction *t); +typedef void i2c_setbitrate_fn_t(struct i2c_periph *p, int bitrate); +typedef void i2c_spin_fn_t(struct i2c_periph *p); // To update peripherals within tight loops, e.g. the blocking functions. Leave NULL if not required. + struct i2c_periph { + /* architecture-specific functions */ + i2c_idle_fn_t *idle; + i2c_submit_fn_t *submit; + i2c_setbitrate_fn_t *setbitrate; + i2c_spin_fn_t *spin; /* circular buffer holding transactions */ struct i2c_transaction *trans[I2C_TRANSACTION_QUEUE_LEN]; uint8_t trans_insert_idx; @@ -223,6 +234,18 @@ extern void i2c4_init(void); #endif /* USE_I2C4 */ +#if USE_SOFTI2C0 +extern struct i2c_periph softi2c0; +extern void softi2c0_init(void); +#endif /* USE_SOFTI2C0 */ + + +#if USE_SOFTI2C1 +extern struct i2c_periph softi2c1; +extern void softi2c1_init(void); +#endif /* USE_SOFTI2C1 */ + + /** Initialize I2C peripheral */ extern void i2c_init(struct i2c_periph *p); @@ -230,7 +253,9 @@ extern void i2c_init(struct i2c_periph *p); * @param p i2c peripheral to be used * @return TRUE if idle */ -extern bool i2c_idle(struct i2c_periph *p); +static inline bool i2c_idle(struct i2c_periph *p) { + return p->idle(p); +} /** Submit a I2C transaction. * Must be implemented by the underlying architecture @@ -238,13 +263,18 @@ extern bool i2c_idle(struct i2c_periph *p); * @param t i2c transaction * @return TRUE if insertion to the transaction queue succeeded */ -extern bool i2c_submit(struct i2c_periph *p, struct i2c_transaction *t); +static inline bool i2c_submit(struct i2c_periph *p, struct i2c_transaction *t) { + return p->submit(p, t); +} /** Set I2C bitrate. * @param p i2c peripheral to be used * @param bitrate bitrate */ -extern void i2c_setbitrate(struct i2c_periph *p, int bitrate); +static inline void i2c_setbitrate(struct i2c_periph *p, int bitrate) { + p->setbitrate(p, bitrate); +} + extern void i2c_event(void); /* diff --git a/sw/airborne/mcu_periph/softi2c.c b/sw/airborne/mcu_periph/softi2c.c new file mode 100644 index 0000000000..7d1edefb30 --- /dev/null +++ b/sw/airborne/mcu_periph/softi2c.c @@ -0,0 +1,668 @@ +/* + * Copyright (C) 2020 Tom van Dijk + * + * This file is part of paparazzi. + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/** + * @file mcu_periph/softi2c.c + * Platform-independent software I2C implementation. + * Can be used transparently in place of the hardware I2C in i2c.h. + * + * This implementation can only be used as the (only) master on the I2C bus. + */ + +#include "mcu_periph/softi2c.h" + +#include "mcu_arch.h" +#include "mcu_periph/gpio.h" +#include "mcu_periph/sys_time.h" + +#if PERIODIC_TELEMETRY +#include "subsystems/datalink/telemetry.h" +#endif + +#include + + +#ifndef SOFTI2C0_DIVIDER +#define SOFTI2C0_DIVIDER 1 +#endif +#ifndef SOFTI2C1_DIVIDER +#define SOFTI2C1_DIVIDER 1 +#endif + + +struct softi2c_device { + struct i2c_periph *periph; + uint32_t sda_port; + uint16_t sda_pin; + uint32_t scl_port; + uint16_t scl_pin; + /* Bit-level state machine */ + uint8_t bit_state; + /* Byte-level state machine */ + uint8_t byte_state; + /* Transaction-level state machine */ + // periph->status + // periph->idx_buf +}; + + +static bool softi2c_idle(struct i2c_periph *periph) __attribute__((unused)); +static bool softi2c_submit(struct i2c_periph *periph, struct i2c_transaction *t) __attribute__((unused)); +static void softi2c_setbitrate(struct i2c_periph *periph, int bitrate) __attribute__((unused)); +static void softi2c_spin(struct i2c_periph *p) __attribute__((unused)); + + +#if USE_SOFTI2C0 && ( \ + !defined(SOFTI2C0_SDA_GPIO) || !defined(SOFTI2C0_SDA_PIN) || \ + !defined(SOFTI2C0_SCL_GPIO) || !defined(SOFTI2C0_SCL_PIN)) +#error "SDA and SCL pins must be configured for SOFTI2C0!" +#endif + +#if USE_SOFTI2C1 && ( \ + !defined(SOFTI2C1_SDA_GPIO) || !defined(SOFTI2C1_SDA_PIN) || \ + !defined(SOFTI2C1_SCL_GPIO) || !defined(SOFTI2C1_SCL_PIN)) +#error "SDA and SCL pins must be configured for SOFTI2C1!" +#endif + + +#if USE_SOFTI2C0 +struct i2c_periph softi2c0; +static struct softi2c_device softi2c0_device; +void softi2c0_init(void) { + i2c_init(&softi2c0); + softi2c0_hw_init(); +} +#if PERIODIC_TELEMETRY +void send_softi2c0_err(struct transport_tx *trans, struct link_device *dev) +{ + uint16_t i2c0_wd_reset_cnt = softi2c0.errors->wd_reset_cnt; + uint16_t i2c0_queue_full_cnt = softi2c0.errors->queue_full_cnt; + uint16_t i2c0_ack_fail_cnt = softi2c0.errors->ack_fail_cnt; + uint16_t i2c0_miss_start_stop_cnt = softi2c0.errors->miss_start_stop_cnt; + uint16_t i2c0_arb_lost_cnt = softi2c0.errors->arb_lost_cnt; + uint16_t i2c0_over_under_cnt = softi2c0.errors->over_under_cnt; + uint16_t i2c0_pec_recep_cnt = softi2c0.errors->pec_recep_cnt; + uint16_t i2c0_timeout_tlow_cnt = softi2c0.errors->timeout_tlow_cnt; + uint16_t i2c0_smbus_alert_cnt = softi2c0.errors->smbus_alert_cnt; + uint16_t i2c0_unexpected_event_cnt = softi2c0.errors->unexpected_event_cnt; + uint32_t i2c0_last_unexpected_event = softi2c0.errors->last_unexpected_event; + uint8_t _bus0 = 10; + pprz_msg_send_I2C_ERRORS(trans, dev, AC_ID, + &i2c0_wd_reset_cnt, + &i2c0_queue_full_cnt, + &i2c0_ack_fail_cnt, + &i2c0_miss_start_stop_cnt, + &i2c0_arb_lost_cnt, + &i2c0_over_under_cnt, + &i2c0_pec_recep_cnt, + &i2c0_timeout_tlow_cnt, + &i2c0_smbus_alert_cnt, + &i2c0_unexpected_event_cnt, + &i2c0_last_unexpected_event, + &_bus0); +} +#endif +#endif /* USE_SOFTI2C0 */ + +#if USE_SOFTI2C1 +struct i2c_periph softi2c1; +static struct softi2c_device softi2c1_device; +void softi2c1_init(void) { + i2c_init(&softi2c1); + softi2c1_hw_init(); +} +#if PERIODIC_TELEMETRY +void send_softi2c1_err(struct transport_tx *trans, struct link_device *dev) +{ + uint16_t i2c1_wd_reset_cnt = softi2c1.errors->wd_reset_cnt; + uint16_t i2c1_queue_full_cnt = softi2c1.errors->queue_full_cnt; + uint16_t i2c1_ack_fail_cnt = softi2c1.errors->ack_fail_cnt; + uint16_t i2c1_miss_start_stop_cnt = softi2c1.errors->miss_start_stop_cnt; + uint16_t i2c1_arb_lost_cnt = softi2c1.errors->arb_lost_cnt; + uint16_t i2c1_over_under_cnt = softi2c1.errors->over_under_cnt; + uint16_t i2c1_pec_recep_cnt = softi2c1.errors->pec_recep_cnt; + uint16_t i2c1_timeout_tlow_cnt = softi2c1.errors->timeout_tlow_cnt; + uint16_t i2c1_smbus_alert_cnt = softi2c1.errors->smbus_alert_cnt; + uint16_t i2c1_unexpected_event_cnt = softi2c1.errors->unexpected_event_cnt; + uint32_t i2c1_last_unexpected_event = softi2c1.errors->last_unexpected_event; + uint8_t _bus1 = 11; + pprz_msg_send_I2C_ERRORS(trans, dev, AC_ID, + &i2c1_wd_reset_cnt, + &i2c1_queue_full_cnt, + &i2c1_ack_fail_cnt, + &i2c1_miss_start_stop_cnt, + &i2c1_arb_lost_cnt, + &i2c1_over_under_cnt, + &i2c1_pec_recep_cnt, + &i2c1_timeout_tlow_cnt, + &i2c1_smbus_alert_cnt, + &i2c1_unexpected_event_cnt, + &i2c1_last_unexpected_event, + &_bus1); +} +#endif +#endif /* USE_SOFTI2C1 */ + + +static void softi2c_gpio_highz(uint32_t port, uint16_t pin) { +#if defined(CHIBIOS_MCU_ARCH_H) || defined(STM32_MCU_ARCH_H) + /* Arch's with input_pullup */ + gpio_setup_input_pullup(port, pin); +#else + /* Arch's without input_pullup */ + // Gpio_set might enable pullup on some architectures (e.g. arduino), not sure + // if it works here. If not, an external pull-up resistor is required on the + // I2C lines. + gpio_setup_input(port, pin); + gpio_set(port, pin); +#endif /* arch with/without input_pullup */ +} + +static bool softi2c_gpio_read(uint32_t port, uint16_t pin) { + softi2c_gpio_highz(port, pin); + return gpio_get(port, pin); +} + +static void softi2c_gpio_drive_low(uint32_t port, uint16_t pin) { + gpio_setup_output(port, pin); + gpio_clear(port, pin); +} + +static void softi2c_setup_gpio( + uint32_t sda_port, uint16_t sda_pin, + uint32_t scl_port, uint16_t scl_pin) __attribute__((unused)); + +static void softi2c_setup_gpio( + uint32_t sda_port, uint16_t sda_pin, + uint32_t scl_port, uint16_t scl_pin) { +#ifdef STM32_MCU_ARCH_H + gpio_enable_clock(sda_port); + gpio_enable_clock(scl_port); +#endif /* STM32F1/F4 */ + softi2c_gpio_highz(sda_port, sda_pin); + softi2c_gpio_highz(scl_port, scl_pin); +} + + +#if USE_SOFTI2C0 +struct i2c_errors softi2c0_errors; + +void softi2c0_hw_init(void) { + softi2c0.idle = softi2c_idle; + softi2c0.submit = softi2c_submit; + softi2c0.setbitrate = softi2c_setbitrate; + softi2c0.spin = softi2c_spin; + softi2c0.reg_addr = (void *) &softi2c0_device; + softi2c0.errors = &softi2c0_errors; + ZEROS_ERR_COUNTER(softi2c0_errors); + + softi2c0_device.periph = &softi2c0; + softi2c0_device.sda_port = SOFTI2C0_SDA_GPIO; + softi2c0_device.sda_pin = SOFTI2C0_SDA_PIN; + softi2c0_device.scl_port = SOFTI2C0_SCL_GPIO; + softi2c0_device.scl_pin = SOFTI2C0_SCL_PIN; + + /* Set up GPIO */ + softi2c_setup_gpio( + SOFTI2C0_SDA_GPIO, SOFTI2C0_SDA_PIN, + SOFTI2C0_SCL_GPIO, SOFTI2C0_SCL_PIN); +} +#endif /* USE_SOFTI2C0 */ + +#if USE_SOFTI2C1 +struct i2c_errors softi2c1_errors; + +void softi2c1_hw_init(void) { + softi2c1.idle = softi2c_idle; + softi2c1.submit = softi2c_submit; + softi2c1.setbitrate = softi2c_setbitrate; + softi2c1.spin = softi2c_spin; + softi2c1.reg_addr = (void *) &softi2c1_device; + softi2c1.errors = &softi2c1_errors; + ZEROS_ERR_COUNTER(softi2c1_errors); + + softi2c1_device.periph = &softi2c1; + softi2c1_device.sda_port = SOFTI2C1_SDA_GPIO; + softi2c1_device.sda_pin = SOFTI2C1_SDA_PIN; + softi2c1_device.scl_port = SOFTI2C1_SCL_GPIO; + softi2c1_device.scl_pin = SOFTI2C1_SCL_PIN; + + /* Set up GPIO */ + softi2c_setup_gpio( + SOFTI2C1_SDA_GPIO, SOFTI2C1_SDA_PIN, + SOFTI2C1_SCL_GPIO, SOFTI2C1_SCL_PIN); +} +#endif /* USE_SOFTI2C1 */ + + +/* + * I2C implementation + */ +// Some notes about timing: +// The I2C standard lists timing requirements in the order of microseconds. This +// is difficult to explicitly implement for two reasons: +// - The event loop spins at a ~20 us period. +// - The sys time clock has a resolution of ~200 us. (Can be set higher, but +// can cause lockups when set to ~2us or lower.) +// This module handles timing by returning to the event loop at every pause. +// The event loop runs slow enough to satisfy all I2C standard mode minimum +// timings. Because the event loop takes longer than the minimum timing, the +// maximum bitrate is reduced. However, *software*-i2c should not be used for +// high-bitrate applications anyway. +// (Experimental code where usleep was used for in-bit timing had only little +// performance benefit (~20kHz vs ~15kHz). Because sleeping in the event loop +// can have other negative consequences, this option was removed.) +// Pauses *between* bits also use the time between event calls as a delay. As a +// result, the bitrate is an integer fraction of the event rate and cannot be +// set exactly. The bitrate can be coarsely adjusted per softi2c device by +// setting SOFTI2CX_DIVIDER. + + +// Bit read/write functions +// Should be called continuously from event function. +// Return true upon completion. +// The bit functions start by setting SCL low and end at the last transition +// before SCL is set low again. The time between event calls is used to space +// this last transition and the new SCL low edge, thereby setting the bus +// frequency. + +static bool softi2c_write_start(struct softi2c_device *d) { + softi2c_gpio_drive_low(d->sda_port, d->sda_pin); + return true; // > T_HD_STA_MIN +} + +// Note: write_bit may also be used to write the ACK bit (T_VD_ACK == T_VD_DAT) +static bool softi2c_write_bit(struct softi2c_device *d, bool bit) { + switch (d->bit_state) { + case 0: + // Start of bit + softi2c_gpio_drive_low(d->scl_port, d->scl_pin); + d->bit_state++; + return false; + case 1: + // After SCL fall time and data hold time + if (bit) { + softi2c_gpio_highz(d->sda_port, d->sda_pin); + } else { + softi2c_gpio_drive_low(d->sda_port, d->sda_pin); + } + d->bit_state++; + return false; + case 2: + // After SDA rise(/fall) and data set-up time + softi2c_gpio_highz(d->scl_port, d->scl_pin); + d->bit_state++; + /* Falls through. */ + case 3: + if (!gpio_get(d->scl_port, d->scl_pin)) return false; + // After SCL rise time and confirmed high (clock stretching) + d->bit_state = 0; + return true; // > T_HIGH_MIN + default: return false; + } +} + +// Note: read_bit may also be used to read the ACK bit (T_VD_ACK == T_VD_DAT) +static bool softi2c_read_bit(struct softi2c_device *d, bool *bit) { + switch (d->bit_state) { + case 0: + // Start of bit + softi2c_gpio_drive_low(d->scl_port, d->scl_pin); + d->bit_state++; + return false; + case 1: + // After SCL fall time and data hold time + softi2c_gpio_highz(d->sda_port, d->sda_pin); // SDA may be driven low by slave. + // After SCL(!) fall time and minimum low time (== T_HD_DAT_MIN) + softi2c_gpio_highz(d->scl_port, d->scl_pin); + d->bit_state++; + /* Falls through. */ + case 2: + if (!gpio_get(d->scl_port, d->scl_pin)) return false; + // After SCL rise time and confirmed high (clock stretching) + *bit = softi2c_gpio_read(d->sda_port, d->sda_pin); + d->bit_state = 0; + return true; // > T_HIGH_MIN + default: return false; + } +} + +static bool softi2c_write_restart(struct softi2c_device *d) { + switch (d->bit_state) { + case 0: + // Start of bit + softi2c_gpio_drive_low(d->scl_port, d->scl_pin); + d->bit_state++; + return false; + case 1: + // After SCL fall time and data hold time + softi2c_gpio_highz(d->sda_port, d->sda_pin); + d->bit_state++; + return false; + case 2: + // After SDA rise time and data set-up time + softi2c_gpio_highz(d->scl_port, d->scl_pin); + d->bit_state++; + /* Falls through. */ + case 3: + if (!gpio_get(d->scl_port, d->scl_pin)) return false; + // After SCL rise time and confirmed high (clock stretching) + d->bit_state++; + return false; + case 4: + // After restart set-up time + softi2c_gpio_drive_low(d->sda_port, d->sda_pin); + d->bit_state = 0; + return true; // > T_HD_STA_MIN + default: return false; + } +} + +static bool softi2c_write_stop(struct softi2c_device *d) { + switch (d->bit_state) { + case 0: + // Start of bit + softi2c_gpio_drive_low(d->scl_port, d->scl_pin); + d->bit_state++; + return false; + case 1: + // After SCL fall time and data hold time + softi2c_gpio_drive_low(d->sda_port, d->sda_pin); + d->bit_state++; + return false; + case 2: + // After SDA fall time and data set-up time + softi2c_gpio_highz(d->scl_port, d->scl_pin); + d->bit_state++; + /* Falls through. */ + case 3: + if (!gpio_get(d->scl_port, d->scl_pin)) return false; + // After SCL rise time and confirmed high (clock stretching) + d->bit_state++; + return false; + case 4: + // After stop set-up time + softi2c_gpio_highz(d->sda_port, d->sda_pin); + d->bit_state = 0; + return true; // > T_BUF_MIN + default: return false; + } +} + +// Byte read/write functions +// Should be called continuously from event function. +// Return true upon completion. + +// write_byte: +// starts with SCL <= low for first bit (start bit is not included!) +// ends at SCL allowed low +static bool softi2c_write_byte(struct softi2c_device *d, uint8_t byte, bool *ack) { + bool bit; + switch (d->byte_state) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // Write bit + bit = byte & (0x80 >> d->byte_state); // MSB first + if (!softi2c_write_bit(d, bit)) return false; // Write bit in progress + d->byte_state++; + return false; + case 8: + // Read ACK + if (!softi2c_read_bit(d, ack)) return false; // Read bit in progress + *ack = !*ack; // Inverted, low = acknowledge + d->byte_state = 0; + return true; + default: return false; + } +} + +// read_byte: +// starts with SCL <= low for first bit (start bit is not included!) +// ends at SCL allowed low +// Note: ack should be false when reading last byte (UM10204 3.1.10) +static bool softi2c_read_byte(struct softi2c_device *d, uint8_t *byte, bool ack) { + bool bit; + switch (d->byte_state) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // Read bit + if (!softi2c_read_bit(d, &bit)) return false; // Read bit in progress + if (bit) { + *byte |= (0x80 >> d->byte_state); // MSB first + } else { + *byte &= ~(0x80 >> d->byte_state); // MSB first + } + d->byte_state++; + return false; + case 8: + // Write ACK + // Note: inverted logic, low = acknowledge. + if (!softi2c_write_bit(d, !ack)) return false; // Write bit in progress + d->byte_state = 0; + return true; + default: return false; + } +} + +// Transaction handling +// Should be called continously from event function. +// Returns true upon completion. +static bool softi2c_process_transaction(struct softi2c_device *d, struct i2c_transaction *t) { + uint8_t byte; + bool ack; + switch (d->periph->status) { + case I2CIdle: + // Start of transaction + t->status = I2CTransRunning; + d->periph->status = I2CStartRequested; + d->periph->idx_buf = 0; + return false; + + case I2CStartRequested: + // Send start bit + if (!softi2c_write_start(d)) return false; + // Start bit sent + if (t->type == I2CTransRx) { + d->periph->status = I2CAddrRdSent; + } else { + d->periph->status = I2CAddrWrSent; + } + return false; + + case I2CAddrWrSent: + // Send write address + if (!softi2c_write_byte(d, t->slave_addr, &ack)) return false; + // Write address sent + if (!ack) { + d->periph->errors->ack_fail_cnt++; + t->status = I2CTransFailed; + d->periph->status = I2CStopRequested; + return false; + } + d->periph->status = I2CSendingByte; + return false; + + case I2CSendingByte: + // Check remaining bytes + if (d->periph->idx_buf >= t->len_w) { + d->periph->idx_buf = 0; + if (t->type == I2CTransTxRx) { + d->periph->status = I2CRestartRequested; + } else { + t->status = I2CTransSuccess; + d->periph->status = I2CStopRequested; + } + return false; + } + // Send byte + if (!softi2c_write_byte(d, t->buf[d->periph->idx_buf], &ack)) return false; + // Byte sent + if (!ack) { + d->periph->errors->ack_fail_cnt++; + t->status = I2CTransFailed; + d->periph->status = I2CStopRequested; + return true; + } + d->periph->idx_buf++; + return false; + + case I2CRestartRequested: + // Send restart bit + if (!softi2c_write_restart(d)) return false; + // Restart bit sent + d->periph->status = I2CAddrRdSent; + return false; + + case I2CAddrRdSent: + // Send read address + byte = t->slave_addr | 0x01; + if (!softi2c_write_byte(d, byte, &ack)) return false; + // Read address sent + if (!ack) { + d->periph->errors->ack_fail_cnt++; + t->status = I2CTransFailed; + d->periph->status = I2CStopRequested; + return true; + } + d->periph->status = I2CReadingByte; + return false; + + case I2CReadingByte: + // Check remaining bytes + if (d->periph->idx_buf >= t->len_r - 1) { + d->periph->status = I2CReadingLastByte; + return false; + } + // Read byte + if (!softi2c_read_byte(d, (uint8_t *) &(t->buf[d->periph->idx_buf]), true)) return false; + // Byte read + d->periph->idx_buf++; + return false; + + case I2CReadingLastByte: + // Check remaining bytes + if (d->periph->idx_buf >= t->len_r) { + t->status = I2CTransSuccess; + d->periph->idx_buf = 0; + d->periph->status = I2CStopRequested; + return false; + } + // Read last byte (no ACK!) + if (!softi2c_read_byte(d, (uint8_t *) &(t->buf[d->periph->idx_buf]), false)) return false; + // Byte read + d->periph->idx_buf++; + return false; + + case I2CStopRequested: + // Send stop bit + if (!softi2c_write_stop(d)) return false; + // Stop bit sent + d->periph->status = I2CIdle; + return true; + + default: + // Should never happen, something went wrong + t->status = I2CTransFailed; + d->periph->status = I2CIdle; + return true; + } +} + +// Per-device event function +static void softi2c_device_event(struct softi2c_device *d) __attribute__((unused)); + +static void softi2c_device_event(struct softi2c_device *d) { + struct i2c_periph *p = d->periph; + if (p->trans_insert_idx != p->trans_extract_idx) { + // Transaction(s) in queue + struct i2c_transaction *t = p->trans[p->trans_extract_idx]; + if (softi2c_process_transaction(d, t)) { + // Transaction finished + p->trans_extract_idx = (p->trans_extract_idx + 1) % I2C_TRANSACTION_QUEUE_LEN; + } + } +} + + +/* + * Paparazzi functions + */ +void softi2c_event(void) { +#if USE_SOFTI2C0 + static int softi2c0_cnt = 1; + if (softi2c0_cnt == SOFTI2C0_DIVIDER) { + softi2c_device_event(&softi2c0_device); + softi2c0_cnt = 1; + } else { + softi2c0_cnt++; + } +#endif +#if USE_SOFTI2C1 + static int softi2c1_cnt = 1; + if (softi2c1_cnt == SOFTI2C1_DIVIDER) { + softi2c_device_event(&softi2c1_device); + softi2c1_cnt = 1; + } else { + softi2c1_cnt++; + } +#endif +} + +static void softi2c_spin(struct i2c_periph *p) { + softi2c_device_event((struct softi2c_device *) p->reg_addr); + sys_time_usleep(5); // For I2C timing. +} + +static bool softi2c_idle(struct i2c_periph *p) { + return (p->status == I2CIdle) && (p->trans_insert_idx == p->trans_extract_idx); +} + +static bool softi2c_submit(struct i2c_periph *p, struct i2c_transaction *t) { + uint8_t next_idx = (p->trans_insert_idx + 1) % I2C_TRANSACTION_QUEUE_LEN; + if (next_idx == p->trans_extract_idx) { + // queue full + p->errors->queue_full_cnt++; + t->status = I2CTransFailed; + return false; + } + t->status = I2CTransPending; + /* put transaction in queue */ + p->trans[p->trans_insert_idx] = t; + p->trans_insert_idx = next_idx; + return true; +} + +static void softi2c_setbitrate(struct i2c_periph *p __attribute__((unused)), int bitrate __attribute__((unused))) { + /* Do nothing */ +} diff --git a/sw/airborne/mcu_periph/softi2c.h b/sw/airborne/mcu_periph/softi2c.h new file mode 100644 index 0000000000..f4a832c274 --- /dev/null +++ b/sw/airborne/mcu_periph/softi2c.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 Tom van Dijk + * + * This file is part of paparazzi. + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/** + * @file mcu_periph/softi2c.h + * Platform-independent software I2C implementation. + * Can be used transparently in place of the hardware I2C in i2c.h. + */ + +#ifndef MCU_PERIPH_SOFTI2C_H +#define MCU_PERIPH_SOFTI2C_H + +#include "mcu_periph/i2c.h" + + +void softi2c_event(void); + + +#if USE_SOFTI2C0 +extern void softi2c0_hw_init(void); +#endif /* USE_SOFTI2C0 */ + +#if USE_SOFTI2C1 +extern void softi2c1_hw_init(void); +#endif /* USE_SOFTI2C1 */ + + +#endif // MCU_PERIPH_SOFTI2C_H