/** ****************************************************************************** * @file : adc_interface.c * @brief : Interface for setting and getting information read in from the ADC ****************************************************************************** */ #include "adc_interface.h" #include "battery.h" #include "stm32g0xx_hal_flash.h" #include "task.h" #include "printf.h" #include "string.h" extern ADC_HandleTypeDef hadc1; /* Private typedef -----------------------------------------------------------*/ struct Adc { uint32_t bat_voltage; uint32_t cell_voltage[4]; uint32_t vrefint; uint32_t vdda; int32_t temperature; uint32_t two_s_battery_voltage; uint32_t three_s_battery_voltage; uint32_t four_s_battery_voltage; }; /* Private variables ---------------------------------------------------------*/ struct Adc adc_values; uint32_t adc_buffer[7]; static volatile uint32_t adc_scalars[SCALAR_ARRAY_SIZE], adc_offset[SCALAR_ARRAY_SIZE], adc_buffer_filtered[7], adc_filtered_output[7]; static volatile uint32_t adc_sum_count; static volatile uint16_t vrefint_cal; static volatile uint8_t cal_present; /* Private function prototypes -----------------------------------------------*/ uint8_t Set_Battery_Voltage(uint32_t adc_reading); uint8_t Set_Cell_Voltage(uint8_t cell_number, uint32_t adc_reading); uint8_t Set_MCU_Temperature(uint32_t adc_reading); uint8_t Set_VDDa(uint32_t adc_reading); uint8_t Read_Scalars_From_Flash(void); /** * @brief Gets the battery voltage that was read in from the ADC * @retval Battery voltage in volts * BATTERY_ADC_MULTIPLIER */ uint32_t Get_Battery_Voltage(void) { return adc_values.bat_voltage; } /** * @brief Sets the battery voltage that was read in from the ADC * @param adc_reading: Raw reading from ADC * @retval uint8_t 1 if successful, 0 if error */ uint8_t Set_Battery_Voltage(uint32_t adc_reading) { if ((adc_reading < BATTERY_MIN_ADC_READING) || (adc_reading > 4095)) { adc_values.bat_voltage = 0; return 0; } adc_values.bat_voltage = adc_reading * adc_scalars[0]; return 1; } /** * @brief Gets cell X voltage that was read in from the ADC * @param cell_number: Cell number 0-3 to get voltage * @retval Cell 1 voltage in volts * BATTERY_ADC_MULTIPLIER */ uint32_t Get_Cell_Voltage(uint8_t cell_number) { if (cell_number > 3) { return UINT32_MAX; } return adc_values.cell_voltage[cell_number]; } /** * @brief Sets the cell 1 voltage that was read in from the ADC * @param cell_number: Cell number 0-3 to set voltage * @param adc_reading: Raw reading from ADC * @retval uint8_t 1 if successful, 0 if error */ uint8_t Set_Cell_Voltage(uint8_t cell_number, uint32_t adc_reading) { if (cell_number > 3) { return 0; } if (cell_number == 0) { if ((adc_reading < CELL_ONE_MIN_ADC_READING) || (adc_reading > 4095)) { adc_values.cell_voltage[0] = 0; return 0; } else { adc_values.cell_voltage[0] = adc_reading * adc_scalars[1]; if (adc_values.cell_voltage[0] > CELL_MAX_VOLTAGE) { adc_values.cell_voltage[0] = 0; return 0; } } } if (cell_number == 1) { if ((adc_reading < CELL_TWO_MIN_ADC_READING) || (adc_reading > 4095)) { adc_values.cell_voltage[1] = 0; adc_values.two_s_battery_voltage = 0; return 0; } else { adc_values.two_s_battery_voltage = adc_reading * adc_scalars[2]; if (adc_values.two_s_battery_voltage > TWO_S_MAX_VOLTAGE) { adc_values.two_s_battery_voltage = 0; return 0; } if ( adc_values.two_s_battery_voltage > adc_values.cell_voltage[0] ) { adc_values.cell_voltage[1] = adc_values.two_s_battery_voltage - adc_values.cell_voltage[0]; } else { adc_values.cell_voltage[1] = 0; return 0; } } } if (cell_number == 2) { if ((adc_reading < CELL_THREE_MIN_ADC_READING) || (adc_reading > 4095)) { adc_values.cell_voltage[2] = 0; return 0; } else { adc_values.three_s_battery_voltage = adc_reading * adc_scalars[3]; if (adc_values.three_s_battery_voltage > THREE_S_MAX_VOLTAGE) { adc_values.three_s_battery_voltage = 0; return 0; } if ( adc_values.three_s_battery_voltage > adc_values.two_s_battery_voltage ) { adc_values.cell_voltage[2] = adc_values.three_s_battery_voltage - adc_values.two_s_battery_voltage; } else { adc_values.cell_voltage[2] = 0; return 0; } } } if (cell_number == 3) { if ((adc_reading < CELL_FOUR_MIN_ADC_READING) || (adc_reading > 4095)) { adc_values.cell_voltage[3] = 0; return 0; } else { adc_values.four_s_battery_voltage = adc_reading * adc_scalars[4]; if (adc_values.four_s_battery_voltage > FOUR_S_MAX_VOLTAGE) { adc_values.four_s_battery_voltage = 0; return 0; } if ( adc_values.four_s_battery_voltage > adc_values.three_s_battery_voltage ) { adc_values.cell_voltage[3] = adc_values.four_s_battery_voltage - adc_values.three_s_battery_voltage; } else { adc_values.cell_voltage[3] = 0; return 0; } } } return 1; } /** * @brief Gets mcu junction temperature that was read in from the ADC * @retval MCU junction temperature in celcius */ int32_t Get_MCU_Temperature(void) { return adc_values.temperature; } /** * @brief Sets the mcu junction temperature that was read in from the ADC * @param temperature_c: MCU junction temperature in celcius * @retval uint8_t 1 if successful, 0 if error */ uint8_t Set_MCU_Temperature(uint32_t adc_reading) { if ((adc_reading < 0) || (adc_reading > 4095)) { return 0; } else { adc_values.vrefint = __HAL_ADC_CALC_VREFANALOG_VOLTAGE(adc_filtered_output[6], ADC_RESOLUTION_12B); adc_values.temperature = __HAL_ADC_CALC_TEMPERATURE(adc_values.vrefint, adc_reading, ADC_RESOLUTION_12B); } return 1; } /** * @brief Gets the value of VDDa read in from the ADC * @retval VDDa in volts * BATTERY_ADC_MULTIPLIER */ uint32_t Get_VDDa() { return adc_values.vdda; } /** * @brief Sets the value of VDDa read in from the ADC * @param adc_reading: Raw reading from ADC * @retval uint8_t 1 if successful, 0 if error */ uint8_t Set_VDDa(uint32_t adc_reading) { if ((adc_reading < 0) || (adc_reading > 4095)) { return 0; } else { adc_values.vdda = 3 * ((vrefint_cal * BATTERY_ADC_MULTIPLIER) / adc_reading); } return 1; } /** * @brief Calculates and sets the ADC scalars based on a reference voltage input * @param reference_voltage: Reference voltage in milivolts * @retval uint8_t 1 if successful, 0 if error */ uint8_t Calibrate_ADC(float reference_voltage_mv) { if (reference_voltage_mv > 4200.0f) { return 0; } printf("Input Reference Voltage in mv: %.3f\r\n", reference_voltage_mv); if (reference_voltage_mv < 0.01f) { for (int i = 0; i < 5; i++) { adc_offset[i] = adc_filtered_output[i]; printf("ADC Channel %u offset: %u\r\n", i, adc_offset[i]); } } else { for (int i = 0; i < 5; i++) { float scale = (reference_voltage_mv * BATTERY_ADC_MULTIPLIER) / (adc_filtered_output[i] * 1000); adc_scalars[i] = (uint32_t)scale; printf("ADC Channel %u scalar: %u\r\n", i, adc_scalars[i]); } } return 1; } void vRead_ADC(void const *pvParameters) { // calibrate ADC vTaskDelay(500 / portTICK_PERIOD_MS); while (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK); vTaskDelay(500 / portTICK_PERIOD_MS); vrefint_cal = (uint32_t)(*VREFINT_CAL_ADDR); // VREFINT calibration value //Read the scalars out of OTP flash Read_Scalars_From_Flash(); adc_sum_count = 0; static uint32_t thread_notification; const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); // Start the DMA ADC HAL_ADC_Start_DMA(&hadc1, adc_buffer, hadc1.Init.NbrOfConversion); for (;;) { /* Wait to be notified of an interrupt. */ thread_notification = ulTaskNotifyTake(pdTRUE, xMaxBlockTime); if (thread_notification) { /* A notification was received. */ Set_Battery_Voltage(adc_filtered_output[0]); for (int i = 0; i < 4; i++) { Set_Cell_Voltage(i, adc_filtered_output[i+1]); } Set_MCU_Temperature(adc_filtered_output[5]); Set_VDDa(adc_filtered_output[6]); /* Determines battery connection state and performs balancing */ Battery_Connection_State(); } else { /* Did not receive a notification within the expected time. */ printf("Did Not Receive an ADC Notification\r\n"); } } } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { /* tCONV = Sampling time + 12.5 x ADC clock cycles For 160 sample time and 16MHz clock divided by 4 tCONV = (160 + 12.5) x 1/(16MHz/4) = 43.125us For 6 reads = 258.75us or 3.864kHz */ unsigned length = sizeof(adc_buffer_filtered)/sizeof(adc_buffer_filtered[0]); for(unsigned i = 0; i < length; i++) { adc_buffer_filtered[i] += adc_buffer[i]; } adc_sum_count++; if (adc_sum_count == ADC_FILTER_SUM_COUNT) { length = sizeof(adc_filtered_output)/sizeof(adc_filtered_output[0]); for(unsigned i = 0; i < length; i++) { adc_filtered_output[i] = ( adc_buffer_filtered[i] / ADC_FILTER_SUM_COUNT ); } adc_sum_count = 0; // Clear the buffer memset((uint32_t *)adc_buffer_filtered, 0, sizeof(adc_buffer_filtered)); BaseType_t should_context_switch = pdFALSE; vTaskNotifyGiveFromISR(adcTaskHandle, &should_context_switch); portYIELD_FROM_ISR(should_context_switch); } } uint32_t Get_Two_S_Voltage() { return adc_values.two_s_battery_voltage; } uint32_t Get_Three_S_Voltage() { return adc_values.three_s_battery_voltage; } uint32_t Get_Four_S_Voltage() { return adc_values.four_s_battery_voltage; } /** * @brief Writes the cal result to OTP Flash * @retval uint8_t 1 if successful, 0 if error */ uint8_t Write_Cal_To_OTP_Flash() { HAL_StatusTypeDef status; /* Check input parameters */ for (int i = 0; i < SCALAR_ARRAY_SIZE; i++) { if ((adc_scalars[i] < 750) || (adc_scalars[i] > 5000)) { printf("ERROR: ADC Scalar %u Not Set or Out of Range\r\n", i); /* Return error */ return 1; } } if (HAL_FLASH_Unlock() != HAL_OK) { printf("ERROR: Could not unlock Flash\r\n"); return 1; } uint32_t address = OTP_START_ADDR; uint32_t temp_address = OTP_START_ADDR; for (int i = 0; i < OTP_SIZE; i++) { for (int x = 0; x < SCALAR_ARRAY_SIZE; x++) { uint32_t value = *(uint32_t *)(temp_address + (i * BYTES_IN_UINT64) + (x * BYTES_IN_UINT32)); if ((value > 750) && (value < 5000)) { printf("OTP Memory Value: %u\r\n", value); address = temp_address + ((i + 1) * BYTES_IN_UINT64); } else { break; } } } printf("OPT Scalar Start Address: 0x%08x\r\n", address); uint64_t data_in_64; //Write Calibration Values for (int i = 0; i < 3; i++) { /* Write double */ if (i < 2) { data_in_64 = adc_scalars[i*2] | (uint64_t)adc_scalars[(i*2)+1] << 32; } else { uint32_t mask = 0xFFFFFFFF; data_in_64 = adc_scalars[i*2] | (uint64_t)mask << 32; } printf("Writing 0x%016llx to address: 0x%08x\r\n", (uint64_t)data_in_64, (uint32_t)(address + (i * BYTES_IN_UINT64))); status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (address + (i * BYTES_IN_UINT64)), data_in_64); if (status != HAL_OK) { printf("ERROR: Write scalar #%u to OTP Flash Failed\r\n", i); if (HAL_FLASH_Lock() != HAL_OK) { printf("ERROR: Could not lock Flash\r\n"); return 1; } /* Return error */ return 1; } } if (HAL_FLASH_Lock() != HAL_OK) { printf("ERROR: Could not lock Flash\r\n"); return 1; } return 0; } /** * @brief Read the cal values from OTP Flash * @retval uint8_t 1 if successful, 0 if error */ uint8_t Read_Scalars_From_Flash() { uint32_t address = OTP_START_ADDR; uint32_t temp_scalars[SCALAR_ARRAY_SIZE] = {0}; uint8_t t = SCALAR_ARRAY_SIZE - 1; for (int i = OTP_SIZE; i >= 0; i--) { uint32_t value = *(uint32_t *)(address + (i * BYTES_IN_UINT32)); if ((value > 750) && (value < 5000)) { printf("OTP Value %u at address: 0x%08x\r\n", value, (uint32_t)(address + (i * BYTES_IN_UINT32))); if (cal_present == 0) { temp_scalars[t] = value; } if(t == 0) { cal_present = cal_present + 1; t = SCALAR_ARRAY_SIZE; } t--; } } if (cal_present != 0) { for (int y = 0; y < SCALAR_ARRAY_SIZE; y++) { adc_scalars[y] = temp_scalars[y]; } printf("Calibration values already present. 32 total calibrations can be performed. Number of calibrations performed: %u\r\n", cal_present); printf("Using these calibration values:\r\n"); for (int i = 0; i < SCALAR_ARRAY_SIZE; i++) { printf("Scalar: %u Value: %u\r\n", i, adc_scalars[i]); } return 0; } cal_present = 0; printf("NOT CALIBRATED. Connect known good voltage to cells 1-4 and XT60 in the range of 3.3V - 4V and run cal command.\r\nThen write then to flash with write_otp\r\n"); return 1; }