ADC实验(内部温度传感器实验)(光敏传感器实验)

内部温度传感器简介

STM32 有一个内部的温度传感器,可以用来测量 CPU 及周围的温度(内部温度传感器更 适合于检测温度的变化,需要测量精确温度的情况下,应使用外置传感器)。对于 STM32F103 来说,该温度传感器在内部和 ADC1_IN16 输入通道相连接,此通道把传感器输出的电压转换 成数字值。温度传感器模拟输入推荐采样时间是 17.1us。STM32F103 内部温度传感器支持的温 度范围为:-40~125 度。精度为±1.5℃左右。

STM32 内部温度传感器的使用很简单,只要设置一下内部 ADC,并激活其内部温度传感 器通道就差不多了。关于 ADC 的设置,我们在上一章已经进行了详细的介绍,这里就不再多 说。接下来我们介绍一下和温度传感器设置相关的两个地方。

第一个地方,我们要使用 STM32 的内部温度传感器,必须先激活 ADC 的内部通道,这里 通过 ADC_CR2 的 AWDEN 位(bit23)设置。设置该位为 1 则启用内部温度传感器。

第二个地方,STM32 的内部温度传感器固定的连接在 ADC1 的通道 16 上,所以,我们在 设置好 ADC1 之后只要读取通道 16 的值,就是温度传感器返回来的电压值了。根据这个值,我 们就可以计算出当前温度。计算公式如下:(℃) ={ (V25-Vsense) / Avg_Slope}+25

式子中:V25 = Vsense 在 25 度时的数值(典型值为:1.43)

Avg_Slope = 温度与 Vsense 曲线的平均斜率(单位:mv/℃或 uv/℃)(典型值:4.3mv/℃)。

利用以上公式,我们就可以方便的计算出当前温度传感器的温度了。

ADC实验(内部温度传感器实验)(光敏传感器实验)_第1张图片

ADC实验(内部温度传感器实验)(光敏传感器实验)_第2张图片 代码

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

ADC_HandleTypeDef g_adc_handle;/* ADC句柄 */

/* ADC内部温度传感器通道 */
void adc_temperature_init(void)
{
    ADC_ChannelConfTypeDef adc_ch_conf = {0};
    
    g_adc_handle.Instance = ADC1;/* 选择ADC1 */
    g_adc_handle.Init.ContinuousConvMode = DISABLE;/* 关闭连续转换模式 */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;/* 数据对齐方式:右对齐 */
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;/* 禁止规则通道组间断模式 */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;/* 触发转换方式:软件触发 */
    g_adc_handle.Init.NbrOfConversion = 1;/* 赋值范围是1~16,本实验用到1个规则通道序列 */
    g_adc_handle.Init.NbrOfDiscConversion = 0;/* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;/* 非扫描模式,仅用到一个通道 */
    HAL_ADC_Init(&g_adc_handle);/* 初始化 */
    
    HAL_ADCEx_Calibration_Start(&g_adc_handle);/* 校准ADC */
    
    adc_ch_conf.Channel = ADC_CHANNEL_16;/* 通道16 */
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;/* 序列1 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;/* 采样时间239.5个ADC时钟周期 */
    HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);/* 通道配置 */
}

/* ADC Msp底层函数初始化 */
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)
    {
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
        
        __HAL_RCC_ADC1_CLK_ENABLE();/* 使能ADC1时钟 */
        
        /* 设置ADC时钟 */
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;/* ADC外设时钟 */
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;/* 分频因子6时钟为72M/6=12MHz */
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);/* 设置ADC时钟 */
    }
}

/* 获得ADC转换后结果的函数 */
uint32_t adc_get_result(void)
{
    HAL_ADC_Start(&g_adc_handle);/* 开启ADC转换 */
    HAL_ADC_PollForConversion(&g_adc_handle, 10);/* 轮询转换(等待转换完成) */
    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);/* 返回最近一次ADC1规则组的转换结果 */
}

/* 获取内部温度传感器温度值 */
short adc_get_temperature(void)
{
    uint32_t adcx;
    double temperature;
    short result;
    
    adcx = adc_get_result();/* 得到ADC的转换值 */
    temperature = adcx * (3.3 / 4096);/* 转化为电压值 */
    temperature = (1.43 - temperature) / 0.0043 + 25;/* 计算温度 */
    result = temperature * 100;/* 扩大100倍. */
    
    return result;
}

由于前面实验中的 adc_init 实验是 对 ADC_CHANNEL_1 进行配置的,而我们对内部温度传感器的初始化步骤与普通 ADC 类似, 为了不重复编写代码,我们用位操作函数进行修改 ADC 通道,把 ADC_CR2 的 TSVREFE 位置 1,即 SET_BIT(g_adc_handle.Instance->CR2, ADC_CR2_TSVREFE);这样子就可以完成对内部 温度传感器通道的初始化工作。 

#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();                         /* 初始化 HAL 库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 传口初始化 */
    
    led_init();                         /* LED初始化 */
    lcd_init();                         /* LCD初始化 */
    
    adc_temperature_init();                     /* 初始化ADC内部温度传感器采集 */
    
    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();   /* 得到温度值 */

        if (temp < 0)
        {
            temp = -temp;
            lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, "-", BLUE);   /* 显示负号 */
        }
        else
        {
            lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, " ", BLUE);   /* 无符号 */
        }
        lcd_show_xnum(30 + 11 * 8, 120, temp / 100, 2, 16, 0, BLUE);    /* 显示整数部分 */
        lcd_show_xnum(30 + 14 * 8, 120, temp % 100, 2, 16, 0X80, BLUE); /* 显示小数部分 */
        
        LED0_TOGGLE();  /* LED0闪烁,提示程序运行 */
        delay_ms(250);
    }
}

该部分的代码逻辑很简单,先是得到温度值,再根据温度值判断正负值,来显示温度符号, 再显示整数和小数部分。

光敏传感器实验

ADC实验(内部温度传感器实验)(光敏传感器实验)_第3张图片

光敏传感器简介 

STM32F103 精英开发板板载了一个光敏二极管(光敏电阻),作为光敏传感器,它对光的 变化非常敏感。光敏二极管也叫光电二极管。光敏二极管与半导体二极管在结构上是类似的, 其管芯是一个具有光敏特征的 PN 结,具有单向导电性,因此工作时需加上反向电压。无光照 时,有很小的饱和反向漏电流,即暗电流,此时光敏二极管截止。当受到光照时,饱和反向漏 电流大大增加,形成光电流,它随入射光强度的变化而变化。当光线照射 PN 结时,可以使 PN 结 中产生电子一空穴对,使少数载流子的密度增加。这些载流子在反向电压下漂移,使反向电流 增加。因此可以利用光照强弱来改变电路中的电流。

利用这个电流变化,我们串接一个电阻,就可以转换成电压的变化,从而通过 ADC 读取电 压值,判断外部光线的强弱。

本章,我们利用 ADC3 的通道 6(PF8)来读取光敏二极管电压的变化,从而得到环境光线 的变化,并将得到的光线强度,显示在 TFTLCD 上面。

通过 ADC3 的通道 6(PF8)读取光敏传感器(LS1)的电压值,并转换为 0~100 的光线强 度值,显示在 LCD 模块上面。光线越亮,值越大;光线越暗,值越小。大家可以用手指遮挡 LS1 和用手电筒照射 LS1,来查看光强变化。LED0 闪烁用于提示程序正在运行。

原理图

我们主要来看看光敏传感器和开发板的连接,如下图所示:ADC实验(内部温度传感器实验)(光敏传感器实验)_第4张图片

图中,LS1 是光敏二极管,外观看起像与贴片 LED 类似(位于 OLED 插座旁边,LS1), R34 为其提供反向电压,当环境光线变化时,LS1 两端的电压也会随之改变,通过 ADC3_IN6 通道读取 LIGHT_SENSOR(PF8)上面的电压,即可得到环境光线的强弱。光线越强,电压越 低,光线越暗,电压越高。V = VCC3.3 - IR34 

ADC实验(内部温度传感器实验)(光敏传感器实验)_第5张图片

代码

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

ADC_HandleTypeDef g_adc_handle;/* ADC句柄 */

/* ADC单通道 */
void adc_init(void)
{
    ADC_ChannelConfTypeDef adc_ch_conf = {0};
    
    g_adc_handle.Instance = ADC3;/* 选择ADC3 */
    g_adc_handle.Init.ContinuousConvMode = DISABLE;/* 关闭连续转换模式 */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;/* 数据对齐方式:右对齐 */
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;/* 禁止规则通道组间断模式 */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;/* 触发转换方式:软件触发 */
    g_adc_handle.Init.NbrOfConversion = 1;/* 赋值范围是1~16,本实验用到1个规则通道序列 */
    g_adc_handle.Init.NbrOfDiscConversion = 0;/* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;/* 非扫描模式,仅用到一个通道 */
    HAL_ADC_Init(&g_adc_handle);/* 初始化 */
    
    HAL_ADCEx_Calibration_Start(&g_adc_handle);/* 校准ADC */
    
    adc_ch_conf.Channel = ADC_CHANNEL_6;/* 通道1 */
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1;/* 序列1 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;/* 采样时间239.5个ADC时钟周期 */
    HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);/* 通道配置 */
}

/* ADC Msp底层函数初始化 */
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();/* 开启GPIOF时钟 */
        __HAL_RCC_ADC3_CLK_ENABLE();/* 使能ADC3时钟 */
        
        /* 设置ADC时钟 */
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;/* ADC外设时钟 */
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;/* 分频因子6时钟为72M/6=12MHz */
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);/* 设置ADC时钟 */
        
        /* 设置ADC采集通道对应IO引脚工作模式 */
        gpio_init_struct.Pin = GPIO_PIN_8;/* ADC通道IO引脚 */
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;/* 模拟 */
        HAL_GPIO_Init(GPIOF, &gpio_init_struct);
    }
}

/* 获得ADC转换后结果的函数 */
uint32_t adc_get_result(void)
{
    HAL_ADC_Start(&g_adc_handle);/* 开启ADC转换 */
    HAL_ADC_PollForConversion(&g_adc_handle, 10);/* 轮询转换(等待转换完成) */
    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);/* 返回最近一次ADC1规则组的转换结果 */
}

/* 读取光敏传感器值, 0~100:0,最暗;100,最亮 */
uint8_t lsens_get_val(void)
{
    uint32_t temp;
    
    temp = adc_get_result();
    temp = temp / 40;/* 0~100, 对应 0~4095, 4095 / 100 = 40 */
    
    if(temp > 100)
    {
        temp = 100;/* 若remp大于100,则temp等于100,4095除以40等于102.375 */
    }
    
    return (uint8_t)(100 - temp);/* V = VCC3.3 - IR,光照越强,I越大,V越小,V与光照呈反相 */
}

lsens_get_val 函数用于获取当前光照强度,该函数通过 adc_get_result 函数得到通 道 6 转换的电压值,经过简单量化后,处理成 0~100 的光强值。0 对应最暗,100 对应最亮。 

#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();                         /* 初始化 HAL 库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 传口初始化 */
    
    led_init();                         /* LED初始化 */
    lcd_init();                         /* LCD初始化 */
    adc_init();                         /* ADC3初始化 */
    
    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); /* 显示ADC3的值 */
        LED0_TOGGLE();   /* LED0闪烁,提示程序运行 */
        delay_ms(250);
    }
}

该部分的代码逻辑很简单,初始化各个外设之后,进入死循环,通过 lsens_get_val 获取光 敏传感器得到的光强值(0~100),并显示在 TFTLCD 上面。

你可能感兴趣的:(stm32)