mirror of https://github.com/ARMmbed/mbed-os.git
332 lines
11 KiB
C
332 lines
11 KiB
C
/* mbed Microcontroller Library
|
|
* Copyright (c) 2019, Arm Limited and affiliates.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "i2c_api.h"
|
|
#include "cyhal_i2c.h"
|
|
#include "cyhal_utils.h"
|
|
#include "cyhal_hwmgr.h"
|
|
#include "mbed_error.h"
|
|
#include "mbed_assert.h"
|
|
#include "mbed_critical.h"
|
|
|
|
#if DEVICE_I2C
|
|
|
|
#define CY_I2C_DEFAULT_TIMEOUT 200
|
|
|
|
#define CY_I2C_SLAVE_EVENT_IDLE 0
|
|
#define CY_I2C_SLAVE_EVENT_READ_ADDR 1
|
|
#define CY_I2C_SLAVE_EVENT_GENERAL_CALL 2
|
|
#define CY_I2C_SLAVE_EVENT_WRITE_ADDR 3
|
|
|
|
static inline struct i2c_s *cy_get_i2c(i2c_t *obj)
|
|
{
|
|
#if DEVICE_I2C_ASYNCH
|
|
return &(obj->i2c);
|
|
#else
|
|
return obj;
|
|
#endif
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
static uint32_t cy_i2c_convert_event(i2c_t *obj, cyhal_i2c_event_t event)
|
|
{
|
|
if (CYHAL_I2C_EVENT_NONE != (event & (CYHAL_I2C_SLAVE_ERR_EVENT | CYHAL_I2C_MASTER_ERR_EVENT))) {
|
|
event |= I2C_EVENT_ERROR;
|
|
}
|
|
if (CYHAL_I2C_EVENT_NONE != (event & (CYHAL_I2C_SLAVE_RD_CMPLT_EVENT | CYHAL_I2C_SLAVE_WR_CMPLT_EVENT | CYHAL_I2C_MASTER_RD_CMPLT_EVENT | CYHAL_I2C_MASTER_WR_CMPLT_EVENT)) && !i2c_active(obj)) {
|
|
event |= I2C_EVENT_TRANSFER_COMPLETE;
|
|
}
|
|
return event;
|
|
}
|
|
|
|
static void cy_i2c_event_handler(void *handler_arg, cyhal_i2c_event_t event)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c((i2c_t *)handler_arg);
|
|
switch (event) {
|
|
case CYHAL_I2C_SLAVE_READ_EVENT:
|
|
i2c->slave_event = CY_I2C_SLAVE_EVENT_READ_ADDR;
|
|
break;
|
|
case CYHAL_I2C_SLAVE_WRITE_EVENT:
|
|
i2c->slave_event = CY_I2C_SLAVE_EVENT_WRITE_ADDR;
|
|
break;
|
|
case CYHAL_I2C_SLAVE_ERR_EVENT: // fallthrough
|
|
case CYHAL_I2C_SLAVE_RD_CMPLT_EVENT: // fallthrough
|
|
case CYHAL_I2C_SLAVE_WR_CMPLT_EVENT:
|
|
i2c->slave_event = CY_I2C_SLAVE_EVENT_IDLE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef DEVICE_I2C_ASYNCH
|
|
i2c->async_event = cy_i2c_convert_event((i2c_t *)handler_arg, event);
|
|
void (*async_handler)(void) = i2c->async_handler;
|
|
if (NULL != async_handler && i2c->async_event != 0) {
|
|
(*async_handler)();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void i2c_init(i2c_t *obj, PinName sda, PinName scl)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
cy_rslt_t result = cyhal_i2c_init(&(i2c->hal_i2c), sda, scl, NULL);
|
|
if (result == CYHAL_HWMGR_RSLT_ERR_INUSE) {
|
|
// MBED I2C driver currently does not support free, so we will allow I2C to be reallocated.
|
|
// TODO: once the the I2C driver properly supports free, this need to be fixed so that clocks and pins are no longer leaked.
|
|
cyhal_hwmgr_free(&(i2c->hal_i2c.resource));
|
|
cyhal_resource_inst_t pin_rsc = cyhal_utils_get_gpio_resource(sda);
|
|
cyhal_hwmgr_free(&pin_rsc);
|
|
pin_rsc = cyhal_utils_get_gpio_resource(scl);
|
|
cyhal_hwmgr_free(&pin_rsc);
|
|
result = cyhal_i2c_init(&(i2c->hal_i2c), sda, scl, NULL);
|
|
}
|
|
if (CY_RSLT_SUCCESS != result) {
|
|
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_init");
|
|
}
|
|
i2c->cfg.is_slave = false;
|
|
i2c->cfg.address = 0;
|
|
i2c->cfg.frequencyhal_hz = 400000;
|
|
i2c->async_handler = NULL;
|
|
cyhal_i2c_register_callback(&(i2c->hal_i2c), &cy_i2c_event_handler, obj);
|
|
cyhal_i2c_enable_event(&(i2c->hal_i2c), (cyhal_i2c_event_t)(CYHAL_I2C_SLAVE_READ_EVENT | CYHAL_I2C_SLAVE_WRITE_EVENT | CYHAL_I2C_SLAVE_ERR_EVENT | CYHAL_I2C_SLAVE_RD_CMPLT_EVENT | CYHAL_I2C_SLAVE_WR_CMPLT_EVENT | CYHAL_I2C_MASTER_ERR_EVENT | CYHAL_I2C_MASTER_RD_CMPLT_EVENT | CYHAL_I2C_MASTER_WR_CMPLT_EVENT), CYHAL_ISR_PRIORITY_DEFAULT, true);
|
|
}
|
|
|
|
void i2c_frequency(i2c_t *obj, int hz)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
i2c->cfg.frequencyhal_hz = (uint32_t)hz;
|
|
if (CY_RSLT_SUCCESS != cyhal_i2c_configure(&(i2c->hal_i2c), &(i2c->cfg))) {
|
|
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_configure");
|
|
}
|
|
}
|
|
|
|
int i2c_start(i2c_t *obj)
|
|
{
|
|
/* start/stop is generated by i2c_read/i2c_write */
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
/* Clear state of address */
|
|
i2c->address_set = false;
|
|
return 0;
|
|
}
|
|
|
|
int i2c_stop(i2c_t *obj)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
/* Clear state of address */
|
|
i2c->address_set = false;
|
|
if (i2c->hal_i2c.context.state != CY_SCB_I2C_IDLE) {
|
|
return Cy_SCB_I2C_MasterSendStop(i2c->hal_i2c.base, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
if (CY_RSLT_SUCCESS != cyhal_i2c_master_read(&(i2c->hal_i2c), address >> 1, (uint8_t *)data, (uint16_t)length, CY_I2C_DEFAULT_TIMEOUT, (bool)stop)) {
|
|
return (int)I2C_ERROR_NO_SLAVE;
|
|
}
|
|
return length;
|
|
}
|
|
|
|
int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
if (CY_RSLT_SUCCESS != cyhal_i2c_master_write(&(i2c->hal_i2c), address >> 1, (const uint8_t *)data, (uint16_t)length, CY_I2C_DEFAULT_TIMEOUT, (bool)stop)) {
|
|
return (int)I2C_ERROR_NO_SLAVE;
|
|
}
|
|
// NOTE: HAL does not report how many bytes were actually sent in case of early NAK
|
|
return length;
|
|
}
|
|
|
|
void i2c_reset(i2c_t *obj)
|
|
{
|
|
i2c_stop(obj);
|
|
}
|
|
|
|
int i2c_byte_read(i2c_t *obj, int last)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
uint8_t value;
|
|
|
|
/* Slave device address is stored in object by first write operation */
|
|
if (i2c->address_set) {
|
|
/* Send slave device address */
|
|
/* Make sure if I2C transaction direction is 'Read' */
|
|
if (CY_SCB_I2C_SUCCESS != Cy_SCB_I2C_MasterSendStart(i2c->hal_i2c.base, i2c->address, CY_SCB_I2C_READ_XFER, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context))) {
|
|
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "i2c_send_start");
|
|
}
|
|
/* Clear state of address. It is not needed for next operation. */
|
|
i2c->address_set = false;
|
|
}
|
|
|
|
/* Read next byte */
|
|
if (CY_SCB_I2C_SUCCESS != Cy_SCB_I2C_MasterReadByte(i2c->hal_i2c.base, last != 0 ? CY_SCB_I2C_NAK : CY_SCB_I2C_ACK, &value, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context))) {
|
|
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "i2c_read_byte");
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
int i2c_byte_write(i2c_t *obj, int data)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
cy_en_scb_i2c_status_t status = CY_SCB_I2C_SUCCESS;
|
|
|
|
/* First byte should be address */
|
|
if (i2c->address_set) {
|
|
/* Verify if Master is ready for send slave address and send first data byte */
|
|
/* Make sure that I2C transaction direction is 'Write' */
|
|
if (i2c->hal_i2c.context.state == CY_SCB_I2C_IDLE) {
|
|
status = Cy_SCB_I2C_MasterSendStart(i2c->hal_i2c.base, i2c->address, CY_SCB_I2C_WRITE_XFER, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context));
|
|
}
|
|
|
|
if (status == CY_SCB_I2C_SUCCESS) {
|
|
status = Cy_SCB_I2C_MasterWriteByte(i2c->hal_i2c.base, (uint8_t)data, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context));
|
|
}
|
|
} else {
|
|
/* Store slave address and remember status for next byte read or write operation */
|
|
i2c->address = data >> 1;
|
|
i2c->address_set = true;
|
|
}
|
|
|
|
|
|
switch (status) {
|
|
case CY_SCB_I2C_MASTER_MANUAL_TIMEOUT:
|
|
return 2;
|
|
case CY_SCB_I2C_SUCCESS:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const PinMap *i2c_master_sda_pinmap(void)
|
|
{
|
|
return PinMap_I2C_SDA;
|
|
}
|
|
|
|
const PinMap *i2c_master_scl_pinmap(void)
|
|
{
|
|
return PinMap_I2C_SCL;
|
|
}
|
|
|
|
const PinMap *i2c_slave_sda_pinmap(void)
|
|
{
|
|
return PinMap_I2C_SDA;
|
|
}
|
|
|
|
const PinMap *i2c_slave_scl_pinmap(void)
|
|
{
|
|
return PinMap_I2C_SCL;
|
|
}
|
|
|
|
#if DEVICE_I2CSLAVE
|
|
|
|
void i2c_slave_mode(i2c_t *obj, int enable_slave)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
i2c->cfg.is_slave = (0 != enable_slave);
|
|
if (CY_RSLT_SUCCESS != cyhal_i2c_configure(&(i2c->hal_i2c), &(i2c->cfg))) {
|
|
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_configure");
|
|
}
|
|
}
|
|
|
|
int i2c_slave_receive(i2c_t *obj)
|
|
{
|
|
return cy_get_i2c(obj)->slave_event;
|
|
}
|
|
|
|
int i2c_slave_read(i2c_t *obj, char *data, int length)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
if (CY_RSLT_SUCCESS != cyhal_i2c_slave_config_read_buff(&(i2c->hal_i2c), (uint8_t *)data, (uint16_t)length)) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int i2c_slave_write(i2c_t *obj, const char *data, int length)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
if (CY_RSLT_SUCCESS != cyhal_i2c_slave_config_write_buff(&(i2c->hal_i2c), (const uint8_t *)data, (uint16_t)length)) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
i2c->cfg.address = address;
|
|
if (CY_RSLT_SUCCESS != cyhal_i2c_configure(&(i2c->hal_i2c), &(i2c->cfg))) {
|
|
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_configure");
|
|
}
|
|
Cy_SCB_I2C_SlaveSetAddressMask(i2c->hal_i2c.base, (uint8_t)mask);
|
|
}
|
|
|
|
#endif
|
|
|
|
#if DEVICE_I2C_ASYNCH
|
|
|
|
void i2c_transfer_asynch(i2c_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint32_t address, uint32_t stop, uint32_t handler, uint32_t event, DMAUsage hint)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
core_util_critical_section_enter();
|
|
i2c->async_rx_size = rx_length;
|
|
i2c->async_handler = (void (*)(void))handler;
|
|
core_util_critical_section_exit();
|
|
if (CY_RSLT_SUCCESS != cyhal_i2c_master_transfer_async(&(i2c->hal_i2c), address, tx, tx_length, rx, rx_length)) {
|
|
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_master_transfer_async");
|
|
}
|
|
}
|
|
|
|
uint32_t i2c_irq_handler_asynch(i2c_t *obj)
|
|
{
|
|
return cy_get_i2c(obj)->async_event;
|
|
}
|
|
|
|
uint8_t i2c_active(i2c_t *obj)
|
|
{
|
|
return cy_get_i2c(obj)->hal_i2c.pending != 0 ? 1 : 0;
|
|
}
|
|
|
|
void i2c_abort_asynch(i2c_t *obj)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
if (CY_RSLT_SUCCESS != cyhal_i2c_abort_async(&(i2c->hal_i2c))) {
|
|
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_abort_async");
|
|
}
|
|
}
|
|
|
|
void i2c_free(i2c_t *obj)
|
|
{
|
|
struct i2c_s *i2c = cy_get_i2c(obj);
|
|
cyhal_i2c_free(&i2c->hal_i2c);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|