2024-07-01 06:44 PM - edited 2024-07-02 04:52 AM
I recently wrote some code with the help of stack exchange regarding an MQ2 gas sensor. I needed to alter the code to use DMA as the MCU I'm using for the project only has one ADC channel. My goal was to measure the ADC value of the MQ2 sensor along with a battery voltage for when it drops below 2.5 volts to blink a red LED. I'm noticing that the UART is very slow when outputting numbers, and when I adjust my voltage source voltage it takes a few seconds to update. It only seems faster when the Battery voltage isn't below 2.5V.
I'm not sure exactly what I need to fix with my code but it can be seen below:
#include "main.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BATLED GPIO_PIN_3
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
UART_HandleTypeDef huart2;
uint16_t ADCValues[2];
uint16_t ADCCount = sizeof (ADCValues)/ sizeof (ADCValues[0]);
uint16_t ADC_MQ2_AVG;
uint16_t ADC_Voltage_AVG;
float Voltage = 0;
float result_ratio;
float Vresult_ratio;
float Ro = 10;
char msg[50];
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
static void MX_USART2_UART_Init(void);
uint8_t ADCConversionComplete = 0;
static void uart_send_str (const char* msg)
{
HAL_UART_Transmit (&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
ADCConversionComplete = 1;
}
static float ReadSensor (uint32_t sample_count, int pause_ms)
{
uint32_t i;
uint16_t MQ2ADCVAL;
uint32_t ADCsum1 = 0;
for (i = 0; i < sample_count; i++) {
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADCValues,ADCCount);
MQ2ADCVAL = (uint16_t)ADCValues[0];
ADCsum1 += MQ2ADCVAL;
HAL_Delay (pause_ms);
}
ADC_MQ2_AVG = ADCsum1 / sample_count;
return 0;
}
static float BAT_VOLT (uint32_t sample_count, int pause_ms)
{
uint32_t i;
uint16_t ADCVoltage;
uint32_t ADCsum2 = 0;
for (i = 0; i < sample_count; i++) {
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADCValues,ADCCount);
ADCVoltage = (uint16_t)ADCValues[1];
Voltage = (ADCVoltage *3.3) /(4095);
ADCsum2 += ADCVoltage;
HAL_Delay (pause_ms);
}
ADC_Voltage_AVG = ADCsum2/ sample_count;
if(Voltage<2.5){
HAL_GPIO_WritePin(GPIOC,BATLED,GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC,BATLED,GPIO_PIN_RESET);
HAL_Delay(500);
}
return 0;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USART2_UART_Init();
while(1)
{
result_ratio = ReadSensor (5, 50) / Ro;
Vresult_ratio = BAT_VOLT(5,50);
while((ADCConversionComplete == 0))
{
}
sprintf(msg, "MQ2ADCVAL: %hu\tADCVoltage: %hu Voltage: %f\r\n",ADC_MQ2_AVG,ADC_Voltage_AVG,Voltage);
uart_send_str (msg);
}
}
PuTTy terminal:
Solved! Go to Solution.
2024-07-02 05:16 AM
Which STM32 are you using?
> Should I control the LED via a Timer channel?
Maybe; but generally, you want to understand design methods for non-blocking operations(*).
> "You don't show things like how do you calculate the result" - I'm a little confused what you mean by this can you further clarify?
In the original post I overlooked this:
Voltage = (ADCVoltage *3.3) /(4095);
This (and also the comparison of Voltage to 2.5) is a double-precision calculation (*), and as such, it takes relatively long time even on a STM32 which has a single-precision FPU like the 'F4 family. You want to use single-precision float constants and explicit casts:
Voltage = ((float)ADCVoltage *3.3f) /(4095.0f);
(*) These are relatively basic elements of microcontroller programming (although the double/float etc. is C programming in general), and this is not the proper place to explain them. You may want to do extensive reading and/or take some appropriate courses.
JW
2024-07-01 11:12 PM
You don't show things like how do you calculate the result, but besides that, it is full of delays and blocking code. For example, isn't this a deliberate delay?
if(Voltage<2.5){
HAL_GPIO_WritePin(GPIOC,BATLED,GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC,BAT
LED,GPIO_PIN_RESET);
HAL_Delay(500);
}
JW
2024-07-02 04:47 AM
Yes, I realize now it is acting as a deliberate delay although I don't want the LED to slow down the UART terminal as it blinks. Should I control the LED via a Timer channel?
"You don't show things like how do you calculate the result" - I'm a little confused what you mean by this can you further clarify?
2024-07-02 05:16 AM
Which STM32 are you using?
> Should I control the LED via a Timer channel?
Maybe; but generally, you want to understand design methods for non-blocking operations(*).
> "You don't show things like how do you calculate the result" - I'm a little confused what you mean by this can you further clarify?
In the original post I overlooked this:
Voltage = (ADCVoltage *3.3) /(4095);
This (and also the comparison of Voltage to 2.5) is a double-precision calculation (*), and as such, it takes relatively long time even on a STM32 which has a single-precision FPU like the 'F4 family. You want to use single-precision float constants and explicit casts:
Voltage = ((float)ADCVoltage *3.3f) /(4095.0f);
(*) These are relatively basic elements of microcontroller programming (although the double/float etc. is C programming in general), and this is not the proper place to explain them. You may want to do extensive reading and/or take some appropriate courses.
JW
2024-07-02 05:20 AM
I see, what books or courses do you recommend I take? I was thinking about taking a programming class that teaches C, but maybe I'd be better off taking something that involves Embedded Engineering instead.
2024-07-02 05:29 AM
> what books or courses do you recommend I take
I don't teach and don't know what to recommend in this regard. Also my path to this point was highly atypical.
Perhaps look around in your local offerings, and internet; try to talk to your peers.
JW
2024-07-02 12:25 PM
See this YouTube video on using a timercallback to blink an LED and many other useful stuff without blocking other code.
https://www.youtube.com/watch?v=o0qhmXR5LD0