mirror of https://github.com/esphome/esphome.git
196 lines
7.9 KiB
C++
196 lines
7.9 KiB
C++
|
#include "at581x.h"
|
||
|
#include "esphome/core/log.h"
|
||
|
|
||
|
/* Select gain for AT581X (3dB per step for level1, 6dB per step for level 2), high value = small gain. (p12) */
|
||
|
const uint8_t GAIN_ADDR_TABLE[] = {0x5c, 0x63};
|
||
|
const uint8_t GAIN5C_TABLE[] = {0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, 0xb8, 0xc8};
|
||
|
const uint8_t GAIN63_TABLE[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
|
||
|
const uint8_t GAIN61_VALUE = 0xCA; // 0xC0 | 0x02 (freq present) | 0x08 (gain present)
|
||
|
|
||
|
/*!< Power consumption configuration table (p12). */
|
||
|
const uint8_t POWER_TABLE[] = {48, 56, 63, 70, 77, 91, 105, 115, 40, 44, 47, 51, 54, 61, 68, 78};
|
||
|
const uint8_t POWER67_TABLE[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
|
||
|
const uint8_t POWER68_TABLE[] = {0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8,
|
||
|
24, 24, 24, 24, 24, 24, 24, 24}; // See Page 12, shift by 3 bits
|
||
|
|
||
|
/*!< Frequency Configuration table (p14/15 of datasheet). */
|
||
|
const uint8_t FREQ_ADDR = 0x61;
|
||
|
const uint16_t FREQ_TABLE[] = {5696, 5715, 5730, 5748, 5765, 5784, 5800, 5819, 5836, 5851, 5869, 5888};
|
||
|
const uint8_t FREQ5F_TABLE[] = {0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x40, 0x41, 0x42, 0x43};
|
||
|
const uint8_t FREQ60_TABLE[] = {0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e};
|
||
|
|
||
|
/*!< Value for RF and analog modules switch (p10). */
|
||
|
const uint8_t RF_OFF_TABLE[] = {0x46, 0xaa, 0x50};
|
||
|
const uint8_t RF_ON_TABLE[] = {0x45, 0x55, 0xA0};
|
||
|
const uint8_t RF_REG_ADDR[] = {0x5d, 0x62, 0x51};
|
||
|
|
||
|
/*!< Registers of Lighting delay time. Unit: ms, min 2s (p8) */
|
||
|
const uint8_t HIGH_LEVEL_DELAY_CONTROL_ADDR = 0x41; /*!< Time_flag_out_ctrl 0x01 */
|
||
|
const uint8_t HIGH_LEVEL_DELAY_VALUE_ADDR = 0x42; /*!< Time_flag_out_1 Bit<7:0> */
|
||
|
|
||
|
const uint8_t RESET_ADDR = 0x00;
|
||
|
|
||
|
/*!< Sensing distance address */
|
||
|
const uint8_t SIGNAL_DETECTION_THRESHOLD_ADDR_LO = 0x10;
|
||
|
const uint8_t SIGNAL_DETECTION_THRESHOLD_ADDR_HI = 0x11;
|
||
|
|
||
|
/*!< Bit field value for power registers */
|
||
|
const uint8_t POWER_THRESHOLD_ADDR_HI = 0x68;
|
||
|
const uint8_t POWER_THRESHOLD_ADDR_LO = 0x67;
|
||
|
const uint8_t PWR_WORK_TIME_EN = 8; // Reg 0x67
|
||
|
const uint8_t PWR_BURST_TIME_EN = 32; // Reg 0x68
|
||
|
const uint8_t PWR_THRESH_EN = 64; // Reg 0x68
|
||
|
const uint8_t PWR_THRESH_VAL_EN = 128; // Reg 0x67
|
||
|
|
||
|
/*!< Times */
|
||
|
const uint8_t TRIGGER_BASE_TIME_ADDR = 0x3D; // 4 bytes, so up to 0x40
|
||
|
const uint8_t PROTECT_TIME_ADDR = 0x4E; // 2 bytes, up to 0x4F
|
||
|
const uint8_t TRIGGER_KEEP_TIME_ADDR = 0x42; // 4 bytes, so up to 0x45
|
||
|
const uint8_t TIME41_VALUE = 1;
|
||
|
const uint8_t SELF_CHECK_TIME_ADDR = 0x38; // 2 bytes, up to 0x39
|
||
|
|
||
|
namespace esphome {
|
||
|
namespace at581x {
|
||
|
|
||
|
static const char *const TAG = "at581x";
|
||
|
|
||
|
bool AT581XComponent::i2c_write_reg(uint8_t addr, uint8_t data) {
|
||
|
return this->write_register(addr, &data, 1) == esphome::i2c::NO_ERROR;
|
||
|
}
|
||
|
bool AT581XComponent::i2c_write_reg(uint8_t addr, uint32_t data) {
|
||
|
return this->i2c_write_reg(addr + 0, uint8_t(data & 0xFF)) &&
|
||
|
this->i2c_write_reg(addr + 1, uint8_t((data >> 8) & 0xFF)) &&
|
||
|
this->i2c_write_reg(addr + 2, uint8_t((data >> 16) & 0xFF)) &&
|
||
|
this->i2c_write_reg(addr + 3, uint8_t((data >> 24) & 0xFF));
|
||
|
}
|
||
|
bool AT581XComponent::i2c_write_reg(uint8_t addr, uint16_t data) {
|
||
|
return this->i2c_write_reg(addr, uint8_t(data & 0xFF)) && this->i2c_write_reg(addr + 1, uint8_t((data >> 8) & 0xFF));
|
||
|
}
|
||
|
|
||
|
bool AT581XComponent::i2c_read_reg(uint8_t addr, uint8_t &data) {
|
||
|
return this->read_register(addr, &data, 1) == esphome::i2c::NO_ERROR;
|
||
|
}
|
||
|
|
||
|
void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up AT581X..."); }
|
||
|
void AT581XComponent::dump_config() { LOG_I2C_DEVICE(this); }
|
||
|
#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
|
||
|
bool AT581XComponent::i2c_write_config() {
|
||
|
ESP_LOGCONFIG(TAG, "Writing new config for AT581X...");
|
||
|
ESP_LOGCONFIG(TAG, "Frequency: %dMHz", this->freq_);
|
||
|
ESP_LOGCONFIG(TAG, "Sensing distance: %d", this->delta_);
|
||
|
ESP_LOGCONFIG(TAG, "Power: %dµA", this->power_);
|
||
|
ESP_LOGCONFIG(TAG, "Gain: %d", this->gain_);
|
||
|
ESP_LOGCONFIG(TAG, "Trigger base time: %dms", this->trigger_base_time_ms_);
|
||
|
ESP_LOGCONFIG(TAG, "Trigger keep time: %dms", this->trigger_keep_time_ms_);
|
||
|
ESP_LOGCONFIG(TAG, "Protect time: %dms", this->protect_time_ms_);
|
||
|
ESP_LOGCONFIG(TAG, "Self check time: %dms", this->self_check_time_ms_);
|
||
|
|
||
|
// Set frequency point
|
||
|
if (!this->i2c_write_reg(FREQ_ADDR, GAIN61_VALUE)) {
|
||
|
ESP_LOGE(TAG, "Failed to write AT581X Freq mode");
|
||
|
return false;
|
||
|
}
|
||
|
// Find the current frequency from the table to know what value to write
|
||
|
for (size_t i = 0; i < ARRAY_SIZE(FREQ_TABLE) + 1; i++) {
|
||
|
if (i == ARRAY_SIZE(FREQ_TABLE)) {
|
||
|
ESP_LOGE(TAG, "Set frequency not found");
|
||
|
return false;
|
||
|
}
|
||
|
if (FREQ_TABLE[i] == this->freq_) {
|
||
|
if (!this->i2c_write_reg(0x5F, FREQ5F_TABLE[i]) || !this->i2c_write_reg(0x60, FREQ60_TABLE[i])) {
|
||
|
ESP_LOGE(TAG, "Failed to write AT581X Freq value");
|
||
|
return false;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Set distance
|
||
|
if (!this->i2c_write_reg(SIGNAL_DETECTION_THRESHOLD_ADDR_LO, (uint8_t) (this->delta_ & 0xFF)) ||
|
||
|
!this->i2c_write_reg(SIGNAL_DETECTION_THRESHOLD_ADDR_HI, (uint8_t) (this->delta_ >> 8))) {
|
||
|
ESP_LOGE(TAG, "Failed to write AT581X sensing distance low");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Set power setting
|
||
|
uint8_t pwr67 = PWR_THRESH_VAL_EN | PWR_WORK_TIME_EN, pwr68 = PWR_BURST_TIME_EN | PWR_THRESH_EN;
|
||
|
for (size_t i = 0; i < ARRAY_SIZE(POWER_TABLE) + 1; i++) {
|
||
|
if (i == ARRAY_SIZE(POWER_TABLE)) {
|
||
|
ESP_LOGE(TAG, "Set power not found");
|
||
|
return false;
|
||
|
}
|
||
|
if (POWER_TABLE[i] == this->power_) {
|
||
|
pwr67 |= POWER67_TABLE[i];
|
||
|
pwr68 |= POWER68_TABLE[i]; // See Page 12
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!this->i2c_write_reg(POWER_THRESHOLD_ADDR_LO, pwr67) || !this->i2c_write_reg(POWER_THRESHOLD_ADDR_HI, pwr68)) {
|
||
|
ESP_LOGE(TAG, "Failed to write AT581X power registers");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Set gain
|
||
|
if (!this->i2c_write_reg(GAIN_ADDR_TABLE[0], GAIN5C_TABLE[this->gain_]) ||
|
||
|
!this->i2c_write_reg(GAIN_ADDR_TABLE[1], GAIN63_TABLE[this->gain_ >> 1])) {
|
||
|
ESP_LOGE(TAG, "Failed to write AT581X gain registers");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Set times
|
||
|
if (!this->i2c_write_reg(TRIGGER_BASE_TIME_ADDR, (uint32_t) this->trigger_base_time_ms_)) {
|
||
|
ESP_LOGE(TAG, "Failed to write AT581X trigger base time registers");
|
||
|
return false;
|
||
|
}
|
||
|
if (!this->i2c_write_reg(TRIGGER_KEEP_TIME_ADDR, (uint32_t) this->trigger_keep_time_ms_)) {
|
||
|
ESP_LOGE(TAG, "Failed to write AT581X trigger keep time registers");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!this->i2c_write_reg(PROTECT_TIME_ADDR, (uint16_t) this->protect_time_ms_)) {
|
||
|
ESP_LOGE(TAG, "Failed to write AT581X protect time registers");
|
||
|
return false;
|
||
|
}
|
||
|
if (!this->i2c_write_reg(SELF_CHECK_TIME_ADDR, (uint16_t) this->self_check_time_ms_)) {
|
||
|
ESP_LOGE(TAG, "Failed to write AT581X self check time registers");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!this->i2c_write_reg(0x41, TIME41_VALUE)) {
|
||
|
ESP_LOGE(TAG, "Failed to enable AT581X time registers");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Don't know why it's required in other code, it's not in datasheet
|
||
|
if (!this->i2c_write_reg(0x55, (uint8_t) 0x04)) {
|
||
|
ESP_LOGE(TAG, "Failed to enable AT581X");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Ok, config is written, let's reset the chip so it's using the new config
|
||
|
return this->reset_hardware_frontend();
|
||
|
}
|
||
|
|
||
|
// float AT581XComponent::get_setup_priority() const { return 0; }
|
||
|
bool AT581XComponent::reset_hardware_frontend() {
|
||
|
if (!this->i2c_write_reg(RESET_ADDR, (uint8_t) 0) || !this->i2c_write_reg(RESET_ADDR, (uint8_t) 1)) {
|
||
|
ESP_LOGE(TAG, "Failed to reset AT581X hardware frontend");
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void AT581XComponent::set_rf_mode(bool enable) {
|
||
|
const uint8_t *p = enable ? &RF_ON_TABLE[0] : &RF_OFF_TABLE[0];
|
||
|
for (size_t i = 0; i < ARRAY_SIZE(RF_REG_ADDR); i++) {
|
||
|
if (!this->i2c_write_reg(RF_REG_ADDR[i], p[i])) {
|
||
|
ESP_LOGE(TAG, "Failed to write AT581X RF mode");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace at581x
|
||
|
} // namespace esphome
|