2024-05-22 06:27 PM - last edited on 2024-05-24 10:25 AM by Tesla DeLorean
I recently have been working on a custom board with the stm32f030f4p6 for a gas sensor project, I just got it to bootload successfully and blink some LEDs so now I'm using the ADC channel to read values from it and converting it to a voltage level for testing sake (the final code will convert the values in ppm).
I've noticed almost a 30% error in the com port that I'm reading from an L476RG I have hooked up to via some external wires and I'm not exactly sure how to make it more accurate. For example, the current ADC voltage value I'm reading is 2.36 on PuTTY but on my multimeter, I only see 1.86 volts.
Here is a picture of the schematic:
And the layout:
The ADC is set to a 12-bit resolution and the sample size is at 1.5 cycles, I tried increasing the sample size but this didn't seem to do much.
Unfortunately, when I made this board I didn't realize I read the datasheet wrong and chose a UART IC when this MCU can only use USART, so I have no way of reading from the terminal without using the L476RGs ADC port and I feel using two MCUs for one setup also isn't helping.
Here is the code:
#include "main.h"
#include <stdlib.h> //Standard library header file since we use a logarithmic function
#include <stdio.h> //Standard header filer
#include <string.h> //String header filer
#define ledadc GPIO_PIN_7
uint16_t ADCval; //Unsigned 16 bit variable, 16 bit registers to hold 12 bit ADC data
uint16_t BATval; //Unsigned 16 bit variable, to store the battery value
float Voltage; //Float variable, that will take the 12 bit ADC "ADCVal" data and produce a voltage output
float BATVoltage; //Float variable, that takes the BATval and stores it as voltage value
ADC_HandleTypeDef hadc;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC_Init();
while (1)
{
HAL_ADC_Start(&hadc); //Starts the ADC on the STM32
HAL_ADC_PollForConversion(&hadc, 100); //Pulls the data from the ADC of the MCU to gather the voltage values from the gas sensor
ADCval = HAL_ADC_GetValue(&hadc); //Pulls ADC value from channel zero, stores it in the voltage variable we have created
Voltage = (ADCval *5.0) /(4095); //Obtains voltage value by taking ADC value from HAL, dividing it by the 16 bit resolution,
//And multiplying it by the reference voltage of the STM32 (3.3V)
if(Voltage<2){
HAL_GPIO_WritePin(GPIOA,ledadc,GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA,ledadc,GPIO_PIN_RESET);
HAL_Delay(500);
}
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSI14CalibrationValue = 16;
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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief ADC Initialization Function
* None
* @retval None
*/
static void MX_ADC_Init(void)
{
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_5;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC_Init 2 */
/* USER CODE END ADC_Init 2 */
}
/**
* @brief GPIO Initialization Function
* None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pin : PA7 */
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* file: pointer to the source file name
* line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
Any help is greatly appreciated.
2024-05-26 01:41 PM
I'm multiplying it by 5 because I am using pin A5 on the L476RG to read the value of the gas sensor, and that board is plugged into my computer and powered off of 5V, I have no way off reading directly off of my PCB bc I messed up with the pin configuration for USART and chose a UART IC instead. This was from reading the pinout incorrectly on the datasheet.
Right now I have a wire soldered on from my gas sensor to a breadboard, with both grounds of the PCB and L476RG connected, and the analog pin from the L476RG connected after a resistor:
I know this isn't exactly the best way to do this, but I don't see any other way since there's no room on the pads of the MCU to solder a wire directly to it. I'm also assuming the cable length doesn't help.
2024-05-27 05:34 AM
Again, the ADC does not "know" about your 5V supply to the gas sensor.
The ADC works in that way that it outputs value which represents the ratio between input signal and the ADC reference voltage, which is connected to VREF+ pin of STM32 (in small packages VREF+ is connected internally to VDDA).
JW
2024-05-29 04:52 AM
So I read the schematic of the L476RG, and you are correct it is connected to 3V3 as well. Its unsurprising since most MCUs operate at that voltage range anyway.