Fertirrega_v6 Without Blocking Samples
This commit is contained in:
@@ -11,6 +11,15 @@
|
||||
|
||||
#include "ad5934_driver.h"
|
||||
|
||||
volatile AD5934_State_t ad5934_state = AD5934_IDLE;
|
||||
|
||||
volatile uint32_t g_ms_counter = 0; /* Incrementado a cada 1ms no Timer */
|
||||
|
||||
uint8_t current_mux_channel = AD5934_ID_RTD;
|
||||
uint8_t current_hw_mux_connection = ADG715_Channel_Map[AD5934_CH_RTD_LOW_GAIN]; // PT100 Connection
|
||||
|
||||
uint8_t sweep_count = 0; /* Conta quantos sweeps foram feitos no canal atual (0 a 7) */
|
||||
|
||||
int16_t temperature_dut_samples[2][AD5934_TEMP_AVERAGES]={{0},{0}};
|
||||
int16_t temperature_ref_samples[2][AD5934_TEMP_AVERAGES]={{0},{0}};
|
||||
float temperature_display[AD5934_TEMP_AVERAGES] = {0};
|
||||
@@ -19,6 +28,11 @@ float temperature_display[AD5934_TEMP_AVERAGES] = {0};
|
||||
int16_t ec_dut_samples[2][AD5934_EC_AVERAGES]={{0},{0}};
|
||||
float ec_display[AD5934_EC_AVERAGES] = {0};
|
||||
|
||||
AD5934_filter_t g_rtd_filter = {0};
|
||||
AD5934_filter_t g_ec_filter = {0};
|
||||
AD5934_filter_t g_ref_filter = {0};
|
||||
|
||||
uint8_t is_new_channel = 1;
|
||||
/******************************************************************************
|
||||
* @brief Set an AD5934 internal register value.
|
||||
*
|
||||
@@ -151,7 +165,7 @@ void AD5934_Init(void)
|
||||
AD5934_SetRegisterValue(AD5934_NR_INCR_REG_LB, AD5934_STEP_FREQ_0, 2);
|
||||
|
||||
// Set 128 Settling Time
|
||||
AD5934_SetRegisterValue(AD5934_NR_SETTLE_REG_LB, AD5934_SETTLING_TIME_0S01, 2);
|
||||
AD5934_SetRegisterValue(AD5934_NR_SETTLE_REG_LB, AD5934_SETTLING_TIME_64, 2);
|
||||
|
||||
}
|
||||
|
||||
@@ -177,18 +191,50 @@ void AD5934_RestartSweep(void)
|
||||
AD5934_SetRegisterValue(AD5934_CONTROL_REG_HB, (AD5934_CONTROL_FUNCTION(AD5934_START_FREQ_SWEEP) | AD5934_CONTROL_RANGE(AD5934_400mVpp_RANGE) | AD5934_PGA_GAIN(AD5934_PGA_GAIN_X1)), 1);
|
||||
|
||||
// Wait for data to be valid
|
||||
AD5934_Wait_For_Data_Valid();
|
||||
//AD5934_Wait_For_Data_Valid();
|
||||
|
||||
// Power Down
|
||||
AD5934_SetRegisterValue(AD5934_CONTROL_REG_HB, (AD5934_CONTROL_FUNCTION(AD5934_POWER_DOWN) | AD5934_CONTROL_RANGE(AD5934_400mVpp_RANGE) | AD5934_PGA_GAIN(AD5934_PGA_GAIN_X1)), 1);
|
||||
//AD5934_SetRegisterValue(AD5934_CONTROL_REG_HB, (AD5934_CONTROL_FUNCTION(AD5934_POWER_DOWN) | AD5934_CONTROL_RANGE(AD5934_400mVpp_RANGE) | AD5934_PGA_GAIN(AD5934_PGA_GAIN_X1)), 1);
|
||||
|
||||
// Place AD5934 in standby (instead of power down)
|
||||
//AD5934_SetRegisterValue(AD5934_CONTROL_REG_HB, (AD5934_CONTROL_FUNCTION(AD5934_STANDBY) | AD5934_CONTROL_RANGE(AD5934_400mVpp_RANGE) | AD5934_PGA_GAIN(AD5934_PGA_GAIN_X1)), 1);
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* @brief Repeat Sweep at the AD5934 with the same sweep parameters.
|
||||
*
|
||||
* @param None.
|
||||
*
|
||||
* @return none.
|
||||
******************************************************************************/
|
||||
void AD5934_Repeat_Sweep(void)
|
||||
{
|
||||
// Repeat AD5934 Sweep
|
||||
AD5934_SetRegisterValue(AD5934_CONTROL_REG_HB, (AD5934_CONTROL_FUNCTION(AD5934_REPEAT_FREQ) | AD5934_CONTROL_RANGE(AD5934_400mVpp_RANGE) | AD5934_PGA_GAIN(AD5934_PGA_GAIN_X1)), 1);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* @brief Stop the AD5934 to sweep.
|
||||
*
|
||||
* @param None.
|
||||
*
|
||||
* @return None.
|
||||
******************************************************************************/
|
||||
void AD5934_StopSweep(void)
|
||||
{
|
||||
// Power Down
|
||||
//AD5934_SetRegisterValue(AD5934_CONTROL_REG_HB, (AD5934_CONTROL_FUNCTION(AD5934_POWER_DOWN) | AD5934_CONTROL_RANGE(AD5934_400mVpp_RANGE) | AD5934_PGA_GAIN(AD5934_PGA_GAIN_X1)), 1);
|
||||
|
||||
// Place AD5934 in standby (instead of power down to save startup time)
|
||||
AD5934_SetRegisterValue(AD5934_CONTROL_REG_HB, (AD5934_CONTROL_FUNCTION(AD5934_STANDBY) | AD5934_CONTROL_RANGE(AD5934_400mVpp_RANGE) | AD5934_PGA_GAIN(AD5934_PGA_GAIN_X1)), 1);
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* @brief Start the AD5934 frequency sweep parameters.
|
||||
*
|
||||
* @param: channel = AD5934_CH_REF_100R, AD5934_CH_REF_1K, AD5934_CH_REF_10K, AD5934_CH_PT100, AD5934_CH_PT1000 or AD5934_CH_EC
|
||||
* @param: none
|
||||
*
|
||||
* @return Real(int16) and Imaginary(int16) numbers into a int32.
|
||||
******************************************************************************/
|
||||
@@ -386,7 +432,6 @@ float AD5934_GetImpedance(float temperature_dut)
|
||||
ec_dut_samples[1][i] = ec_dut_samples[1][i-1];
|
||||
}
|
||||
|
||||
|
||||
// Deconstruct 32-bit sample into Real and Imaginary components
|
||||
ec_dut_samples[0][0] = (float)(int16_t)(sample_dut & 0xFFFF);
|
||||
ec_dut_samples[1][0] = (float)(int16_t)((sample_dut >> 16) & 0xFFFF);
|
||||
@@ -424,6 +469,30 @@ float AD5934_GetImpedance(float temperature_dut)
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* @brief Get Real and Imaginary values and calculate Magnitude.
|
||||
*
|
||||
* @param: none
|
||||
*
|
||||
* @return Magnitude (float).
|
||||
******************************************************************************/
|
||||
float AD5934_GetMagnitude(void)
|
||||
{
|
||||
|
||||
// Get Real Data register
|
||||
int16_t real_value = ((AD5934_GetRegisterValue(AD5934_REAL_REG_LB,2)));
|
||||
|
||||
// Get Imaginary Data register
|
||||
int16_t imag_value = ((AD5934_GetRegisterValue(AD5934_IMG_REG_LB,2)));
|
||||
|
||||
// Calculate Magnitude using float function sqrtf
|
||||
float magnitude = sqrtf(((float)real_value * (float)real_value) + ((float)imag_value * (float)imag_value));
|
||||
|
||||
// Return magnitude value
|
||||
return (magnitude);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Monitora o status com TIMEOUT para evitar travamento do sistema.
|
||||
@@ -585,6 +654,260 @@ float AD5934_Calibrate(float dry_probe_real, float dry_probe_imag, float standar
|
||||
return calibration_factor;
|
||||
}
|
||||
|
||||
|
||||
void AD5934_Process_System(void)
|
||||
{
|
||||
static uint32_t state_timer = 0;
|
||||
uint8_t status = 0;
|
||||
|
||||
switch (ad5934_state)
|
||||
{
|
||||
case AD5934_IDLE:
|
||||
ADG715_Update(current_mux_channel); // Starts changing the mux channel
|
||||
is_new_channel = 1; // Forces first channel
|
||||
state_timer = g_ms_counter;
|
||||
ad5934_state = AD5934_WAIT_MUX;
|
||||
break;
|
||||
|
||||
case AD5934_WAIT_MUX:
|
||||
if ((g_ms_counter - state_timer) >= AD5934_SYNC_MUX_SETTLING) // Wait until AD715 Mux switch stability
|
||||
{
|
||||
sweep_count = 0;
|
||||
ad5934_state = AD5934_START_CONVERSION;
|
||||
}
|
||||
break;
|
||||
|
||||
case AD5934_START_CONVERSION:
|
||||
state_timer = g_ms_counter; // Starts to count the Burst period for the sweeps
|
||||
|
||||
/* DECISÃO DE COMANDO: Novo canal vs Leituras sucessivas */
|
||||
if (is_new_channel) // Decision: Channel Switched or Next Sample in the Burst ?
|
||||
{
|
||||
AD5934_RestartSweep(); // Clean some AD5934 internal registers to Re-Start Sweep
|
||||
is_new_channel = 0; // Resets the flag to read the next Sample Burst
|
||||
}
|
||||
else
|
||||
{
|
||||
AD5934_Repeat_Sweep(); // Get Sample and just repeat reading
|
||||
}
|
||||
|
||||
ad5934_state = AD5934_WAIT_CONVERSION; // Next State
|
||||
break;
|
||||
|
||||
case AD5934_WAIT_CONVERSION:
|
||||
status = (uint8_t)AD5934_GetRegisterValue(AD5934_STATUS_REG, 1); // Check if the current converion is ready in the AD5934
|
||||
if ((status & AD5934_STATUS_DATA_VALID) != 0)
|
||||
{
|
||||
ad5934_state = AD5934_READ_DATA;
|
||||
}
|
||||
break;
|
||||
|
||||
case AD5934_READ_DATA:
|
||||
{
|
||||
float magnitude = AD5934_GetMagnitude();
|
||||
|
||||
switch (current_mux_channel)
|
||||
{
|
||||
case AD5934_ID_RTD:
|
||||
AD5934_RTD_NewSample(magnitude);
|
||||
break;
|
||||
|
||||
case AD5934_ID_EC:
|
||||
AD5934_EC_NewSample(magnitude);
|
||||
break;
|
||||
|
||||
case AD5934_ID_REF:
|
||||
AD5934_Reference_NewSample(magnitude);
|
||||
break;
|
||||
}
|
||||
|
||||
sweep_count++;
|
||||
|
||||
if (sweep_count >= AD5934_BURST_SIZE) // Check if number of samples for the burst is complete
|
||||
{
|
||||
ad5934_state = AD5934_SWITCH_MUX; // Next Re-Start Sweep
|
||||
}
|
||||
else
|
||||
{
|
||||
ad5934_state = AD5934_WAIT_NEXT_SWEEP; // Next Repeat Sweep
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AD5934_WAIT_NEXT_SWEEP:
|
||||
if ((g_ms_counter - state_timer) >= AD5934_TIME_PER_SWEEP)
|
||||
{
|
||||
ad5934_state = AD5934_START_CONVERSION;
|
||||
}
|
||||
break;
|
||||
|
||||
case AD5934_SWITCH_MUX:
|
||||
AD5934_StopSweep(); // Put AD5934 in Standby
|
||||
current_mux_channel = (uint8_t)((current_mux_channel + 1) % 3); // Pointer to the next channel (Circular Buffer 0 to 2)
|
||||
switch (current_mux_channel)
|
||||
{
|
||||
case AD5934_ID_RTD:
|
||||
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_SET) // OFF = PT1000, ON = PT100, PA7 has internal pull-up and switch connects to GND
|
||||
current_hw_mux_connection = AD5934_CH_RTD_LOW_GAIN; //PT100
|
||||
else
|
||||
current_hw_mux_connection = AD5934_CH_RTD_MID_GAIN; //PT1000
|
||||
break;
|
||||
|
||||
case AD5934_ID_EC:
|
||||
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET) // Hardware Setup: Gain Selection (PA6)
|
||||
current_hw_mux_connection = AD5934_CH_EC_HIGH_GAIN;
|
||||
else
|
||||
current_hw_mux_connection = AD5934_CH_EC_MID_GAIN;
|
||||
break;
|
||||
|
||||
case AD5934_ID_REF:
|
||||
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_SET) // OFF = PT1000, ON = PT100, PA7 has internal pull-up and switch connects to GND
|
||||
current_hw_mux_connection = AD5934_CH_REF100R_LOW_GAIN; // 100 Ohms Reference Resistor
|
||||
else
|
||||
current_hw_mux_connection = AD5934_CH_REF1K_MID_GAIN; // 1K Reference Resistor
|
||||
break;
|
||||
}
|
||||
ADG715_Update(current_hw_mux_connection); // Change the analog mux
|
||||
is_new_channel = 1; // Channel Switched
|
||||
state_timer = g_ms_counter; // Reload timer to wait for stability
|
||||
ad5934_state = AD5934_WAIT_MUX; // Next State
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple insertion sort - BURST_SIZE is small, so the cost is negligible
|
||||
* and it avoids depending on qsort/heap allocation.
|
||||
**/
|
||||
void AD5934_Sort_Array(float *v, uint8_t n)
|
||||
{
|
||||
for (uint8_t i = 1; i < n; i++)
|
||||
{
|
||||
float key = v[i];
|
||||
int8_t j = (int8_t)i - 1;
|
||||
while (j >= 0 && v[j] > key)
|
||||
{
|
||||
v[j + 1] = v[j];
|
||||
j--;
|
||||
}
|
||||
v[j + 1] = key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trimmed mean: sorts the buffer IN PLACE (the caller's burst buffer is
|
||||
* about to be reset right after this call anyway, so preserving its
|
||||
* original order is not needed), discards TRIM_COUNT values from each
|
||||
* end, and averages the remainder. Sorting in place avoids a temporary
|
||||
* array and removes the need for memcpy()/string.h entirely.
|
||||
**/
|
||||
float AD5934_Trimmed_Mean(float *buffer, uint8_t n, uint8_t trim)
|
||||
{
|
||||
AD5934_Sort_Array(buffer, n);
|
||||
|
||||
uint8_t start = trim;
|
||||
uint8_t end = n - trim; /* exclusive */
|
||||
|
||||
if (end <= start) /* guard against a misconfigured trim value */
|
||||
{
|
||||
start = 0;
|
||||
end = n;
|
||||
}
|
||||
|
||||
float sum = 0.0f;
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = start; i < end; i++)
|
||||
{
|
||||
sum += buffer[i];
|
||||
count++;
|
||||
}
|
||||
return sum / (float)count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple average of the history buffer (stage-2 sliding window)
|
||||
*/
|
||||
float AD5934_History_Average(const float *hist, uint8_t n)
|
||||
{
|
||||
float sum = 0.0f;
|
||||
for (uint8_t i = 0; i < n; i++)
|
||||
sum += hist[i];
|
||||
return sum / (float)n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Common processing core: applied identically to all 3 channels.
|
||||
* Receives the current raw reading and the channel's state (global
|
||||
* arrays/struct).
|
||||
**/
|
||||
void AD5934_Process_Sample(AD5934_filter_t *ch, float raw)
|
||||
{
|
||||
/* --- Stage 1: accumulate into the current burst --- */
|
||||
ch->burst[ch->burst_index] = raw;
|
||||
ch->burst_index++;
|
||||
|
||||
if (ch->burst_index < AD5934_BURST_SIZE)
|
||||
return; /* still collecting the burst, nothing else to do */
|
||||
|
||||
/* Burst complete: compute the trimmed mean */
|
||||
float burst_result = AD5934_Trimmed_Mean(ch->burst, AD5934_BURST_SIZE, AD5934_TRIM_COUNT);
|
||||
ch->burst_index = 0; /* reset for the next burst */
|
||||
|
||||
/* --- Stage 2a: rolling history / sliding window --- */
|
||||
ch->history[ch->history_index] = burst_result;
|
||||
ch->history_index = (uint8_t)((ch->history_index + 1) % AD5934_HISTORY_SIZE);
|
||||
if (ch->history_count < AD5934_HISTORY_SIZE)
|
||||
ch->history_count++;
|
||||
|
||||
float window_average = AD5934_History_Average(ch->history, ch->history_count);
|
||||
|
||||
/* --- Stage 2b: first-order IIR low-pass filter on the burst result --- */
|
||||
if (!ch->iir_initialized)
|
||||
{
|
||||
ch->iir_state = burst_result;
|
||||
ch->iir_initialized = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch->iir_state = AD5934_IIR_ALPHA * burst_result + (1.0f - AD5934_IIR_ALPHA) * ch->iir_state;
|
||||
}
|
||||
|
||||
/* Final output: combines the IIR state (fast response to real drift)
|
||||
* with the window average (more stable, slower response). Adjust
|
||||
* the weighting per channel if needed. */
|
||||
ch->filtered_value = 0.5f * ch->iir_state + 0.5f * window_average;
|
||||
ch->value_valid = 1;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* PUBLIC FUNCTIONS - ONE PER CHANNEL */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/* Call this every time a sweep completes with the mux on the RTD
|
||||
* (PT100/PT1000) channel */
|
||||
void AD5934_RTD_NewSample(float raw)
|
||||
{
|
||||
AD5934_Process_Sample(&g_rtd_filter, raw);
|
||||
}
|
||||
|
||||
/* Call this every time a sweep completes with the mux on the EC
|
||||
* (conductivity probe) channel */
|
||||
void AD5934_EC_NewSample(float raw)
|
||||
{
|
||||
AD5934_Process_Sample(&g_ec_filter, raw);
|
||||
}
|
||||
|
||||
/* Call this every time a sweep completes with the mux on the on-board
|
||||
* precision reference resistor channel */
|
||||
void AD5934_Reference_NewSample(float raw)
|
||||
{
|
||||
AD5934_Process_Sample(&g_ref_filter, raw);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* ADG715
|
||||
*****************************************************************************
|
||||
|
||||
@@ -9,9 +9,14 @@
|
||||
|
||||
#include "ads1015_driver.h"
|
||||
|
||||
ADS1015_I2C i2c;
|
||||
ADS1015_I2C i2c_ads1015;
|
||||
float ph_dut_samples[ADS1015_PH_AVERAGES]={0};
|
||||
|
||||
|
||||
ADS1015_filter_t g_ph_filter = {0};
|
||||
|
||||
volatile ADS1015_State_t ads1015_state = ADS1015_IDLE;
|
||||
|
||||
// Write the register
|
||||
static void writeRegister(ADS1015_I2C *i2c, uint8_t reg, uint16_t value) {
|
||||
uint8_t pData[3] = { reg, (uint8_t) (value >> 8), (uint8_t) (value & 0xFF) };
|
||||
@@ -36,13 +41,14 @@ static void ADSbegin(ADS1015_I2C *i2c) {
|
||||
// Declare an ADS1015 structure
|
||||
void ADS1015(ADS1015_I2C *i2c, I2C_HandleTypeDef *hi2c, uint8_t i2cAddress) {
|
||||
i2c->hi2c = hi2c;
|
||||
i2c->m_i2cAddress = i2cAddress << 1; // It's Important to shift the address << 1
|
||||
i2c->m_i2cAddress = i2cAddress; // << 1; // It's Important to shift the address << 1
|
||||
i2c->m_conversionDelay = ADS1015_CONVERSIONDELAY;
|
||||
i2c->m_bitShift = 4;
|
||||
i2c->m_gain = GAIN_TWOTHIRDS; /* +/- 6.144V range (limited to VDD +0.3V max!) */
|
||||
i2c->m_gain = GAIN_SIXTEEN; /* +/- 6.144V range (limited to VDD +0.3V max!) */
|
||||
//ADSbegin(i2c); //Ready is not used
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* // The ADC input range (or gain) can be changed via the following
|
||||
// functions, but be careful never to exceed VDD +0.3V max, or to
|
||||
@@ -126,7 +132,7 @@ int16_t ADSreadADC_Differential_0_1(ADS1015_I2C *i2c) {
|
||||
ADS1015_REG_CONFIG_CLAT_NONLAT | // Non-latching (default val)
|
||||
ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low (default val)
|
||||
ADS1015_REG_CONFIG_CMODE_TRAD | // Traditional comparator (default val)
|
||||
ADS1015_REG_CONFIG_DR_128SPS | // 128 samples per second (default)
|
||||
ADS1015_REG_CONFIG_DR_250SPS | // 128 samples per second (default)
|
||||
ADS1015_REG_CONFIG_MODE_SINGLE; // Single-shot mode (default)
|
||||
|
||||
// Set PGA/voltage range
|
||||
@@ -271,6 +277,207 @@ int16_t ADSgetLastConversionResults(ADS1015_I2C *i2c) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ADS1015_Process_System(void)
|
||||
{
|
||||
static uint32_t state_timer = 0;
|
||||
static uint32_t sample_count = 0;
|
||||
uint16_t config_reg = 0;
|
||||
|
||||
switch (ads1015_state)
|
||||
{
|
||||
case ADS1015_IDLE:
|
||||
sample_count = 0;
|
||||
ads1015_state = ADS1015_START_CONVERSION;
|
||||
break;
|
||||
|
||||
case ADS1015_START_CONVERSION:
|
||||
state_timer = g_ms_counter;
|
||||
|
||||
/* Escreve no Config Register (P[1:0]=01b) com OS=1 para
|
||||
* iniciar a conversão single-shot. O bit OS só tem efeito
|
||||
* quando o dispositivo está em power-down (seção 7.4.2.1) */
|
||||
// Write config register to the ADC
|
||||
writeRegister(&i2c_ads1015, ADS1015_REG_POINTER_CONFIG, ADS1015_CONFIG_START_SINGLE);
|
||||
|
||||
ads1015_state = ADS1015_WAIT_CONVERSION;
|
||||
break;
|
||||
|
||||
case ADS1015_WAIT_CONVERSION:
|
||||
/* Poll do bit OS no Config Register (P[1:0]=01b):
|
||||
* Ao LER o bit OS:
|
||||
* 0b = dispositivo ainda convertendo
|
||||
* 1b = conversão concluída, dado pronto no Conversion Register
|
||||
* (Tabela 8-4, descrição do bit OS)
|
||||
*/
|
||||
|
||||
config_reg = readRegister(&i2c_ads1015, ADS1015_REG_POINTER_CONFIG);
|
||||
|
||||
if (((config_reg & ADS1015_REG_CONFIG_OS_SINGLE) != 0) && (g_ms_counter - state_timer)>ADS1015_TIME_PER_SAMPLE)
|
||||
{
|
||||
ads1015_state = ADS1015_READ_DATA;
|
||||
}
|
||||
// (opcional) tratar timeout comparando g_ms_counter - state_timer
|
||||
// Datasheet: conversão single-cycle, tempo = 1/DR (ex: 1600SPS ≈ 625µs)
|
||||
break;
|
||||
|
||||
case ADS1015_READ_DATA:
|
||||
{
|
||||
/* Muda o Address Pointer para o Conversion Register (P[1:0]=00b)
|
||||
* e lê os 16 bits. Dado é 12 bits em complemento de 2,
|
||||
* left-justified em D[15:4]; D[3:0] sempre lê 0h (Tabela 8-3) */
|
||||
int16_t raw = (int16_t)readRegister(&i2c_ads1015, ADS1015_REG_POINTER_CONVERT);
|
||||
raw >>= 4; // remove os 4 bits reservados (D[3:0]), resultado = 12 bits signed
|
||||
|
||||
float value = (float)(4096 - raw); // ((float)raw * ADS1015_ADC_VREF) / ADS1015_ADC_MAX; // usa FSR configurado no PGA
|
||||
|
||||
ADS1015_pH_NewSample(value);
|
||||
|
||||
sample_count++;
|
||||
|
||||
if (sample_count >= ADS1015_BURST_SIZE)
|
||||
{
|
||||
ads1015_state = ADS1015_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_timer = g_ms_counter;
|
||||
ads1015_state = ADS1015_WAIT_NEXT_SAMPLE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ADS1015_WAIT_NEXT_SAMPLE:
|
||||
if ((g_ms_counter - state_timer) >= ADS1015_TIME_PER_SAMPLE)
|
||||
{
|
||||
ads1015_state = ADS1015_START_CONVERSION;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple insertion sort - BURST_SIZE is small, so the cost is negligible
|
||||
* and it avoids depending on qsort/heap allocation.
|
||||
**/
|
||||
void ADS1015_Sort_Array(float *v, uint8_t n)
|
||||
{
|
||||
for (uint8_t i = 1; i < n; i++)
|
||||
{
|
||||
float key = v[i];
|
||||
int8_t j = (int8_t)i - 1;
|
||||
while (j >= 0 && v[j] > key)
|
||||
{
|
||||
v[j + 1] = v[j];
|
||||
j--;
|
||||
}
|
||||
v[j + 1] = key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trimmed mean: sorts the buffer IN PLACE (the caller's burst buffer is
|
||||
* about to be reset right after this call anyway, so preserving its
|
||||
* original order is not needed), discards TRIM_COUNT values from each
|
||||
* end, and averages the remainder. Sorting in place avoids a temporary
|
||||
* array and removes the need for memcpy()/string.h entirely.
|
||||
**/
|
||||
float ADS1015_Trimmed_Mean(float *buffer, uint8_t n, uint8_t trim)
|
||||
{
|
||||
ADS1015_Sort_Array(buffer, n);
|
||||
|
||||
uint8_t start = trim;
|
||||
uint8_t end = n - trim; /* exclusive */
|
||||
|
||||
if (end <= start) /* guard against a misconfigured trim value */
|
||||
{
|
||||
start = 0;
|
||||
end = n;
|
||||
}
|
||||
|
||||
float sum = 0.0f;
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = start; i < end; i++)
|
||||
{
|
||||
sum += buffer[i];
|
||||
count++;
|
||||
}
|
||||
return sum / (float)count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple average of the history buffer (stage-2 sliding window)
|
||||
*/
|
||||
float ADS1015_History_Average(const float *hist, uint8_t n)
|
||||
{
|
||||
float sum = 0.0f;
|
||||
for (uint8_t i = 0; i < n; i++)
|
||||
sum += hist[i];
|
||||
return sum / (float)n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Common processing core: applied identically to all 3 channels.
|
||||
* Receives the current raw reading and the channel's state (global
|
||||
* arrays/struct).
|
||||
**/
|
||||
void ADS1015_Process_Sample(ADS1015_filter_t *ch, float raw)
|
||||
{
|
||||
/* --- Stage 1: accumulate into the current burst --- */
|
||||
ch->burst[ch->burst_index] = raw;
|
||||
ch->burst_index++;
|
||||
|
||||
if (ch->burst_index < ADS1015_BURST_SIZE)
|
||||
return; /* still collecting the burst, nothing else to do */
|
||||
|
||||
/* Burst complete: compute the trimmed mean */
|
||||
float burst_result = ADS1015_Trimmed_Mean(ch->burst, ADS1015_BURST_SIZE, ADS1015_TRIM_COUNT);
|
||||
ch->burst_index = 0; /* reset for the next burst */
|
||||
|
||||
/* --- Stage 2a: rolling history / sliding window --- */
|
||||
ch->history[ch->history_index] = burst_result;
|
||||
ch->history_index = (uint8_t)((ch->history_index + 1) % ADS1015_HISTORY_SIZE);
|
||||
if (ch->history_count < ADS1015_HISTORY_SIZE)
|
||||
ch->history_count++;
|
||||
|
||||
float window_average = ADS1015_History_Average(ch->history, ch->history_count);
|
||||
|
||||
/* --- Stage 2b: first-order IIR low-pass filter on the burst result --- */
|
||||
if (!ch->iir_initialized)
|
||||
{
|
||||
ch->iir_state = burst_result;
|
||||
ch->iir_initialized = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch->iir_state = ADS1015_IIR_ALPHA * burst_result + (1.0f - ADS1015_IIR_ALPHA) * ch->iir_state;
|
||||
}
|
||||
|
||||
/* Final output: combines the IIR state (fast response to real drift)
|
||||
* with the window average (more stable, slower response). Adjust
|
||||
* the weighting per channel if needed. */
|
||||
ch->filtered_value = 0.5f * ch->iir_state + 0.5f * window_average;
|
||||
ch->value_valid = 1;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* PUBLIC FUNCTIONS - ONE PER CHANNEL */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
|
||||
void ADS1015_pH_NewSample(float raw)
|
||||
{
|
||||
ADS1015_Process_Sample(&g_ph_filter, raw);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
float ADSCalculate_ph_mV(void) // pH in mV
|
||||
{
|
||||
float ph_dut_sum;
|
||||
@@ -279,7 +486,7 @@ float ADSCalculate_ph_mV(void) // pH in mV
|
||||
for (i = (ADS1015_PH_AVERAGES-1); i > 0; i--)
|
||||
ph_dut_samples[i] = ph_dut_samples[i-1];
|
||||
|
||||
ph_dut_samples[0] = (((float)(ADSreadADC_Differential_0_1(&i2c)) * ADS1015_ADC_VREF) / ADS1015_ADC_MAX); // 1. Converter leitura bruta do ADC para tensão real (mV)
|
||||
ph_dut_samples[0] = (((float)(ADSreadADC_Differential_0_1(&i2c_ads1015)) * ADS1015_ADC_VREF) / ADS1015_ADC_MAX); // 1. Converter leitura bruta do ADC para tensão real (mV)
|
||||
|
||||
for (i = 0, ph_dut_sum=0; i < ADS1015_PH_AVERAGES; i++)
|
||||
ph_dut_sum += ph_dut_samples[i];
|
||||
@@ -333,7 +540,7 @@ float ADSCalculate_ph_Uncompensated(void)
|
||||
* Note: The subtraction of 2048 assumes a differential configuration
|
||||
* where the midpoint is at the mid-scale of the ADS1015.
|
||||
*/
|
||||
float v_measured = ((float)(2048 - ADSreadADC_Differential_0_1(&i2c)) * ADS1015_ADC_VREF) / ADS1015_ADC_MAX;
|
||||
float v_measured = ((float)(ADSreadADC_Differential_0_1(&i2c_ads1015)) * ADS1015_ADC_VREF) / ADS1015_ADC_MAX;
|
||||
|
||||
/*
|
||||
* 2. Calculate the original Slope (measured during calibration).
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "main.h"
|
||||
#include "adc.h"
|
||||
#include "i2c.h"
|
||||
#include "tim.h"
|
||||
#include "usart.h"
|
||||
#include "gpio.h"
|
||||
|
||||
@@ -57,6 +58,8 @@
|
||||
|
||||
/* USER CODE BEGIN PV */
|
||||
|
||||
|
||||
|
||||
uint8_t rx_buffer[256]; /*!< Buffer for received data */
|
||||
uint8_t tx_data[] = "Hello RS-485 Broadcast!"; /*!< Data to send */
|
||||
|
||||
@@ -158,14 +161,21 @@ int main(void)
|
||||
MX_I2C1_Init();
|
||||
MX_I2C2_Init();
|
||||
MX_USART1_UART_Init();
|
||||
MX_TIM3_Init();
|
||||
/* USER CODE BEGIN 2 */
|
||||
|
||||
|
||||
|
||||
Flash_Load_Page(&flash_data);
|
||||
|
||||
|
||||
HAL_TIM_Base_Start_IT(&htim3);
|
||||
|
||||
//TempSensor_Init(&hadc1);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
HAL_TIM_Base_Start(&htim3);
|
||||
HAL_ADCEx_Calibration_Start(&hadc1);
|
||||
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)AD_RES, 2); // Internal Temperature conversion
|
||||
*/
|
||||
@@ -176,8 +186,12 @@ int main(void)
|
||||
rs485_init();
|
||||
|
||||
|
||||
ADS1015(&i2c, &hi2c1, ADS_ADDR_GND);
|
||||
ADSsetGain(&i2c, GAIN_FOUR);
|
||||
ADS1015(&i2c_ads1015, &hi2c1, ADS1015_ADDR_GND);
|
||||
//ADSsetGain(&i2c_ads1015, GAIN_FOUR);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ADG715_ResetChannels();
|
||||
// Start CE and RTD Measurement
|
||||
@@ -189,7 +203,7 @@ int main(void)
|
||||
|
||||
/* Infinite loop */
|
||||
/* USER CODE BEGIN WHILE */
|
||||
|
||||
/*
|
||||
for(i=0;i<AD5934_TEMP_AVERAGES;i++) // Loop to get stability and get averages
|
||||
{
|
||||
refResistance = AD5934_Get_Ref_Resistance(); // On Board Resistor
|
||||
@@ -265,14 +279,15 @@ int main(void)
|
||||
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
while (1)
|
||||
{
|
||||
|
||||
|
||||
AD5934_Process_System();
|
||||
ADS1015_Process_System();
|
||||
|
||||
|
||||
current_millis = HAL_GetTick();
|
||||
current_millis = g_ms_counter;
|
||||
|
||||
|
||||
// Piscar LED verde em PB5 a cada 0,5 segundos
|
||||
@@ -289,8 +304,19 @@ int main(void)
|
||||
uint8_t led_data[2] = {0x01, current_green_state}; // Command 0x01 for green LED
|
||||
|
||||
}
|
||||
/*
|
||||
status1 = rs485_send_broadcast(ph_t, strlen((char*)ph_t));
|
||||
|
||||
|
||||
ph_compensated = ADSCalculate_ph_mV() ;
|
||||
|
||||
|
||||
FloatToString(tempString, ph_compensated);
|
||||
status0 = rs485_send_broadcast(tempString, (strlen((char*)tempString)-1));
|
||||
status1 = rs485_send_broadcast(newline_ph, strlen((char*)newline_ph));
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
// Piscar LED vermelho em PB4 a cada 1 segundo
|
||||
@@ -308,26 +334,44 @@ int main(void)
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (g_rtd_filter.value_valid)
|
||||
{
|
||||
float rtd_final = g_rtd_filter.filtered_value;
|
||||
|
||||
status1 = rs485_send_broadcast(tm_t, strlen((char*)tm_t));
|
||||
temperature_RTD = AD5934_GetTemperature(refResistance);
|
||||
FloatToString(tempString, temperature_RTD);
|
||||
//temperature_RTD = AD5934_GetTemperature(refResistance);
|
||||
FloatToString(tempString, rtd_final);
|
||||
status0 = rs485_send_broadcast(tempString, (strlen((char*)tempString)-1));
|
||||
status1 = rs485_send_broadcast(newline_temp, strlen((char*)newline_temp));
|
||||
}
|
||||
|
||||
if (g_ec_filter.value_valid)
|
||||
{
|
||||
float ec_final = g_ec_filter.filtered_value;
|
||||
status1 = rs485_send_broadcast(ad_t, strlen((char*)ad_t));
|
||||
admittance_EC = AD5934_GetImpedance(temperature_RTD);
|
||||
FloatToString(tempString, admittance_EC);
|
||||
//admittance_EC = AD5934_GetImpedance(temperature_RTD);
|
||||
FloatToString(tempString, ec_final);
|
||||
status0 = rs485_send_broadcast(tempString, (strlen((char*)tempString)-1));
|
||||
status1 = rs485_send_broadcast(newline_admi, strlen((char*)newline_admi));
|
||||
}
|
||||
|
||||
if (g_ref_filter.value_valid)
|
||||
{
|
||||
//float reference_final = g_ref_filter.filtered_value;
|
||||
status1 = rs485_send_broadcast(ph_t, strlen((char*)ph_t));
|
||||
ph_compensated = 10*ADSCalculate_ph_mV();//ADSCalculate_ph_Compensated(temperature_RTD);
|
||||
ph_compensated = g_ph_filter.filtered_value;
|
||||
FloatToString(tempString, ph_compensated);
|
||||
|
||||
status0 = rs485_send_broadcast(tempString, (strlen((char*)tempString)-1));
|
||||
status1 = rs485_send_broadcast(newline_ph, strlen((char*)newline_ph));
|
||||
|
||||
status1 = rs485_send_broadcast(newline, strlen((char*)newline));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -524,6 +568,18 @@ void intToStr(int N, char *str) {
|
||||
}
|
||||
}
|
||||
|
||||
/* TIM3 Timer Interrupt */
|
||||
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
|
||||
{
|
||||
if (htim->Instance == TIM3)
|
||||
{
|
||||
g_ms_counter++; /* Relógio global do sistema */
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
/* USER CODE END 0 */
|
||||
|
||||
/* External variables --------------------------------------------------------*/
|
||||
extern TIM_HandleTypeDef htim3;
|
||||
extern UART_HandleTypeDef huart1;
|
||||
/* USER CODE BEGIN EV */
|
||||
|
||||
@@ -198,6 +199,20 @@ void SysTick_Handler(void)
|
||||
/* please refer to the startup file (startup_stm32f1xx.s). */
|
||||
/******************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief This function handles TIM3 global interrupt.
|
||||
*/
|
||||
void TIM3_IRQHandler(void)
|
||||
{
|
||||
/* USER CODE BEGIN TIM3_IRQn 0 */
|
||||
|
||||
/* USER CODE END TIM3_IRQn 0 */
|
||||
HAL_TIM_IRQHandler(&htim3);
|
||||
/* USER CODE BEGIN TIM3_IRQn 1 */
|
||||
|
||||
/* USER CODE END TIM3_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles USART1 global interrupt.
|
||||
*/
|
||||
|
||||
@@ -41,9 +41,9 @@ void MX_TIM3_Init(void)
|
||||
|
||||
/* USER CODE END TIM3_Init 1 */
|
||||
htim3.Instance = TIM3;
|
||||
htim3.Init.Prescaler = 3;
|
||||
htim3.Init.Prescaler = 2;
|
||||
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
htim3.Init.Period = 59999;
|
||||
htim3.Init.Period = 999;
|
||||
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
|
||||
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
|
||||
@@ -55,7 +55,7 @@ void MX_TIM3_Init(void)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
||||
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
||||
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
|
||||
{
|
||||
@@ -77,6 +77,10 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
|
||||
/* USER CODE END TIM3_MspInit 0 */
|
||||
/* TIM3 clock enable */
|
||||
__HAL_RCC_TIM3_CLK_ENABLE();
|
||||
|
||||
/* TIM3 interrupt Init */
|
||||
HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
|
||||
HAL_NVIC_EnableIRQ(TIM3_IRQn);
|
||||
/* USER CODE BEGIN TIM3_MspInit 1 */
|
||||
|
||||
/* USER CODE END TIM3_MspInit 1 */
|
||||
@@ -93,6 +97,9 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
|
||||
/* USER CODE END TIM3_MspDeInit 0 */
|
||||
/* Peripheral clock disable */
|
||||
__HAL_RCC_TIM3_CLK_DISABLE();
|
||||
|
||||
/* TIM3 interrupt Deinit */
|
||||
HAL_NVIC_DisableIRQ(TIM3_IRQn);
|
||||
/* USER CODE BEGIN TIM3_MspDeInit 1 */
|
||||
|
||||
/* USER CODE END TIM3_MspDeInit 1 */
|
||||
|
||||
Reference in New Issue
Block a user