ADC experiment (internal temperature sensor experiment) (photosensitive sensor experiment)

Introduction to Internal Temperature Sensor

STM32 has an internal temperature sensor that can be used to measure the temperature of the CPU and its surroundings (the internal temperature sensor is more suitable for detecting temperature changes. When precise temperature measurement is required, an external sensor should be used). For STM32F103, the temperature sensor is internally connected to the ADC1_IN16 input channel, which converts the voltage output by the sensor into a digital value. The recommended sampling time for the temperature sensor analog input is 17.1us. The temperature range supported by the STM32F103 internal temperature sensor is: -40~125 degrees. The accuracy is about ±1.5℃.

Using the STM32 internal temperature sensor is very simple, just set the internal ADC and activate its internal temperature sensor channel. Regarding the setting of ADC, we have introduced it in detail in the previous chapter, so we won’t say more here. Next, we introduce two places related to temperature sensor settings.

First, if we want to use the internal temperature sensor of STM32, we must first activate the internal channel of the ADC, which is set through the AWDEN bit (bit23) of ADC_CR2. Setting this bit to 1 enables the internal temperature sensor.

The second place is that the internal temperature sensor of STM32 is fixedly connected to channel 16 of ADC1. Therefore, after setting up ADC1, we only need to read the value of channel 16, which is the voltage value returned by the temperature sensor. Based on this value, we can calculate the current temperature. The calculation formula is as follows:(℃) ={ (V25-Vsense) / Avg_Slope} + 25

In the formula: V25 = Vsense value at 25 degrees (typical value: 1.43)

Avg_Slope = average slope of the temperature vs. Vsense curve (unit: mv/℃ or uv/℃) (typ.: 4.3mv/℃).

Using the above formula, we can easily calculate the temperature of the current temperature sensor.

Code

#include "./BSP/ADC/adc.h"

ADC_HandleTypeDef g_adc_handle;/* ADC handle */

/* ADC internal temperature sensor channel */
void adc_temperature_init(void)
{
    ADC_ChannelConfTypeDef adc_ch_conf = {0};
    
    g_adc_handle.Instance = ADC1;/* Select ADC1 */
    g_adc_handle.Init.ContinuousConvMode = DISABLE;/* Turn off continuous conversion mode */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;/* Data alignment: right aligned */
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;/* Disable rule channel group discontinuous mode */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;/* Trigger conversion method: software trigger */
    g_adc_handle.Init.NbrOfConversion = 1;/* The assignment range is 1~16, this experiment uses 1 regular channel sequence */
    g_adc_handle.Init.NbrOfDiscConversion = 0;/* Configure the number of regular channels in discontinuous mode. After disabling the discontinuous mode of the regular channel group, this parameter is ignored */
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;/* Non-scan mode, only one channel is used */
    HAL_ADC_Init( & amp;g_adc_handle);/* Initialization */
    
    HAL_ADCEx_Calibration_Start( & amp;g_adc_handle);/* Calibrate ADC */
    
    adc_ch_conf.Channel = ADC_CHANNEL_16;/* Channel 16 */
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;/* sequence 1 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;/* Sampling time 239.5 ADC clock cycles */
    HAL_ADC_ConfigChannel( & amp;g_adc_handle, & amp;adc_ch_conf);/* Channel configuration */
}

/* ADC Msp underlying function initialization */
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)
    {
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
        
        __HAL_RCC_ADC1_CLK_ENABLE();/* Enable ADC1 clock */
        
        /* Set ADC clock */
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;/* ADC peripheral clock */
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;/* The frequency division factor 6 clock is 72M/6=12MHz */
        HAL_RCCEx_PeriphCLKConfig( & amp;adc_clk_init);/* Set ADC clock */
    }
}

/* Function to obtain the ADC conversion result */
uint32_t adc_get_result(void)
{
    HAL_ADC_Start( & amp;g_adc_handle);/* Start ADC conversion */
    HAL_ADC_PollForConversion( & amp;g_adc_handle, 10);/* Polling for conversion (waiting for conversion to complete) */
    return (uint16_t)HAL_ADC_GetValue( & amp;g_adc_handle);/* Return the latest conversion result of the ADC1 rule group */
}

/* Get the temperature value of the internal temperature sensor */
short adc_get_temperature(void)
{
    uint32_t adcx;
    double temperature;
    short result;
    
    adcx = adc_get_result();/* Get the ADC conversion value */
    temperature = adcx * (3.3 / 4096);/* Convert to voltage value */
    temperature = (1.43 - temperature) / 0.0043 + 25;/* Calculate temperature */
    result = temperature * 100;/* Expand 100 times. */
    
    return result;
}

Since the adc_init experiment in the previous experiment configured ADC_CHANNEL_1, and our initialization steps for the internal temperature sensor are similar to those of ordinary ADCs, in order not to write code repeatedly, we use bit operation functions to modify the ADC channel and set the TSVREFE bit of ADC_CR2 to 1 , that is, SET_BIT(g_adc_handle.Instance->CR2, ADC_CR2_TSVREFE); in this way, the initialization of the internal temperature sensor channel can be completed.

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/ADC/adc.h"

int main(void)
{
    short temp;
    
    HAL_Init(); /* Initialize HAL library */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* Set clock, 72Mhz */
    delay_init(72); /* Delay initialization */
    usart_init(115200); /* Port initialization */
    
    led_init(); /* LED initialization */
    lcd_init(); /* LCD initialization */
    
    adc_temperature_init(); /* Initialize ADC internal temperature sensor collection */
    
    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "Temperature TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 120, 200, 16, 16, "TEMPERATE: 00.00C", BLUE);
    
    while(1)
    {
        temp = adc_get_temperature(); /* Get the temperature value */

        if (temp < 0)
        {
            temp = -temp;
            lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, "-", BLUE); /* Display negative sign */
        }
        else
        {
            lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, " ", BLUE); /* unsigned */
        }
        lcd_show_xnum(30 + 11 * 8, 120, temp / 100, 2, 16, 0, BLUE); /* Display the integer part */
        lcd_show_xnum(30 + 14 * 8, 120, temp % 100, 2, 16, 0X80, BLUE); /* Display the decimal part */
        
        LED0_TOGGLE(); /* LED0 flashes, prompting the program to run */
        delay_ms(250);
    }
}

The code logic of this part is very simple. First, the temperature value is obtained, and then the positive and negative values are determined based on the temperature value to display the temperature symbol, and then the integer and decimal parts are displayed.

Light sensitive sensor experiment

Introduction to Photosensitive Sensors

The STM32F103 Elite Development Board has a photodiode (photoresistor) onboard. As a photosensitive sensor, it is very sensitive to changes in light. Photodiodes are also called photodiodes. Photodiodes are similar in structure to semiconductor diodes. The tube core is a PN junction with photosensitive characteristics and has unidirectional conductivity, so a reverse voltage needs to be applied during operation. When there is no light, there is a small saturated reverse leakage current, that is, dark current, and the photosensitive diode is turned off at this time. When exposed to light, the saturated reverse leakage current greatly increases, forming a photocurrent, which changes with the change of incident light intensity. When light irradiates the PN junction, electron-hole pairs can be generated in the PN junction, increasing the density of minority carriers. These carriers drift under the reverse voltage, causing the reverse current to increase. Therefore, the intensity of light can be used to change the current in the circuit.

Using this current change, we can convert it into a voltage change by connecting a resistor in series, and then read the voltage value through the ADC to determine the intensity of external light.

In this chapter, we use channel 6 (PF8) of ADC3 to read changes in the photodiode voltage to obtain changes in ambient light, and display the obtained light intensity on the TFTLCD.

The voltage value of the photosensitive sensor (LS1) is read through channel 6 (PF8) of ADC3, converted into a light intensity value of 0~100, and displayed on the LCD module. The brighter the light, the larger the value; the darker the light, the smaller the value. You can use your finger to cover LS1 and shine a flashlight on LS1 to see changes in light intensity. LED0 flashes to indicate that the program is running.

Schematic

Let’s mainly look at the connection between the photosensitive sensor and the development board, as shown in the figure below:

In the picture, LS1 is a photosensitive diode, which looks similar to a SMD LED (located next to the OLED socket, LS1). R34 provides reverse voltage for it. When the ambient light changes, the voltage at both ends of LS1 will also change. Read the voltage on LIGHT_SENSOR (PF8) through the ADC3_IN6 channel to get the intensity of the ambient light. The stronger the light, the lower the voltage; the darker the light, the higher the voltage. V = VCC3.3 – IR34

Code

#include "./BSP/ADC/adc.h"

ADC_HandleTypeDef g_adc_handle;/* ADC handle */

/* ADC single channel */
void adc_init(void)
{
    ADC_ChannelConfTypeDef adc_ch_conf = {0};
    
    g_adc_handle.Instance = ADC3;/* Select ADC3 */
    g_adc_handle.Init.ContinuousConvMode = DISABLE;/* Turn off continuous conversion mode */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;/* Data alignment: right aligned */
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;/* Disable rule channel group discontinuous mode */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;/* Trigger conversion method: software trigger */
    g_adc_handle.Init.NbrOfConversion = 1;/* The assignment range is 1~16, this experiment uses 1 regular channel sequence */
    g_adc_handle.Init.NbrOfDiscConversion = 0;/* Configure the number of regular channels in discontinuous mode. After disabling the discontinuous mode of the regular channel group, this parameter is ignored */
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;/* Non-scan mode, only one channel is used */
    HAL_ADC_Init( & amp;g_adc_handle);/* Initialization */
    
    HAL_ADCEx_Calibration_Start( & amp;g_adc_handle);/* Calibrate ADC */
    
    adc_ch_conf.Channel = ADC_CHANNEL_6;/* Channel 1 */
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;/* sequence 1 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;/* Sampling time 239.5 ADC clock cycles */
    HAL_ADC_ConfigChannel( & amp;g_adc_handle, & amp;adc_ch_conf);/* Channel configuration */
}

/* ADC Msp underlying function initialization */
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC3)
    {
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
        GPIO_InitTypeDef gpio_init_struct = {0};
        
        __HAL_RCC_GPIOF_CLK_ENABLE();/* Enable GPIOF clock */
        __HAL_RCC_ADC3_CLK_ENABLE();/* Enable ADC3 clock */
        
        /* Set ADC clock */
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;/* ADC peripheral clock */
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;/* The frequency division factor 6 clock is 72M/6=12MHz */
        HAL_RCCEx_PeriphCLKConfig( & amp;adc_clk_init);/* Set ADC clock */
        
        /* Set the working mode of the IO pin corresponding to the ADC acquisition channel */
        gpio_init_struct.Pin = GPIO_PIN_8;/* ADC channel IO pin */
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;/* Simulation */
        HAL_GPIO_Init(GPIOF, & amp;gpio_init_struct);
    }
}

/* Function to obtain the ADC conversion result */
uint32_t adc_get_result(void)
{
    HAL_ADC_Start( & amp;g_adc_handle);/* Start ADC conversion */
    HAL_ADC_PollForConversion( & amp;g_adc_handle, 10);/* Polling for conversion (waiting for conversion to complete) */
    return (uint16_t)HAL_ADC_GetValue( & amp;g_adc_handle);/* Return the latest conversion result of the ADC1 rule group */
}

/* Read the light sensor value, 0~100:0, the darkest; 100, the brightest */
uint8_t lsens_get_val(void)
{
    uint32_t temp;
    
    temp = adc_get_result();
    temp = temp / 40;/* 0~100, corresponding to 0~4095, 4095 / 100 = 40 */
    
    if(temp > 100)
    {
        temp = 100;/* If remp is greater than 100, then temp is equal to 100, and 4095 divided by 40 equals 102.375 */
    }
    
    return (uint8_t)(100 - temp);/* V = VCC3.3 - IR, the stronger the light, the larger I, the smaller V, and V is inverse phase with the light */
}

The lsens_get_val function is used to obtain the current light intensity. This function obtains the converted voltage value of channel 6 through the adc_get_result function. After simple quantization, it is processed into a light intensity value of 0~100. 0 corresponds to the darkest and 100 corresponds to the brightest.

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/ADC/adc.h"

int main(void)
{
    short adcx;
    
    HAL_Init(); /* Initialize HAL library */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* Set clock, 72Mhz */
    delay_init(72); /* Delay initialization */
    usart_init(115200); /* Port initialization */
    
    led_init(); /* LED initialization */
    lcd_init(); /* LCD initialization */
    adc_init(); /* ADC3 initialization */
    
    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "LSENS TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "LSENS_VAL:", BLUE);
    
    while(1)
    {
        adcx = lsens_get_val();
        lcd_show_xnum(30 + 10 * 8, 110, adcx, 3, 16, 0, BLUE); /* Display the value of ADC3 */
        LED0_TOGGLE(); /* LED0 flashes, prompting the program to run */
        delay_ms(250);
    }
}

The code logic of this part is very simple. After initializing each peripheral, it enters an infinite loop, obtains the light intensity value (0~100) obtained by the photosensitive sensor through lsens_get_val, and displays it on the TFTLCD.