circuitpython/ports/silabs/common-hal/busio/I2C.c

213 lines
6.2 KiB
C

/*
* This file is part of Adafruit for EFR32 project
*
* The MIT License (MIT)
*
* Copyright 2023 Silicon Laboratories Inc. www.silabs.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "shared-bindings/busio/I2C.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "supervisor/shared/translate/translate.h"
#include "shared-bindings/microcontroller/Pin.h"
STATIC I2CSPM_Init_TypeDef i2cspm_init;
STATIC bool in_used = false;
STATIC bool never_reset = false;
// Reser I2C peripheral
void i2c_reset(void) {
if ((!never_reset) && in_used) {
I2C_Reset(DEFAULT_I2C_PERIPHERAL);
in_used = false;
}
}
// Construct I2C protocol, this function init i2c peripheral
void common_hal_busio_i2c_construct(busio_i2c_obj_t *self,
const mcu_pin_obj_t *scl,
const mcu_pin_obj_t *sda,
uint32_t frequency, uint32_t timeout) {
if ((scl != NULL) && (sda != NULL)) {
if (scl->function_list[ DEFAULT_I2C_PERIPHERAL == I2C1?
FN_I2C1_SCL : FN_I2C0_SCL] == 1 &&
scl->function_list[DEFAULT_I2C_PERIPHERAL == I2C1?
FN_I2C1_SDA : FN_I2C0_SDA] == 1) {
self->scl = scl;
self->sda = sda;
self->has_lock = false;
i2cspm_init.sclPort = self->scl->port;
i2cspm_init.sclPin = self->scl->number;
i2cspm_init.sdaPort = self->sda->port;
i2cspm_init.sdaPin = self->sda->number;
i2cspm_init.port = DEFAULT_I2C_PERIPHERAL;
i2cspm_init.i2cRefFreq = 0;
i2cspm_init.i2cMaxFreq = I2C_FREQ_STANDARD_MAX;
i2cspm_init.i2cClhr = i2cClockHLRStandard;
self->i2cspm = i2cspm_init.port;
I2CSPM_Init(&i2cspm_init);
common_hal_mcu_pin_claim(scl);
common_hal_mcu_pin_claim(sda);
in_used = true;
} else {
mp_raise_ValueError(translate("Hardware busy, try alternative pins"));
}
} else {
raise_ValueError_invalid_pins();
}
}
// Never reset I2C obj when reload
void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) {
never_reset = true;
common_hal_never_reset_pin(self->sda);
common_hal_never_reset_pin(self->scl);
}
// Check I2C status, deinited or not
bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) {
return self->sda == NULL;
}
// Deinit i2c obj, reset I2C pin
void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) {
if (common_hal_busio_i2c_deinited(self)) {
return;
}
I2C_Reset(self->i2cspm);
common_hal_reset_pin(self->sda);
common_hal_reset_pin(self->scl);
self->sda = NULL;
self->scl = NULL;
self->i2cspm = NULL;
in_used = false;
}
// Probe device in I2C bus
bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) {
I2C_TransferSeq_TypeDef seq;
I2C_TransferReturn_TypeDef ret;
uint8_t data = 0;
seq.addr = addr << 1;
seq.flags = I2C_FLAG_READ;
seq.buf[0].data = &data;
seq.buf[0].len = 1;
ret = I2CSPM_Transfer(self->i2cspm, &seq);
if (ret != i2cTransferDone) {
return false;
}
return true;
}
// Lock I2C bus
bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) {
bool grabbed_lock = false;
if (!self->has_lock) {
grabbed_lock = true;
self->has_lock = true;
}
return grabbed_lock;
}
// Check I2C lock status
bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) {
return self->has_lock;
}
// Unlock I2C bus
void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) {
self->has_lock = false;
}
// Write data to the device selected by address
uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr,
const uint8_t *data, size_t len) {
I2C_TransferSeq_TypeDef seq;
I2C_TransferReturn_TypeDef ret;
seq.addr = addr << 1;
seq.flags = I2C_FLAG_WRITE;
seq.buf[0].data = (uint8_t *)data;
seq.buf[0].len = len;
ret = I2CSPM_Transfer(self->i2cspm, &seq);
if (ret != i2cTransferDone) {
return MP_EIO;
}
return 0;
}
// Read into buffer from the device selected by address
uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self,
uint16_t addr,
uint8_t *data, size_t len) {
I2C_TransferSeq_TypeDef seq;
I2C_TransferReturn_TypeDef ret;
seq.addr = addr << 1;
seq.flags = I2C_FLAG_READ;
seq.buf[0].data = data;
seq.buf[0].len = len;
ret = I2CSPM_Transfer(self->i2cspm, &seq);
if (ret != i2cTransferDone) {
return MP_EIO;
}
return 0;
}
// Write the bytes from out_data to the device selected by address,
uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr,
uint8_t *out_data, size_t out_len,
uint8_t *in_data, size_t in_len) {
I2C_TransferSeq_TypeDef seq;
I2C_TransferReturn_TypeDef ret;
seq.addr = addr << 1;
seq.flags = I2C_FLAG_WRITE_READ;
// Select command to issue
seq.buf[0].data = out_data;
seq.buf[0].len = out_len;
// Select location/length of data to be read
seq.buf[1].data = in_data;
seq.buf[1].len = in_len;
ret = I2CSPM_Transfer(self->i2cspm, &seq);
if (ret != i2cTransferDone) {
return MP_EIO;
}
return 0;
}