From a1a7eb9e598b1e3639d4c30429e8223a7717c6c4 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 31 Oct 2020 15:45:36 +1100 Subject: [PATCH] Hall Effect sensor working in higher range --- .../TS100/Core/BSP/Pine64/I2C_Wrapper.cpp | 172 +++++++++--------- workspace/TS100/Core/Drivers/Si7210.cpp | 159 +++++++++++++++- workspace/TS100/Core/Drivers/Si7210.h | 6 + workspace/TS100/Core/Drivers/Si7210_defines.h | 78 +++++++- 4 files changed, 320 insertions(+), 95 deletions(-) diff --git a/workspace/TS100/Core/BSP/Pine64/I2C_Wrapper.cpp b/workspace/TS100/Core/BSP/Pine64/I2C_Wrapper.cpp index 90818c4d..f1e9f25b 100644 --- a/workspace/TS100/Core/BSP/Pine64/I2C_Wrapper.cpp +++ b/workspace/TS100/Core/BSP/Pine64/I2C_Wrapper.cpp @@ -293,9 +293,9 @@ bool FRToSI2C::Mem_Write(uint16_t DevAddress, uint16_t MemAddress, uint8_t *p_bu return false; } } + timeout = 0; if (timeout < I2C_TIME_OUT) { i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); - timeout = 0; state = I2C_TRANSMIT_DATA; } else { //Dont retry as this means a NAK @@ -416,106 +416,106 @@ bool FRToSI2C::writeRegistersBulk(const uint8_t address, const I2C_REG *register bool FRToSI2C::wakePart(uint16_t DevAddress) { //wakepart is a special case where only the device address is sent if (!lock()) - return false; + return false; - i2c_interrupt_disable(I2C0, I2C_INT_ERR); - i2c_interrupt_disable(I2C0, I2C_INT_EV); - i2c_interrupt_disable(I2C0, I2C_INT_BUF); - dma_parameter_struct dma_init_struct; + i2c_interrupt_disable(I2C0, I2C_INT_ERR); + i2c_interrupt_disable(I2C0, I2C_INT_EV); + i2c_interrupt_disable(I2C0, I2C_INT_BUF); + dma_parameter_struct dma_init_struct; - uint8_t state = I2C_START; - uint16_t timeout = 0; - bool done = false; - bool timedout = false; - while (!(done || timedout)) { - switch (state) { - case I2C_START: - /* i2c master sends start signal only when the bus is idle */ - while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT )) { - timeout++; - } - if (timeout < I2C_TIME_OUT) { - i2c_start_on_bus(I2C0); - timeout = 0; - state = I2C_SEND_ADDRESS; - } else { - I2C_Unstick(); - timeout = 0; - state = I2C_START; - } - break; - case I2C_SEND_ADDRESS: - /* i2c master sends START signal successfully */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT )) { - timeout++; - } - if (timeout < I2C_TIME_OUT) { - i2c_master_addressing(I2C0, DevAddress, I2C_TRANSMITTER); - timeout = 0; - state = I2C_CLEAR_ADDRESS_FLAG; - } else { - timedout = true; - done = true; - timeout = 0; - state = I2C_START; - } - break; - case I2C_CLEAR_ADDRESS_FLAG: - /* address flag set means i2c slave sends ACK */ - while ((!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) && (timeout < I2C_TIME_OUT )) { - timeout++; - if (i2c_flag_get(I2C0, I2C_FLAG_AERR)) { - i2c_flag_clear(I2C0, I2C_FLAG_AERR); - i2c_stop_on_bus(I2C0); - /* i2c master sends STOP signal successfully */ - while ((I2C_CTL0(I2C0) & 0x0200) && (timeout < I2C_TIME_OUT )) { - timeout++; - } - //Address NACK'd - unlock(); - return false; - } - } - if (timeout < I2C_TIME_OUT) { - i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); - timeout = 0; - state = I2C_STOP; - } else { - //Dont retry as this means a NAK + uint8_t state = I2C_START; + uint16_t timeout = 0; + bool done = false; + bool timedout = false; + while (!(done || timedout)) { + switch (state) { + case I2C_START: + /* i2c master sends start signal only when the bus is idle */ + while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT )) { + timeout++; + } + if (timeout < I2C_TIME_OUT) { + i2c_start_on_bus(I2C0); + timeout = 0; + state = I2C_SEND_ADDRESS; + } else { + I2C_Unstick(); + timeout = 0; + state = I2C_START; + } + break; + case I2C_SEND_ADDRESS: + /* i2c master sends START signal successfully */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT )) { + timeout++; + } + if (timeout < I2C_TIME_OUT) { + i2c_master_addressing(I2C0, DevAddress, I2C_TRANSMITTER); + timeout = 0; + state = I2C_CLEAR_ADDRESS_FLAG; + } else { + timedout = true; + done = true; + timeout = 0; + state = I2C_START; + } + break; + case I2C_CLEAR_ADDRESS_FLAG: + /* address flag set means i2c slave sends ACK */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) && (timeout < I2C_TIME_OUT )) { + timeout++; + if (i2c_flag_get(I2C0, I2C_FLAG_AERR)) { + i2c_flag_clear(I2C0, I2C_FLAG_AERR); i2c_stop_on_bus(I2C0); /* i2c master sends STOP signal successfully */ while ((I2C_CTL0(I2C0) & 0x0200) && (timeout < I2C_TIME_OUT )) { timeout++; } + //Address NACK'd unlock(); return false; } - break; - - case I2C_STOP: - /* send a stop condition to I2C bus */ + } + if (timeout < I2C_TIME_OUT) { + i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); + timeout = 0; + state = I2C_STOP; + } else { + //Dont retry as this means a NAK i2c_stop_on_bus(I2C0); /* i2c master sends STOP signal successfully */ while ((I2C_CTL0(I2C0) & 0x0200) && (timeout < I2C_TIME_OUT )) { timeout++; } - if (timeout < I2C_TIME_OUT) { - timeout = 0; - state = I2C_END; - done = true; - } else { - timedout = true; - done = true; - timeout = 0; - state = I2C_START; - } - break; - default: - state = I2C_START; - timeout = 0; - break; + unlock(); + return false; } + break; + + case I2C_STOP: + /* send a stop condition to I2C bus */ + i2c_stop_on_bus(I2C0); + /* i2c master sends STOP signal successfully */ + while ((I2C_CTL0(I2C0) & 0x0200) && (timeout < I2C_TIME_OUT )) { + timeout++; + } + if (timeout < I2C_TIME_OUT) { + timeout = 0; + state = I2C_END; + done = true; + } else { + timedout = true; + done = true; + timeout = 0; + state = I2C_START; + } + break; + default: + state = I2C_START; + timeout = 0; + break; } - unlock(); - return timedout == false; + } + unlock(); + return timedout == false; } diff --git a/workspace/TS100/Core/Drivers/Si7210.cpp b/workspace/TS100/Core/Drivers/Si7210.cpp index d3ef1686..b3eae4ae 100644 --- a/workspace/TS100/Core/Drivers/Si7210.cpp +++ b/workspace/TS100/Core/Drivers/Si7210.cpp @@ -3,27 +3,61 @@ * * Created on: 5 Oct. 2020 * Author: Ralim + * + * This is based on the very nice sample code by Sean Farrelly (@FARLY7) + * Over here : https://github.com/FARLY7/si7210-driver + * + * This class is licensed as MIT to match this code base */ #include #include "Si7210_defines.h" #include "I2C_Wrapper.hpp" bool Si7210::detect() { - uint8_t temp; return FRToSI2C::wakePart(SI7210_ADDRESS); } bool Si7210::init() { //Turn on auto increment and sanity check ID + //Load OTP cal + uint8_t temp; if (FRToSI2C::Mem_Read(SI7210_ADDRESS, SI7210_REG_ID, &temp, 1)) { // We don't really care what model it is etc, just probing to check its probably this iC if (temp != 0x00 && temp != 0xFF) { - temp = 0x01; //turn on auto increment - if (FRToSI2C::Mem_Write(SI7210_ADDRESS, SI7210_REG_INCR, &temp, 1)) { - return true; + temp = 0x00; + + /* Set device and internal driver settings */ + if (!write_reg( SI7210_CTRL1, (uint8_t) ~SW_LOW4FIELD_MASK, 0)) { + return false; } + + /* Disable periodic auto-wakeup by device, and tamper detect. */ + if ((!write_reg(SI7210_CTRL3, (uint8_t) ~SL_TIMEENA_MASK, 0))) + return false; + + /* Disable tamper detection by setting sw_tamper to 63 */ + if (!write_reg(SI7210_CTRL3, SL_FAST_MASK | SL_TIMEENA_MASK, 63 << 2)) + return false; + + if (!set_high_range()) + return false; + + /* Stop the control loop by setting stop bit */ + if (!write_reg( SI7210_POWER_CTRL, MEAS_MASK | USESTORE_MASK, STOP_MASK)) /* WARNING: Removed USE_STORE MASK */ + return false; + + /* Use a burst size of 128/4096 samples in FIR and IIR modes */ + if (!write_reg(SI7210_CTRL4, 0, DF_BURSTSIZE_128 | DF_BW_4096)) + return false; + + /* Select field strength measurement */ + if (!write_reg( SI7210_DSPSIGSEL, 0, DSP_SIGSEL_FIELD_MASK)) + return false; + + return true; //start_periodic_measurement(); + } } return false; @@ -32,6 +66,119 @@ bool Si7210::init() { int16_t Si7210::read() { //Read the two regs int16_t temp = 0; - FRToSI2C::Mem_Read(SI7210_ADDRESS, SI7210_REG_DATAH, (uint8_t*) &temp, 2); - return __builtin_bswap16(temp); + if (!get_field_strength(&temp)) { + temp = 0; + } + return temp; +} + +bool Si7210::write_reg(const uint8_t reg, const uint8_t mask, const uint8_t val) { + uint8_t temp = 0; + if (mask) { + if (!read_reg(reg, &temp)) { + return false; + } + temp &= mask; + } + temp |= val; + return FRToSI2C::Mem_Write(SI7210_ADDRESS, reg, &temp, 1); +} + +bool Si7210::read_reg(const uint8_t reg, uint8_t* val) { + return FRToSI2C::Mem_Read(SI7210_ADDRESS, reg, val, 1); +} + +bool Si7210::start_periodic_measurement() { + /* Enable periodic wakeup */ + if (!write_reg(SI7210_CTRL3, (uint8_t) ~SL_TIMEENA_MASK, SL_TIMEENA_MASK)) + return false; + + /* Start measurement */ + /* Change to ~STOP_MASK with STOP_MASK */ + return write_reg( SI7210_POWER_CTRL, MEAS_MASK | USESTORE_MASK, 0); + +} + +bool Si7210::get_field_strength(int16_t* field) { + *field = 0; + uint8_t val = 0; + FRToSI2C::wakePart(SI7210_ADDRESS); + + if (!write_reg( SI7210_POWER_CTRL, MEAS_MASK | USESTORE_MASK, STOP_MASK)) + return false; + + /* Read most-significant byte */ + if (!read_reg( SI7210_DSPSIGM, &val)) + return false; + *field = (val & DSP_SIGM_DATA_MASK) << 8; + + /* Read least-significant byte of data */ + if (!read_reg( SI7210_DSPSIGL, &val)) + return false; + + *field += val; + *field -= 16384U; + //field is now a +- measurement + //In units of 0.0125 mT + // Aka 12.5uT + //Clear flags + read_reg( SI7210_CTRL1, &val); + read_reg( SI7210_CTRL2, &val); +//Start next one + + /* Use a burst size of 128/4096 samples in FIR and IIR modes */ + write_reg( SI7210_CTRL4, 0, DF_BURSTSIZE_128 | DF_BW_4096); + + /* Selet field strength measurement */ + write_reg( SI7210_DSPSIGSEL, 0, DSP_SIGSEL_FIELD_MASK); + + /* Start measurement */ + write_reg( SI7210_POWER_CTRL, MEAS_MASK | USESTORE_MASK, ONEBURST_MASK); + + return true; +} + +bool Si7210::set_high_range() { + //To set the unit into 200mT range, no magnet temperature calibration + // We want to copy OTP 0x27->0x2C into a0->a5 + uint8_t base_addr = 0x27; // You can change this to pick the temp calibration + bool worked = true; + uint8_t val = 0; + + /* Load A0 register */ + worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr); + worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK); + worked &= read_reg( SI7210_OTP_DATA, &val); + worked &= write_reg( SI7210_A0, 0, val); + + /* Load A1 register */ + worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr + 1); + worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK); + worked &= read_reg( SI7210_OTP_DATA, &val); + worked &= write_reg( SI7210_A1, 0, val); + + /* Load A2 register */ + worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr + 2); + worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK); + worked &= read_reg( SI7210_OTP_DATA, &val); + worked &= write_reg( SI7210_A2, 0, val); + + /* Load A3 register */ + worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr + 3); + worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK); + worked &= read_reg( SI7210_OTP_DATA, &val); + worked &= write_reg( SI7210_A3, 0, val); + + /* Load A4 register */ + worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr + 4); + worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK); + worked &= read_reg( SI7210_OTP_DATA, &val); + worked &= write_reg( SI7210_A4, 0, val); + + /* Load A5 register */ + worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr + 5); + worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK); + worked &= read_reg( SI7210_OTP_DATA, &val); + worked &= write_reg( SI7210_A5, 0, val); + return worked; } diff --git a/workspace/TS100/Core/Drivers/Si7210.h b/workspace/TS100/Core/Drivers/Si7210.h index 5f0477b5..ed99ba2b 100644 --- a/workspace/TS100/Core/Drivers/Si7210.h +++ b/workspace/TS100/Core/Drivers/Si7210.h @@ -16,6 +16,12 @@ public: static bool init(); static int16_t read(); private: + static bool write_reg(const uint8_t reg,const uint8_t mask,const uint8_t val); + static bool read_reg(const uint8_t reg, uint8_t *val); + static bool start_periodic_measurement(); + static bool get_field_strength(int16_t *field); + static bool set_high_range(); + }; #endif /* CORE_DRIVERS_SI7210_H_ */ diff --git a/workspace/TS100/Core/Drivers/Si7210_defines.h b/workspace/TS100/Core/Drivers/Si7210_defines.h index 3a7dc13d..a7123069 100644 --- a/workspace/TS100/Core/Drivers/Si7210_defines.h +++ b/workspace/TS100/Core/Drivers/Si7210_defines.h @@ -10,9 +10,81 @@ #define SI7210_ADDRESS (0x30<<1) #define SI7210_REG_ID 0xC0 -#define SI7210_REG_DATAH 0xC1 -#define SI7210_REG_DATAL 0xC2 -#define SI7210_REG_INCR 0xC5 + +/* Si7210 Register addresses */ +#define SI7210_HREVID 0xC0U +#define SI7210_DSPSIGM 0xC1U +#define SI7210_DSPSIGL 0xC2U +#define SI7210_DSPSIGSEL 0xC3U +#define SI7210_POWER_CTRL 0xC4U +#define SI7210_ARAUTOINC 0xC5U +#define SI7210_CTRL1 0xC6U +#define SI7210_CTRL2 0xC7U +#define SI7210_SLTIME 0xC8U +#define SI7210_CTRL3 0xC9U +#define SI7210_A0 0xCAU +#define SI7210_A1 0xCBU +#define SI7210_A2 0xCCU +#define SI7210_CTRL4 0xCDU +#define SI7210_A3 0xCEU +#define SI7210_A4 0xCFU +#define SI7210_A5 0xD0U +#define SI7210_OTP_ADDR 0xE1U +#define SI7210_OTP_DATA 0xE2U +#define SI7210_OTP_CTRL 0xE3U +#define SI7210_TM_FG 0xE4U + +/* Si7210 Register bit masks */ +#define CHIP_ID_MASK 0xF0U +#define REV_ID_MASK 0x0FU +#define DSP_SIGSEL_MASK 0x07U +#define MEAS_MASK 0x80U +#define USESTORE_MASK 0x08U +#define ONEBURST_MASK 0x04U +#define STOP_MASK 0x02U +#define SLEEP_MASK 0x01U +#define ARAUTOINC_MASK 0x01U +#define SW_LOW4FIELD_MASK 0x80U +#define SW_OP_MASK 0x7FU +#define SW_FIELDPOLSEL_MASK 0xC0U +#define SW_HYST_MASK 0x3FU +#define SW_TAMPER_MASK 0xFCU +#define SL_FAST_MASK 0x02U +#define SL_TIMEENA_MASK 0x01U +#define DF_BURSTSIZE_MASK 0xE0U +#define DF_BW_MASK 0x1EU +#define DF_IIR_MASK 0x01U +#define OTP_READ_EN_MASK 0x02U +#define OTP_BUSY_MASK 0x01U +#define TM_FG_MASK 0x03U + +#define DSP_SIGM_DATA_FLAG 0x80U +#define DSP_SIGM_DATA_MASK 0x7FU +#define DSP_SIGSEL_TEMP_MASK 0x01U +#define DSP_SIGSEL_FIELD_MASK 0x04U + +/* Burst sizes */ +#define DF_BW_1 0x0U << 1 +#define DF_BW_2 0x1U << 1 +#define DF_BW_4 0x2U << 1 +#define DF_BW_8 0x3U << 1 +#define DF_BW_16 0x4U << 1 +#define DF_BW_32 0x5U << 1 +#define DF_BW_64 0x6U << 1 +#define DF_BW_128 0x7U << 1 +#define DF_BW_256 0x8U << 1 +#define DF_BW_512 0x9U << 1 +#define DF_BW_1024 0xAU << 1 +#define DF_BW_2048 0xBU << 1 +#define DF_BW_4096 0xCU << 1 +#define DF_BURSTSIZE_1 0x0U << 5 +#define DF_BURSTSIZE_2 0x1U << 5 +#define DF_BURSTSIZE_4 0x2U << 5 +#define DF_BURSTSIZE_8 0x3U << 5 +#define DF_BURSTSIZE_16 0x4U << 5 +#define DF_BURSTSIZE_32 0x5U << 5 +#define DF_BURSTSIZE_64 0x6U << 5 +#define DF_BURSTSIZE_128 0x7U << 5