521 lines
16 KiB
C++
521 lines
16 KiB
C++
/**
|
|
*
|
|
* @license MIT License
|
|
*
|
|
* Copyright (c) 2022 lewis he
|
|
*
|
|
* 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.
|
|
*
|
|
* @file XPowersCommon.h
|
|
* @author Lewis He (lewishe@outlook.com)
|
|
* @date 2022-05-07
|
|
*
|
|
*/
|
|
|
|
|
|
#pragma once
|
|
|
|
#include <stdint.h>
|
|
|
|
#if defined(ARDUINO)
|
|
#include <Wire.h>
|
|
#elif defined(ESP_PLATFORM)
|
|
#include <cstring>
|
|
#include "esp_log.h"
|
|
#include "esp_err.h"
|
|
#include "esp_idf_version.h"
|
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0)) && defined(CONFIG_XPOWERS_ESP_IDF_NEW_API)
|
|
#include "driver/i2c_master.h"
|
|
#else
|
|
#include "driver/i2c.h"
|
|
#define XPOWERSLIB_I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
|
|
#define XPOWERSLIB_I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
|
|
#define XPOWERSLIB_I2C_MASTER_TIMEOUT_MS 1000
|
|
#endif //ESP_IDF_VERSION
|
|
#endif //defined(ARDUINO)
|
|
|
|
#define XPOWERSLIB_I2C_MASTER_SPEED 400000
|
|
|
|
|
|
#ifdef _BV
|
|
#undef _BV
|
|
#endif
|
|
#define _BV(b) (1ULL << (uint64_t)(b))
|
|
|
|
|
|
#ifndef constrain
|
|
#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
|
|
#endif
|
|
|
|
|
|
#define XPOWERS_ATTR_NOT_IMPLEMENTED __attribute__((error("Not implemented")))
|
|
#define IS_BIT_SET(val,mask) (((val)&(mask)) == (mask))
|
|
|
|
#if !defined(ARDUINO)
|
|
#ifdef linux
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#define log_e(__info, ...) printf("error :" __info,##__VA_ARGS__)
|
|
#define log_i(__info, ...) printf("info :" __info,##__VA_ARGS__)
|
|
#define log_d(__info, ...) printf("debug :" __info,##__VA_ARGS__)
|
|
#elif defined(ESP_PLATFORM)
|
|
#define log_e(__fmt, ...) ESP_LOGE("XPowers", __fmt, ##__VA_ARGS__);
|
|
#define log_i(__fmt, ...) ESP_LOGI("XPowers", __fmt, ##__VA_ARGS__);
|
|
#define log_d(__fmt, ...) ESP_LOGD("XPowers", __fmt, ##__VA_ARGS__);
|
|
#else
|
|
#define log_e(...)
|
|
#define log_i(...)
|
|
#define log_d(...)
|
|
#endif //linux
|
|
|
|
#define LOW 0x0
|
|
#define HIGH 0x1
|
|
|
|
//GPIO FUNCTIONS
|
|
#define INPUT 0x01
|
|
#define OUTPUT 0x03
|
|
#define PULLUP 0x04
|
|
#define INPUT_PULLUP 0x05
|
|
#define PULLDOWN 0x08
|
|
#define INPUT_PULLDOWN 0x09
|
|
|
|
#define RISING 0x01
|
|
#define FALLING 0x02
|
|
#endif //!defined(ARDUINO)
|
|
|
|
#if defined(ARDUINO_ARCH_MBED) || defined(ARDUINO_ARCH_ZEPHYR)
|
|
#define log_e(...)
|
|
#define log_i(...)
|
|
#define log_d(...)
|
|
#endif
|
|
|
|
#ifndef ESP32
|
|
#ifdef LOG_FILE_LINE_INFO
|
|
#undef LOG_FILE_LINE_INFO
|
|
#endif
|
|
#define LOG_FILE_LINE_INFO __FILE__, __LINE__
|
|
#ifndef log_e
|
|
#define log_e(fmt, ...) Serial.printf("[E][%s:%d] " fmt "\n", LOG_FILE_LINE_INFO, ##__VA_ARGS__)
|
|
#endif
|
|
#ifndef log_i
|
|
#define log_i(fmt, ...) Serial.printf("[I][%s:%d] " fmt "\n", LOG_FILE_LINE_INFO, ##__VA_ARGS__)
|
|
#endif
|
|
#ifndef log_d
|
|
#define log_d(fmt, ...) Serial.printf("[D][%s:%d] " fmt "\n", LOG_FILE_LINE_INFO, ##__VA_ARGS__)
|
|
#endif
|
|
#endif //ESP32
|
|
|
|
|
|
#if defined(NRF52840_XXAA) || defined(NRF52832_XXAA)
|
|
#ifndef SDA
|
|
#define SDA (0xFF)
|
|
#endif
|
|
|
|
#ifndef SCL
|
|
#define SCL (0xFF)
|
|
#endif
|
|
#endif
|
|
|
|
|
|
typedef int (*iic_fptr_t)(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint8_t len);
|
|
|
|
template <class chipType>
|
|
class XPowersCommon
|
|
{
|
|
|
|
public:
|
|
|
|
#if defined(ARDUINO)
|
|
bool begin(TwoWire &w, uint8_t addr, int sda, int scl)
|
|
{
|
|
if (__has_init)return thisChip().initImpl();
|
|
__has_init = true;
|
|
__sda = sda;
|
|
__scl = scl;
|
|
__wire = &w;
|
|
|
|
#if defined(NRF52840_XXAA) || defined(NRF52832_XXAA)
|
|
if (__sda != 0xFF && __scl != 0xFF) {
|
|
#if !defined(ARDUINO_ARCH_MBED) && !defined(ARDUINO_ARCH_ZEPHYR)
|
|
__wire->setPins(__sda, __scl);
|
|
#endif /* ARDUINO_ARCH_MBED || ZEPHYR */
|
|
}
|
|
__wire->begin();
|
|
#elif defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_STM32)
|
|
if (__sda != 0xFF && __scl != 0xFF) {
|
|
__wire->end();
|
|
__wire->setSDA(__sda);
|
|
__wire->setSCL(__scl);
|
|
}
|
|
__wire->begin();
|
|
#elif defined(ARDUINO_ARCH_ESP32)
|
|
__wire->begin(__sda, __scl);
|
|
#else
|
|
__wire->begin();
|
|
#endif
|
|
__addr = addr;
|
|
return thisChip().initImpl();
|
|
}
|
|
#elif defined(ESP_PLATFORM)
|
|
|
|
|
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0)) && defined(CONFIG_XPOWERS_ESP_IDF_NEW_API)
|
|
|
|
// * Using the new API of esp-idf 5.x, you need to pass the I2C BUS handle,
|
|
// * which is useful when the bus shares multiple devices.
|
|
bool begin(i2c_master_bus_handle_t i2c_dev_bus_handle, uint8_t addr)
|
|
{
|
|
log_i("Using ESP-IDF Driver interface.");
|
|
if (i2c_dev_bus_handle == NULL) return false;
|
|
if (__has_init)return thisChip().initImpl();
|
|
|
|
thisReadRegCallback = NULL;
|
|
thisWriteRegCallback = NULL;
|
|
|
|
/*
|
|
i2c_master_bus_config_t i2c_bus_config;
|
|
memset(&i2c_bus_config, 0, sizeof(i2c_bus_config));
|
|
i2c_bus_config.clk_source = I2C_CLK_SRC_DEFAULT;
|
|
i2c_bus_config.i2c_port = port_num;
|
|
i2c_bus_config.scl_io_num = (gpio_num_t)__scl;
|
|
i2c_bus_config.sda_io_num = (gpio_num_t)__sda;
|
|
i2c_bus_config.glitch_ignore_cnt = 7;
|
|
|
|
i2c_new_master_bus(&i2c_bus_config, &bus_handle);
|
|
*/
|
|
|
|
bus_handle = i2c_dev_bus_handle;
|
|
|
|
i2c_device_config_t i2c_dev_conf = {
|
|
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
|
.device_address = addr,
|
|
.scl_speed_hz = XPOWERSLIB_I2C_MASTER_SPEED,
|
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,3,0))
|
|
// New fields since esp-idf-v5.3-beta1
|
|
.scl_wait_us = 0,
|
|
.flags = {
|
|
. disable_ack_check = 0
|
|
}
|
|
#endif
|
|
};
|
|
|
|
if (ESP_OK != i2c_master_bus_add_device(bus_handle,
|
|
&i2c_dev_conf,
|
|
&__i2c_device)) {
|
|
return false;
|
|
}
|
|
|
|
__has_init = thisChip().initImpl();
|
|
|
|
if (!__has_init) {
|
|
// Initialization failed, delete device
|
|
i2c_master_bus_rm_device(__i2c_device);
|
|
}
|
|
return __has_init;
|
|
}
|
|
|
|
|
|
#else //ESP 4.X
|
|
|
|
|
|
bool begin(i2c_port_t port_num, uint8_t addr, int sda, int scl)
|
|
{
|
|
__i2c_num = port_num;
|
|
log_i("Using ESP-IDF Driver interface.");
|
|
if (__has_init)return thisChip().initImpl();
|
|
__sda = sda;
|
|
__scl = scl;
|
|
__addr = addr;
|
|
thisReadRegCallback = NULL;
|
|
thisWriteRegCallback = NULL;
|
|
|
|
i2c_config_t i2c_conf;
|
|
memset(&i2c_conf, 0, sizeof(i2c_conf));
|
|
i2c_conf.mode = I2C_MODE_MASTER;
|
|
i2c_conf.sda_io_num = sda;
|
|
i2c_conf.scl_io_num = scl;
|
|
i2c_conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
|
i2c_conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
|
i2c_conf.master.clk_speed = XPOWERSLIB_I2C_MASTER_SPEED;
|
|
|
|
/**
|
|
* @brief Without checking whether the initialization is successful,
|
|
* I2C may be initialized externally,
|
|
* so just make sure there is an initialization here.
|
|
*/
|
|
i2c_param_config(__i2c_num, &i2c_conf);
|
|
i2c_driver_install(__i2c_num,
|
|
i2c_conf.mode,
|
|
XPOWERSLIB_I2C_MASTER_RX_BUF_DISABLE,
|
|
XPOWERSLIB_I2C_MASTER_TX_BUF_DISABLE, 0);
|
|
__has_init = thisChip().initImpl();
|
|
return __has_init;
|
|
}
|
|
#endif //ESP 5.X
|
|
#endif //ESP_PLATFORM
|
|
|
|
bool begin(uint8_t addr, iic_fptr_t readRegCallback, iic_fptr_t writeRegCallback)
|
|
{
|
|
if (__has_init)return thisChip().initImpl();
|
|
__has_init = true;
|
|
thisReadRegCallback = readRegCallback;
|
|
thisWriteRegCallback = writeRegCallback;
|
|
__addr = addr;
|
|
return thisChip().initImpl();
|
|
}
|
|
|
|
int readRegister(uint8_t reg)
|
|
{
|
|
uint8_t val = 0;
|
|
return readRegister(reg, &val, 1) == -1 ? -1 : val;
|
|
}
|
|
|
|
int writeRegister(uint8_t reg, uint8_t val)
|
|
{
|
|
return writeRegister(reg, &val, 1);
|
|
}
|
|
|
|
int readRegister(uint8_t reg, uint8_t *buf, uint8_t length)
|
|
{
|
|
if (thisReadRegCallback) {
|
|
return thisReadRegCallback(__addr, reg, buf, length);
|
|
}
|
|
#if defined(ARDUINO)
|
|
if (__wire) {
|
|
__wire->beginTransmission(__addr);
|
|
__wire->write(reg);
|
|
if (__wire->endTransmission() != 0) {
|
|
return -1;
|
|
}
|
|
__wire->requestFrom(__addr, length);
|
|
return __wire->readBytes(buf, length) == length ? 0 : -1;
|
|
}
|
|
#elif defined(ESP_PLATFORM)
|
|
|
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0)) && defined(CONFIG_XPOWERS_ESP_IDF_NEW_API)
|
|
if (ESP_OK == i2c_master_transmit_receive(
|
|
__i2c_device,
|
|
(const uint8_t *)®,
|
|
1,
|
|
buf,
|
|
length,
|
|
-1)) {
|
|
return 0;
|
|
}
|
|
#else //ESP_IDF_VERSION
|
|
if (ESP_OK == i2c_master_write_read_device(__i2c_num,
|
|
__addr,
|
|
(uint8_t *)®,
|
|
1,
|
|
buf,
|
|
length,
|
|
XPOWERSLIB_I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS)) {
|
|
return 0;
|
|
}
|
|
#endif //ESP_IDF_VERSION
|
|
#endif //ESP_PLATFORM
|
|
return -1;
|
|
}
|
|
|
|
int writeRegister(uint8_t reg, uint8_t *buf, uint8_t length)
|
|
{
|
|
if (thisWriteRegCallback) {
|
|
return thisWriteRegCallback(__addr, reg, buf, length);
|
|
}
|
|
#if defined(ARDUINO)
|
|
if (__wire) {
|
|
__wire->beginTransmission(__addr);
|
|
__wire->write(reg);
|
|
__wire->write(buf, length);
|
|
return (__wire->endTransmission() == 0) ? 0 : -1;
|
|
}
|
|
return -1;
|
|
#elif defined(ESP_PLATFORM)
|
|
uint8_t *write_buffer = (uint8_t *)malloc(sizeof(uint8_t) * (length + 1));
|
|
if (!write_buffer) {
|
|
return -1;
|
|
}
|
|
write_buffer[0] = reg;
|
|
memcpy(write_buffer + 1, buf, length);
|
|
|
|
|
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0)) && defined(CONFIG_XPOWERS_ESP_IDF_NEW_API)
|
|
if (ESP_OK != i2c_master_transmit(
|
|
__i2c_device,
|
|
write_buffer,
|
|
length + 1,
|
|
-1)) {
|
|
free(write_buffer);
|
|
return -1;
|
|
}
|
|
#else //ESP_IDF_VERSION
|
|
if (ESP_OK != i2c_master_write_to_device(__i2c_num,
|
|
__addr,
|
|
write_buffer,
|
|
length + 1,
|
|
XPOWERSLIB_I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS)) {
|
|
free(write_buffer);
|
|
return -1;
|
|
}
|
|
#endif //ESP_IDF_VERSION
|
|
free(write_buffer);
|
|
return 0;
|
|
#endif //ESP_PLATFORM
|
|
}
|
|
|
|
|
|
bool inline clrRegisterBit(uint8_t registers, uint8_t bit)
|
|
{
|
|
int val = readRegister(registers);
|
|
if (val == -1) {
|
|
return false;
|
|
}
|
|
return writeRegister(registers, (val & (~_BV(bit)))) == 0;
|
|
}
|
|
|
|
bool inline setRegisterBit(uint8_t registers, uint8_t bit)
|
|
{
|
|
int val = readRegister(registers);
|
|
if (val == -1) {
|
|
return false;
|
|
}
|
|
return writeRegister(registers, (val | (_BV(bit)))) == 0;
|
|
}
|
|
|
|
bool inline getRegisterBit(uint8_t registers, uint8_t bit)
|
|
{
|
|
int val = readRegister(registers);
|
|
if (val == -1) {
|
|
return false;
|
|
}
|
|
return val & _BV(bit);
|
|
}
|
|
|
|
uint16_t inline readRegisterH8L4(uint8_t highReg, uint8_t lowReg)
|
|
{
|
|
int h8 = readRegister(highReg);
|
|
int l4 = readRegister(lowReg);
|
|
if (h8 == -1 || l4 == -1)return UINT16_MAX;
|
|
return (h8 << 4) | (l4 & 0x0F);
|
|
}
|
|
|
|
uint16_t inline readRegisterH8L5(uint8_t highReg, uint8_t lowReg)
|
|
{
|
|
int h8 = readRegister(highReg);
|
|
int l5 = readRegister(lowReg);
|
|
if (h8 == -1 || l5 == -1)return UINT16_MAX;
|
|
return (h8 << 5) | (l5 & 0x1F);
|
|
}
|
|
|
|
uint16_t inline readRegisterH6L8(uint8_t highReg, uint8_t lowReg)
|
|
{
|
|
int h6 = readRegister(highReg);
|
|
int l8 = readRegister(lowReg);
|
|
if (h6 == -1 || l8 == -1)return UINT16_MAX;
|
|
return ((h6 & 0x3F) << 8) | l8;
|
|
}
|
|
|
|
uint16_t inline readRegisterH5L8(uint8_t highReg, uint8_t lowReg)
|
|
{
|
|
int h5 = readRegister(highReg);
|
|
int l8 = readRegister(lowReg);
|
|
if (h5 == -1 || l8 == -1)return 0;
|
|
return ((h5 & 0x1F) << 8) | l8;
|
|
}
|
|
|
|
/*
|
|
* CRTP Helper
|
|
*/
|
|
protected:
|
|
|
|
bool begin()
|
|
{
|
|
#if defined(ARDUINO)
|
|
if (__has_init) return thisChip().initImpl();
|
|
__has_init = true;
|
|
if (__wire) {
|
|
log_i("SDA:%d SCL:%d", __sda, __scl);
|
|
#if defined(NRF52840_XXAA) || defined(NRF52832_XXAA)
|
|
if (__sda != 0xFF && __scl != 0xFF) {
|
|
#if !defined(ARDUINO_ARCH_MBED) && !defined(ARDUINO_ARCH_ZEPHYR)
|
|
__wire->setPins(__sda, __scl);
|
|
#endif /* ARDUINO_ARCH_MBED || ZEPHYR */
|
|
}
|
|
__wire->begin();
|
|
#elif defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_STM32)
|
|
if (__sda != 0xFF && __scl != 0xFF) {
|
|
__wire->end();
|
|
__wire->setSDA(__sda);
|
|
__wire->setSCL(__scl);
|
|
}
|
|
__wire->begin();
|
|
#elif defined(ARDUINO_ARCH_ESP32)
|
|
__wire->begin(__sda, __scl);
|
|
#else
|
|
__wire->begin();
|
|
#endif
|
|
}
|
|
#endif /*ARDUINO*/
|
|
return thisChip().initImpl();
|
|
}
|
|
|
|
void end()
|
|
{
|
|
#if defined(ARDUINO)
|
|
if (__wire) {
|
|
#if defined(ESP_IDF_VERSION)
|
|
#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(4,4,0)
|
|
__wire->end();
|
|
#endif /*ESP_IDF_VERSION*/
|
|
#endif /*ESP_IDF_VERSION*/
|
|
}
|
|
#endif /*ARDUINO*/
|
|
}
|
|
|
|
|
|
inline const chipType &thisChip() const
|
|
{
|
|
return static_cast<const chipType &>(*this);
|
|
}
|
|
|
|
inline chipType &thisChip()
|
|
{
|
|
return static_cast<chipType &>(*this);
|
|
}
|
|
|
|
protected:
|
|
bool __has_init = false;
|
|
#if defined(ARDUINO)
|
|
TwoWire *__wire = NULL;
|
|
#elif defined(ESP_PLATFORM)
|
|
i2c_port_t __i2c_num;
|
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0)) && defined(CONFIG_XPOWERS_ESP_IDF_NEW_API)
|
|
i2c_master_bus_handle_t bus_handle;
|
|
i2c_master_dev_handle_t __i2c_device;
|
|
#endif //ESP_IDF_VERSION
|
|
|
|
#endif //ESP_PLATFORM
|
|
int __sda = -1;
|
|
int __scl = -1;
|
|
uint8_t __addr = 0xFF;
|
|
iic_fptr_t thisReadRegCallback = NULL;
|
|
iic_fptr_t thisWriteRegCallback = NULL;
|
|
};
|