2024-06-30 04:23 PM
I'm developing a vibration and temperature sensor solution for industrial machinery.
Previously, my system was equipped with Atmega 328p chips, but it has now been upgraded to an STM32LO53C8T8 chip.
The circuit has a LoRa radio for sending signals to the gateway, a KX122-1037 accelerometer, a MCP9808 temperature sensor, a MAX17048 battery sensor and a MB85RC FRAM. I connected the TX pin to the arduino Mega to get a DEBUG display on the arduino IDE's serial monitor.
I assembled a PCB with the wiring diagram in the attached image, and it worked well for programming the microcontroller. However, I started to experience some problems when executing the code.
Full Codes: https://gist.github.com/MatheusMarkies/a85b8288ed40ea9962f3d91e22202695
#include "stm32l0xx_hal.h" #include "math.h" #include "string.h" #include "stdio.h" #include "LORA.h" #include "FRAM.h" #include "KX122.h" #include "MCP9808.h" #include "MAX17048.h" #include "BrasensFirmware.h" #define SENSOR_KEY "1111A" TIM_HandleTypeDef htim2; #define DEBUG_PRINT(msg) HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY) #define min(a, b) ((a) < (b) ? (a) : (b)) Transmission_Data data; Transmission_VibrationPackage vibrationPackage; unsigned int sampling_period_us; unsigned long data_sender_period; int package_factor = 0; int acc_sample_factor = 0; FRAM_Metadata metadata; uint32_t sizeInBytes = 0; /* USER CODE END PV */ int main(void) { TIM2_Init(); setup(); while (1) { loop(); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = { 0 }; RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 }; RCC_PeriphCLKInitTypeDef PeriphClkInit = { 0 }; /** Configure the main internal regulator output voltage */ __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI; RCC_OscInitStruct.MSIState = RCC_MSI_ON; RCC_OscInitStruct.MSICalibrationValue = 0; RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1 | RCC_PERIPHCLK_I2C1; PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } void TIM2_Init(void) { __HAL_RCC_TIM2_CLK_ENABLE(); htim2.Instance = TIM2; htim2.Init.Prescaler = (uint32_t)(HAL_RCC_GetPCLK1Freq() / 1000000) - 1; // 1 MHz htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 0xFFFFFFFF; // Máximo período htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } if (HAL_TIM_Base_Start(&htim2) != HAL_OK) { Error_Handler(); } } uint32_t micros() { return __HAL_TIM_GET_COUNTER(&htim2); } void delayMicroseconds(uint32_t us) { uint32_t start = micros(); uint32_t target = start + us; if (target < start) { // Overflow do contador while (micros() >= start) { // Espera até o overflow ocorrer } } while (micros() < target) { // Espera até atingir o tempo desejado } } uint16_t address = 0; unsigned long writeMicroseconds; int current_sample = 0; void writeVibrationInformation() { data.rms_accel[0] = 0.0; data.rms_accel[1] = 0.0; data.rms_accel[2] = 0.0; data.rms_vel[0] = 0.0; data.rms_vel[1] = 0.0; data.rms_vel[2] = 0.0; float velocity_x = 0.0; float velocity_y = 0.0; float velocity_z = 0.0; int d = 0; int count = 0; DEBUG_PRINT("read...\r\n"); //FOR for (int i = 0; i < SAMPLES; i++) { Vibration vibration = KX122_ReadAccelData(&hi2c1); count++; FRAM_WriteFloat(&hi2c1, &metadata, address + SAMPLES * 0 * sizeof(float), vibration.x); FRAM_WriteFloat(&hi2c1, &metadata, address + SAMPLES * 1 * sizeof(float), vibration.y); FRAM_WriteFloat(&hi2c1, &metadata, address + SAMPLES * 2 * sizeof(float), vibration.z); address += sizeof(float); d++; count = 0; data.rms_accel[0] += (vibration.x * vibration.x); data.rms_accel[1] += (vibration.y * vibration.y); data.rms_accel[2] += (vibration.z * vibration.z); float deltaTime = sampling_period_us / 1e6; velocity_x += vibration.x * deltaTime; //Ax * dT velocity_y += vibration.y * deltaTime; //Ay * dT velocity_z += vibration.z * deltaTime; //Az * dT data.rms_vel[0] += (velocity_x * velocity_x); data.rms_vel[1] += (velocity_y * velocity_y); data.rms_vel[2] += (velocity_z * velocity_z); delayMicroseconds(sampling_period_us); } data.rms_accel[0] = sqrt(data.rms_accel[0] / SAMPLES); data.rms_accel[1] = sqrt(data.rms_accel[1] / SAMPLES); data.rms_accel[2] = sqrt(data.rms_accel[2] / SAMPLES); data.rms_vel[0] = sqrt(data.rms_vel[0] / SAMPLES); data.rms_vel[1] = sqrt(data.rms_vel[1] / SAMPLES); data.rms_vel[2] = sqrt(data.rms_vel[2] / SAMPLES); char buffer[256]; snprintf(buffer, sizeof(buffer), "RMS Accel: %.2f, %.2f, %.2f\r\nRMS Vel: %.2f, %.2f, %.2f\r\n", data.rms_accel[0], data.rms_accel[1], data.rms_accel[2], data.rms_vel[0], data.rms_vel[1], data.rms_vel[2]); DEBUG_PRINT(buffer); } HAL_StatusTypeDef MCP9808_ReadTemperature_LowPower(double *temperature) { HAL_StatusTypeDef status; status = MCP9808_Wake(&hi2c1); if (status != HAL_OK) { return status; } HAL_Delay(50); status = MCP9808_ReadTemperature(&hi2c1, temperature); char buffer[256]; snprintf(buffer, sizeof(buffer), "Temp: %d \r\n", *temperature); DEBUG_PRINT(buffer); if (status != HAL_OK) { return status; } status = MCP9808_Shutdown(&hi2c1); return status; } void readBatteryData(uint16_t* soc) { uint16_t voltage; char msg[64]; if (MAX17048_ReadVoltage(&hi2c1, &voltage) == HAL_OK) { snprintf(msg, sizeof(msg), "Voltage: %u mV\r\n", voltage); DEBUG_PRINT(msg); } else { DEBUG_PRINT("Failed to read voltage\r\n"); } if (MAX17048_ReadSOC(&hi2c1, soc) == HAL_OK) { snprintf(msg, sizeof(msg), "State of Charge: %u%%\r\n", *soc); DEBUG_PRINT(msg); } else { DEBUG_PRINT("Failed to read SOC\r\n"); } } void I2C_Scan() { char msg[64]; HAL_StatusTypeDef result; uint8_t i; DEBUG_PRINT("Scanning I2C bus:\r\n"); for (i = 1; i < 128; i++) { result = HAL_I2C_IsDeviceReady(&hi2c1, (uint16_t)(i << 1), 1, 10); if (result == HAL_OK) { snprintf(msg, sizeof(msg), "Device found at 0x%02X\r\n", i); DEBUG_PRINT(msg); } else { } } DEBUG_PRINT("I2C scan completed.\r\n"); } void setup() { DEBUG_PRINT("Starting...\r\n"); I2C_Scan(); if (KX122_Init(&hi2c1) != HAL_ERROR) DEBUG_PRINT("Ready KX122\r\n"); else DEBUG_PRINT("Error in KX122 connection\r\n"); if (MCP9808_Init(&hi2c1) != HAL_ERROR) DEBUG_PRINT("Ready MCP9808\r\n"); else DEBUG_PRINT("Error in MCP9808 connection\r\n"); if (MAX17048_Init(&hi2c1) != HAL_ERROR) DEBUG_PRINT("Ready MAX17048\r\n"); else DEBUG_PRINT("Error in MAX17048 connection\r\n"); if (FRAM_Init(&hi2c1) != HAL_ERROR) DEBUG_PRINT("Ready FRAM\r\n"); else DEBUG_PRINT("Error in FRAM connection\r\n"); DEBUG_PRINT("Ready!\r\n"); FRAM_InitMetadata(&metadata); char buffer[50]; snprintf(buffer, sizeof(buffer), "Next Memory Adds: %d\r\n", metadata.nextFreeAddress); DEBUG_PRINT(buffer); package_factor = 3 * ceil((float)(SAMPLES) / (float)TRANSMISSION_DATA_PACKAGE); data_sender_period = round(1000 * ((float)DATA_TRANSMISSION_PERIOD / (float)package_factor)); sampling_period_us = round(1000000 * (SAMPLES / (float)ACC_DATA_RATE)); acc_sample_factor = floor((float)ACC_DATA_RATE / (float)SAMPLES); sizeInBytes = FRAM_MEMORY_SIZE; FRAM_Format(&hi2c1, &metadata); DEBUG_PRINT("Starting Temperature Reading:\r\n"); double temp; MCP9808_ReadTemperature_LowPower(&temp); char bufferTemp[50]; snprintf(bufferTemp, sizeof(bufferTemp), "Temp: %.2f\r\n", temp); // Usar %.2f para imprimir double DEBUG_PRINT(bufferTemp); DEBUG_PRINT("Starting Battery Reading:\r\n"); int16_t bat = 0; readBatteryData(&bat); char bufferBat[50]; snprintf(bufferBat, sizeof(bufferBat), "Bat: %d\r\n", bat); DEBUG_PRINT(bufferBat); Vibration vibration = KX122_ReadAccelData(&hi2c1); char bufferVib[50]; snprintf(bufferVib, sizeof(bufferVib), "RMS Accel: %.2f, %.2f, %.2f\r\n", vibration.x, vibration.y, vibration.z); // Usar %.2f para imprimir double DEBUG_PRINT(bufferVib); // Teste de escrita e leitura da FRAM float test_value = 123.456f; float read_value = 0.0f; uint16_t test_address = 0x0000; if (FRAM_WriteFloat(&hi2c1, &metadata, test_address, test_value) == HAL_OK) { DEBUG_PRINT("FRAM write success\r\n"); if (FRAM_ReadFloat(&hi2c1, test_address, &read_value) == HAL_OK) { char buffer[100]; snprintf(buffer, sizeof(buffer), "FRAM read success, value: %.3f\r\n", read_value); DEBUG_PRINT(buffer); if (fabs(test_value - read_value) < 0.001f) { DEBUG_PRINT("FRAM test passed\r\n"); } else { DEBUG_PRINT("FRAM test failed: values do not match\r\n"); } } else { DEBUG_PRINT("FRAM read failed\r\n"); } } else { DEBUG_PRINT("FRAM write failed\r\n"); } DEBUG_PRINT("Testando delayMicroseconds...\r\n"); uint32_t start_test = micros(); delayMicroseconds(1000000); // 1 segundo uint32_t end_test = micros(); char bufferDelay[50]; snprintf(bufferDelay, sizeof(bufferDelay), "Atraso: %lu us\r\n", end_test - start_test); DEBUG_PRINT(bufferDelay); DEBUG_PRINT("Starting Vibration Reading:\r\n"); writeVibrationInformation(); } unsigned long readMilliseconds; bool energy_save = false; float temperatureValue; int current_package = 0; int reset_counter = 0; bool sendDataDelay = false; int axis = 0; void readAndSendFRAMData() { DEBUG_PRINT("readAndSendFRAMData...\r\n"); if (energy_save) { readMilliseconds = millis(); sendDataDelay = true; energy_save = false; } else if (millis() > (readMilliseconds + data_sender_period)) { sendDataDelay = true; } if (current_package >= (2 * package_factor / 3)) axis = 2; else if (current_package >= (package_factor / 3)) axis = 1; else axis = 0; if (sendDataDelay) { int start = TRANSMISSION_DATA_PACKAGE * current_package - TRANSMISSION_DATA_PACKAGE * axis * package_factor / 3; int end = (start + TRANSMISSION_DATA_PACKAGE); if (current_package > 0) start += 1; for (int i = start; i <= end; i++) { uint16_t address = i * sizeof(float) + SAMPLES * axis * sizeof(float); float value = 0.0; if (SAMPLES >= i) { FRAM_ReadFloat(&hi2c1, address, &value); } vibrationPackage.dataPackage[i - start] = value; } strncpy(vibrationPackage.key, SENSOR_KEY, sizeof(vibrationPackage.key)); vibrationPackage.type = 'P'; vibrationPackage.start = start; vibrationPackage.end = min(end, SAMPLES); vibrationPackage.axis = axis; LoRa_Idle(&hspi1); sendVibrationPackage(vibrationPackage); readMilliseconds = millis(); sendDataDelay = false; //if (report) { //temperatureValue += readTemperature() / package_factor; current_package++; //reset_counter = 0; LoRa_Sleep(&hspi1); energy_save = true; } } void loop() { DEBUG_PRINT("loop...\r\n"); readAndSendFRAMData(); if (current_package >= package_factor) { writeVibrationInformation(); temperatureValue = 0; MCP9808_ReadTemperature_LowPower( &temperatureValue); data.temperature = temperatureValue; strncpy(data.key, SENSOR_KEY, sizeof(data.key)); data.type = 'D'; uint16_t soc = 0; readBatteryData(&soc); // Passing the address of soc data.battery = soc; sendData(data); current_package = 0; } } void sendVibrationPackage(Transmission_VibrationPackage sendingVibrationPackage) { LoRa_Idle(&hspi1); uint8_t buffer[sizeof(Transmission_VibrationPackage)]; memcpy(buffer, &sendingVibrationPackage, sizeof(Transmission_VibrationPackage)); LoRa_Transmit(&hspi1, buffer, sizeof(Transmission_VibrationPackage)); LoRa_Sleep(&hspi1); } void sendData(Transmission_Data sendingData) { LoRa_Idle(&hspi1); uint8_t buffer[sizeof(Transmission_Data)]; memcpy(buffer, &sendingData, sizeof(Transmission_Data)); LoRa_Transmit(&hspi1, buffer, sizeof(Transmission_Data)); LoRa_Sleep(&hspi1); }
Here I take the accelerometer reading:
void writeVibrationInformation() { data.rms_accel[0] = 0.0; data.rms_accel[1] = 0.0; data.rms_accel[2] = 0.0; data.rms_vel[0] = 0.0; data.rms_vel[1] = 0.0; data.rms_vel[2] = 0.0; float velocity_x = 0.0; float velocity_y = 0.0; float velocity_z = 0.0; int d = 0; int count = 0; DEBUG_PRINT("read...\r\n"); //FOR for (int i = 0; i < SAMPLES; i++) { Vibration vibration = KX122_ReadAccelData(&hi2c1); count++; FRAM_WriteFloat(&hi2c1, &metadata, address + SAMPLES * 0 * sizeof(float), vibration.x); FRAM_WriteFloat(&hi2c1, &metadata, address + SAMPLES * 1 * sizeof(float), vibration.y); FRAM_WriteFloat(&hi2c1, &metadata, address + SAMPLES * 2 * sizeof(float), vibration.z); address += sizeof(float); d++; count = 0; data.rms_accel[0] += (vibration.x * vibration.x); data.rms_accel[1] += (vibration.y * vibration.y); data.rms_accel[2] += (vibration.z * vibration.z); float deltaTime = sampling_period_us / 1e6; velocity_x += vibration.x * deltaTime; //Ax * dT velocity_y += vibration.y * deltaTime; //Ay * dT velocity_z += vibration.z * deltaTime; //Az * dT data.rms_vel[0] += (velocity_x * velocity_x); data.rms_vel[1] += (velocity_y * velocity_y); data.rms_vel[2] += (velocity_z * velocity_z); delayMicroseconds(sampling_period_us); } data.rms_accel[0] = sqrt(data.rms_accel[0] / SAMPLES); data.rms_accel[1] = sqrt(data.rms_accel[1] / SAMPLES); data.rms_accel[2] = sqrt(data.rms_accel[2] / SAMPLES); data.rms_vel[0] = sqrt(data.rms_vel[0] / SAMPLES); data.rms_vel[1] = sqrt(data.rms_vel[1] / SAMPLES); data.rms_vel[2] = sqrt(data.rms_vel[2] / SAMPLES); char buffer[256]; snprintf(buffer, sizeof(buffer), "RMS Accel: %.2f, %.2f, %.2f\r\nRMS Vel: %.2f, %.2f, %.2f\r\n", data.rms_accel[0], data.rms_accel[1], data.rms_accel[2], data.rms_vel[0], data.rms_vel[1], data.rms_vel[2]); DEBUG_PRINT(buffer); }
In this part I run a test in the loop with debug and delay:
for (int i = 0; i < SAMPLES; i++) { char bufferDelay[100]; snprintf(bufferDelay, sizeof(bufferDelay), "Iteracao: %d, Atraso: %d us\r\n", i, sampling_period_us); DEBUG_PRINT(bufferDelay); // Log antes do delay DEBUG_PRINT("Antes do delay...\r\n"); uint32_t start_test = micros(); delayMicroseconds(sampling_period_us); uint32_t end_test = micros(); DEBUG_PRINT("Depois do delay...\r\n"); snprintf(bufferDelay, sizeof(bufferDelay), "Delay real: %lu us\r\n", end_test - start_test); DEBUG_PRINT(bufferDelay); // Log para verificar se a iteração foi completada snprintf(bufferDelay, sizeof(bufferDelay), "Iteracao %d completada\r\n", i); DEBUG_PRINT(bufferDelay); }
I've tried removing the reading from the accelerometer and testing it. My code runs in the while loop, and it also hangs after a short time.
2024-06-30 04:39 PM
Do some real debugging so you understand where it "hangs"
while(1); loops in Error_Handler() and HardFault_Handler() can be particularly silent and unhelpful unless changed to something more useful.
Your approach to the micro second delay is also potentially broken. You have to have the math be aware of the potential for wrapping, like this, which exploits the same nature of the unsigned 32-bit number space as the TIM
void delayMicroseconds(uint32_t us) {
uint32_t start = micros();
while((micros() - start) < us) { // Safer to compute a delta, and compare that..
// Espera até atingir o tempo desejado
}
}
You can't add it up front and check for the future..
2024-06-30 05:57 PM - edited 2024-06-30 05:58 PM
It's STM32L0 (with a zero) not STM32LO.
Your two problems maybe actually be one and the same. Your code is either crashing or hanging, and the only way to get out of this bad state is to reset the chip. Fix one and you may have fixed the other. Such Economy!
2024-07-01 06:34 AM
2024-07-01 06:56 AM
@MatheusMarkies wrote:In the STMCube debug mode, it is stuck on HAL_Init.
So step into that function to see where, exactly, it's getting stuck.
2024-07-01 07:08 AM - edited 2024-07-01 07:08 AM
Why is your debug output corrupted at start of line? I saw this once when I was multiplexing a pin between SPI and UART modes, and was not respecting the timing for CS for the other chip when switching.
You should look into it - later.
2024-07-01 07:41 AM
Well, as I'm using the Mega which is powered by a 5V voltage, I believe it's corrupted by the difference in power supply voltages and the lack of a level shifter in the UART.
2024-07-01 07:45 AM
How can I debug it? This function is defined in a file called "stm32l0xx_hal.c".
The IDE debug is only showing what it executes in main.c
2024-07-01 07:52 AM
@MatheusMarkies wrote:Well, as I'm using the Mega which is powered by a 5V voltage, I believe it's corrupted by the difference in power supply voltages and the lack of a level shifter in the UART.
I don't think so. But let's not get off track here.
2024-07-01 08:00 AM - edited 2024-07-01 08:09 AM
Use the 'Step Into' button:
Also available on the 'Run' menu:
It's a pretty standard debugger feature - Microchip's MPLAB and Microchip Studio both have it:
https://developerhelp.microchip.com/xwiki/bin/view/software-tools/ides/x/debugging/debug-toolbar/