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-07-01 09:38 AM
Apparently it crashes when initializing the "status" variable in the first line of HAL_Init, which is pretty weird, right?
Update: Apparently now when I restart the chip manually it manages to run through the for, but crashes after a few executions of the loop method.
2024-07-01 09:43 AM
@MatheusMarkies wrote:Apparently it crashes when initializing the "status" variable in the first line of HAL_Init, which is pretty weird, right?
What optimisation level are you using?
More likely it's actually crashing somewhere in those early lines ...
2024-07-01 09:59 AM - edited 2024-07-01 10:24 AM
@MatheusMarkies , please carefully define what you mean by "Crashing". What are the observable symptoms? is the debugger inside the hard fault handler? does the chip simply become unresponsive? do flames shoot out of PD2, together with a strong smell of sulphur while one faintly hears the screams of the damned emanating from the depths of the cache unit's bowels ? what?
2024-07-01 10:48 AM
By crash I mean that it stops sending signals via the UART debug, and only comes back if I restart it. By the last signal it sent I can tell where it stopped.
2024-07-01 10:50 AM
How do I see it? Sorry, I'm a newbie to STM32 :eyes:
2024-07-01 01:50 PM
In the Project Properties:
2024-07-01 03:09 PM
I put it on the same way:
My fear is that I've made the wrong connections on my circuit PCB.
2024-07-01 03:50 PM - edited 2024-07-02 04:43 AM
@MatheusMarkies wrote:By crash I mean that it stops sending signals via the UART debug, and only comes back if I restart it. By the last signal it sent I can tell where it stopped.
Good. So now you need to find out if it really is crashing (reaching an undefined/critical error state), or if it's hanging (stuck waiting for something). You should be able to see this with the debugger. Perhaps by switching to instruction mode and seeing where you are relative to the binary (I'm sorry, but I don't have Andrew's patience to walk you through that).
Here's another possible way to debug this, though Andrew may want to suggest a different approach.
Generate a brand new project, without any peripherals, and add only minimal code: HAL_Delay() and toggle an LED in the main loop. Download it to the chip and run it.
Is it crashing? Good, you've narrowed down your problem significantly.
Is it working? add one peripheral, regenerate and run,
Is it crashing? Good, you've narrowed down your problem significantly.
Is it working? add some code that uses that peripheral. regenerate and run
Is it crashing? Good, you've narrowed down your problem significantly.
Is it working? add one more peripheral, regenerate and run,
Continue this way until you find the incremental change that reproduces this behavior, and you're now in a much better position to understand what and where the problem is.
I prefer to start from a working state and inch my way towards the bad state, rather than try to prune away bits until it works. I think it's better to maintain a "working state" as your invariant and work incrementally from there. But it's not the only way.
2024-07-02 02:13 AM - edited 2024-07-02 05:24 AM
@BarryWhit wrote:Here's another possible way to debug this, though Andrew may want to suggest a different approach.
Nope - that's an excellent approach! :thumbs_up:
I'd suggest that after the LED, the next peripheral you get working is a UART.
Having a UART and LED(s) available to give progress/status/diagnostic output is invaluable.
If you don't already use revision control, start using it now!
This will be invaluable in enabling you to get back to the "last known working" state, and to see exactly what changed from one step to the next.
It also seems that you would benefit from taking some time to go through the features in the STM32CubeIDE - including the debugger:
https://www.youtube.com/@stmicroelectronics/search?query=stm32CubeIDE
https://www.youtube.com/playlist?list=PLnMKNibPkDnEDEsV7IBXNvg7oNn3MfRd6
2024-07-02 08:33 AM
I did individual tests of the functions in my code.
And I solved some problems related to structs with the wrong types and other things that really made it crash.
The problem with my for loop was that the delay value was being calculated incorrectly, resulting in a very large wait value.
After fixing this problem and executing the loop with only the delay in it, it executed correctly.
The current delay time of the for is 488 microseconds, which in 2048 passes results in a total wait time of approximately 1 second. (This is so that the accelerometer signal can be read for 1 second, resulting in readings that can later be converted to the frequency domain using an FFT, making it possible to carry out in-depth vibration analysis).
However, in my tests I noticed that any reading of values from the accelerometer and writing to external memory causes the wait time inside the for loop to increase.
Under normal conditions this loop should last exactly 1 second, with the delay in microseconds having its value calculated so that it can do this according to the number of passes of the for.
However, when anything is added to the loop, this time goes up a lot, reaching 26 seconds to do all the passes, when reading acceleration and writing fram are added.
What could be happening? The chip's processing should be enough, right?
Relevant information: I also have an external 25MHz crystal connected to the HSE.