Add OpenMV gpio and i2c abstraction layer.

This commit is contained in:
iabdalkader 2021-01-05 00:07:45 +02:00
parent 3813fed939
commit e122e4e82d
5 changed files with 131 additions and 36 deletions

View File

@ -12,9 +12,18 @@
#define __CAMBUS_H__
#include <stdint.h>
#include <stdbool.h>
#include "cambus_struct.h"
#include "omv_portconfig.h"
// Transfer speeds
typedef enum _cambus_speed {
CAMBUS_SPEED_STANDARD = (0U),
CAMBUS_SPEED_FULL = (1U),
CAMBUS_SPEED_FAST = (2U),
CAMBUS_SPEED_MAX = (3U)
} cambus_speed_t;
// For use with read/write_bytes
enum cambus_xfer_flags {
typedef enum cambus_xfer_flags {
// Stop condition after the transfer.
// Normal transfer with start condition, address, data and stop condition.
CAMBUS_XFER_NO_FLAGS = (0<<0),
@ -24,7 +33,17 @@ enum cambus_xfer_flags {
// No stop condition after the transfer.
// This flag allows chaining multiple writes or reads with the same direction.
CAMBUS_XFER_SUSPEND = (1<<1),
};
} cambus_xfer_flags_t;
typedef struct _cambus {
uint32_t id;
uint32_t speed;
uint32_t initialized;
omv_gpio_t scl_pin;
omv_gpio_t sda_pin;
omv_cambus_t i2c;
} cambus_t;
int cambus_init(cambus_t *bus, uint32_t bus_id, uint32_t speed);
int cambus_deinit(cambus_t *bus);
int cambus_scan(cambus_t *bus);

View File

@ -22,22 +22,33 @@
int cambus_init(cambus_t *bus, uint32_t bus_id, uint32_t speed)
{
bus->id = bus_id;
bus->speed = speed;
bus->initialized = false;
switch (speed) {
case CAMBUS_SPEED_STANDARD:
bus->speed = TWI_FREQUENCY_FREQUENCY_K100; ///< 100 kbps
break;
case CAMBUS_SPEED_FULL:
bus->speed = TWI_FREQUENCY_FREQUENCY_K250; ///< 250 kbps
break;
case CAMBUS_SPEED_FAST:
bus->speed = TWI_FREQUENCY_FREQUENCY_K400; ///< 400 kbps
break;
default:
return -1;
}
switch (bus_id) {
case 0: {
bus->scl_pin = TWI0_SCL_PIN;
bus->sda_pin = TWI0_SDA_PIN;
nrfx_twi_t _twi = NRFX_TWI_INSTANCE(0);
memcpy(&bus->twi, &_twi, sizeof(nrfx_twi_t));
bus->i2c = (nrfx_twi_t) NRFX_TWI_INSTANCE(0);
break;
}
case 1: {
bus->scl_pin = TWI1_SCL_PIN;
bus->sda_pin = TWI1_SDA_PIN;
nrfx_twi_t _twi = NRFX_TWI_INSTANCE(1);
memcpy(&bus->twi, &_twi, sizeof(nrfx_twi_t));
bus->i2c = (nrfx_twi_t) NRFX_TWI_INSTANCE(1);
break;
}
default:
@ -47,17 +58,17 @@ int cambus_init(cambus_t *bus, uint32_t bus_id, uint32_t speed)
nrfx_twi_config_t config = {
.scl = bus->scl_pin,
.sda = bus->sda_pin,
.frequency = speed,
.frequency = bus->speed,
.interrupt_priority = 4,
.hold_bus_uninit = false
};
if (nrfx_twi_init(&bus->twi, &config, NULL, NULL) != NRFX_SUCCESS) {
if (nrfx_twi_init(&bus->i2c, &config, NULL, NULL) != NRFX_SUCCESS) {
return -1;
}
// This bus needs to be enabled for suspended transfers.
nrfx_twi_enable(&bus->twi);
nrfx_twi_enable(&bus->i2c);
bus->initialized = true;
return 0;
@ -66,8 +77,8 @@ int cambus_init(cambus_t *bus, uint32_t bus_id, uint32_t speed)
int cambus_deinit(cambus_t *bus)
{
if (bus->initialized) {
nrfx_twi_disable(&bus->twi);
nrfx_twi_uninit(&bus->twi);
nrfx_twi_disable(&bus->i2c);
nrfx_twi_uninit(&bus->i2c);
bus->initialized = false;
}
return 0;
@ -93,7 +104,7 @@ int cambus_read_bytes(cambus_t *bus, uint8_t slv_addr, uint8_t *buf, int len, ui
}
nrfx_twi_xfer_desc_t desc = NRFX_TWI_XFER_DESC_RX(slv_addr, buf, len);
if (nrfx_twi_xfer(&bus->twi, &desc, xfer_flags) != NRFX_SUCCESS) {
if (nrfx_twi_xfer(&bus->i2c, &desc, xfer_flags) != NRFX_SUCCESS) {
ret = -1;
}
return ret;
@ -111,7 +122,7 @@ int cambus_write_bytes(cambus_t *bus, uint8_t slv_addr, uint8_t *buf, int len, u
}
nrfx_twi_xfer_desc_t desc = NRFX_TWI_XFER_DESC_TX(slv_addr, buf, len);
if (nrfx_twi_xfer(&bus->twi, &desc, xfer_flags) != NRFX_SUCCESS) {
if (nrfx_twi_xfer(&bus->i2c, &desc, xfer_flags) != NRFX_SUCCESS) {
ret = -1;
}
return ret;

View File

@ -0,0 +1,21 @@
/*
* This file is part of the OpenMV project.
*
* Copyright (c) 2013-2019 Ibrahim Abdelkader <iabdalkader@openmv.io>
* Copyright (c) 2013-2019 Kwabena W. Agyeman <kwagyeman@openmv.io>
*
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OpenMV nRF port abstraction layer.
*/
#ifndef __OMV_PORTCONFIG_H__
#define __OMV_PORTCONFIG_H__
#include "nrfx_twi.h"
// omv_gpio_t definition
typedef uint32_t omv_gpio_t;
// cambus/i2c definition
typedef nrfx_twi_t omv_cambus_t;
#endif // __OMV_PORTCONFIG_H__

View File

@ -61,11 +61,25 @@ void I2C4_ER_IRQHandler(void) { HAL_I2C_ER_IRQHandler(&I2CHandle4); }
#endif
#endif // I2C4
static const uint32_t cambus_timing[CAMBUS_SPEED_MAX] = {
#if defined(STM32F4)
100000U, 400000U, 400000U,
#elif defined(STM32F7)
0x1090699B, 0x70330309, 0x50100103,
#elif defined(STM32H7)
0x20D09DE7, 0x40900C22, 0x4030040B,
#else
#error "no I2C timings for this MCU"
#endif
};
int cambus_init(cambus_t *bus, uint32_t bus_id, uint32_t speed)
{
bus->id = bus_id;
bus->speed = speed;
bus->i2c = NULL;
bus->initialized = false;
bus->scl_pin = (omv_gpio_t) {0, 0};
bus->sda_pin = (omv_gpio_t) {0, 0};
switch (bus_id) {
#if defined(I2C1)
@ -100,27 +114,27 @@ int cambus_init(cambus_t *bus, uint32_t bus_id, uint32_t speed)
return -1;
}
if (speed < 0 || speed >= CAMBUS_SPEED_MAX) {
return -1;
} else {
bus->speed = cambus_timing[speed];
}
// Our code only knows about these two I2Cs instances.
if (bus->i2c->Instance == FIR_I2C) {
bus->port = FIR_I2C_PORT;
bus->scl_pin = FIR_I2C_SCL_PIN;
bus->sda_pin = FIR_I2C_SDA_PIN;
bus->scl_pin = (omv_gpio_t) {FIR_I2C_SCL_PIN, FIR_I2C_PORT};
bus->sda_pin = (omv_gpio_t) {FIR_I2C_SDA_PIN, FIR_I2C_PORT};
} else if (bus->i2c->Instance == ISC_I2C) {
bus->port = ISC_I2C_PORT;
bus->scl_pin = ISC_I2C_SCL_PIN;
bus->sda_pin = ISC_I2C_SDA_PIN;
} else {
bus->port = NULL;
bus->scl_pin = 0;
bus->sda_pin = 0;
bus->scl_pin = (omv_gpio_t) {ISC_I2C_SCL_PIN, ISC_I2C_PORT};
bus->sda_pin = (omv_gpio_t) {ISC_I2C_SDA_PIN, ISC_I2C_PORT};
}
// Configure the I2C handle
bus->i2c->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
#if !defined(STM32F4)
bus->i2c->Init.Timing = speed;
bus->i2c->Init.Timing = bus->speed;
#else
bus->i2c->Init.ClockSpeed = speed;
bus->i2c->Init.ClockSpeed = bus->speed;
bus->i2c->Init.DutyCycle = I2C_DUTYCYCLE_2;
#endif
bus->i2c->Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
@ -135,7 +149,8 @@ int cambus_init(cambus_t *bus, uint32_t bus_id, uint32_t speed)
HAL_I2C_DeInit(bus->i2c);
if (HAL_I2C_Init(bus->i2c) != HAL_OK) {
bus->i2c = NULL;
bus->port = NULL;
bus->scl_pin = (omv_gpio_t) {0, 0};
bus->sda_pin = (omv_gpio_t) {0, 0};
return -1;
}
@ -164,6 +179,8 @@ int cambus_init(cambus_t *bus, uint32_t bus_id, uint32_t speed)
#endif
}
}
bus->initialized = true;
return 0;
}
@ -229,12 +246,14 @@ static int cambus_wait_timeout(cambus_t *bus, uint32_t timeout)
int cambus_deinit(cambus_t *bus)
{
if (bus->i2c && bus->i2c->Instance) {
if (bus->initialized) {
HAL_I2C_DeInit(bus->i2c);
bus->i2c->Instance = NULL;
}
bus->i2c = NULL;
bus->port = NULL;
bus->scl_pin = (omv_gpio_t) {0, 0};
bus->sda_pin = (omv_gpio_t) {0, 0};
bus->initialized = false;
return 0;
}
@ -391,20 +410,20 @@ int cambus_write_bytes(cambus_t *bus, uint8_t slv_addr, uint8_t *buf, int len, u
int cambus_pulse_scl(cambus_t *bus)
{
if (bus->port) {
if (bus->initialized && bus->scl_pin.port) {
// Configure SCL as GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pin = bus->scl_pin;
HAL_GPIO_Init(bus->port, &GPIO_InitStructure);
GPIO_InitStructure.Pin = bus->scl_pin.pin;
HAL_GPIO_Init(bus->scl_pin.port, &GPIO_InitStructure);
// Pulse SCL to recover stuck device.
for (int i=0; i<10000; i++) {
HAL_GPIO_WritePin(bus->port, bus->scl_pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(bus->scl_pin.port, bus->scl_pin.pin, GPIO_PIN_SET);
mp_hal_delay_us(10);
HAL_GPIO_WritePin(bus->port, bus->scl_pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(bus->scl_pin.port, bus->scl_pin.pin, GPIO_PIN_RESET);
mp_hal_delay_us(10);
}

View File

@ -0,0 +1,25 @@
/*
* This file is part of the OpenMV project.
*
* Copyright (c) 2013-2019 Ibrahim Abdelkader <iabdalkader@openmv.io>
* Copyright (c) 2013-2019 Kwabena W. Agyeman <kwagyeman@openmv.io>
*
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OpenMV STM32 port abstraction layer.
*/
#ifndef __OMV_PORTCONFIG_H__
#define __OMV_PORTCONFIG_H__
#include STM32_HAL_H
// omv_gpio_t definition
typedef struct _omv_gpio {
uint32_t pin;
GPIO_TypeDef *port;
} omv_gpio_t;
// cambus/i2c definition
// This pointer will be set to its respective I2C handle which is defined in MicroPython along with
// IRQ handlers, if this I2C is enabled in Micropython, or defined and handled in stm32fxxx_hal_msp.c.
typedef I2C_HandleTypeDef *omv_cambus_t;
#endif // __OMV_PORTCONFIG_H__