参考:
1.正点原子
前言:
本笔记的主要目的和意义就是,再次练习ADC的使用。

33.1 光敏传感器简介

光敏传感器是最常见的传感器之一,它的种类繁多,主要有:光电管、光电倍增管、光敏电阻、光敏三极管、太阳能电池、红外线传感器、紫外线传感器、光纤式光电传感器、色彩传感器、CCD 和 CMOS 图像传感器等。光传感器是目前产量最多、应用最广的传感器之一,它在自动控制和非电量电测技术中占有非常重要的地位。
光敏传感器是利用光敏元件将光信号转换为电信号的传感器,它的敏感波长在可见光波长附近,包括红外线波长和紫外线波长。光传感器不只局限于对光的探测,它还可以作为探测元件组成其他传感器,对许多非电量进行检测,只要将这些非电量转换为光信号的变化即可。
探索者 STM32F407 开发板板载了一个光敏二极管(光敏电阻),作为光敏传感器,它对光的变化非常敏感。光敏二极管也叫光电二极管。光敏二极管与半导体二极管在结构上是类似的,其管芯是一个具有光敏特征的 PN 结,具有单向导电性,因此工作时需加上反向电压。无光照时,有很小的饱和反向漏电流,即暗电流,此时光敏二极管截止。当受到光照时,饱和反向漏电流大大增加,形成光电流,它随入射光强度的变化而变化。当光线照射 PN 结时,可以使 PN结中产生电子一空穴对,使少数载流子的密度增加。这些载流子在反向电压下漂移,使反向电流增加。因此可以利用光照强弱来改变电路中的电流。
利用这个电流变化,我们串接一个电阻,就可以转换成电压的变化,从而通过 ADC 读取电压值,判断外部光线的强弱。
本章,我们利用 ADC3 的通道 5(PF7)来读取光敏二极管电压的变化,从而得到环境光线的变化,并将得到的光线强度,通过串口输出。关于 ADC 的介绍,前面已经有详细介绍了,这里我们就不再细说了。

33.2 硬件设计

1. 例程功能
通过 ADC3 的通道 5(PF7)读取光敏传感器(LS1)的电压值,并转换为 0~100 的光线强度值,同过串口输出。光线越亮,值越大;光线越暗,值越小。大家可以用餐厅纸多次折叠遮盖 LS1和用手电筒照射 LS1,来查看光强变化。
2. 硬件资源
1)串口 1(PA9/PA10 连接在板载 USB 转串口芯片 CH340 上面)
2)ADC3 :通道 5 – PF7
3)光敏传感器 – PF7
3. 原理图
我们主要来看看光敏传感器和开发板的连接,如下图所示:
在这里插入图片描述

图中,LS1 是光敏二极管,外观看与贴片 LED 类似(实物在开发板 OLED 右侧),R76 为其提供反向电压,当环境光线变化时,LS1 两端的电压也会随之改变,从而通过 ADC3_IN5 通道,读取 LIGHT_SENSOR(PF7)上面的电压,即可得到环境光线的强弱。光线越强,电压越低,光线越暗,电压越高。

33.3 程序设计

33.3.1 ADC 的 HAL 库驱动

本实验用到的 ADC 的 HAL 库 API 函数前面都介绍过,具体调用情况请看程序解析部分。下面介绍读取光敏传感器 ADC 值的配置步骤。

读取光敏传感器 ADC 值配置步骤
1)开启 ADCx 和通道输出的 GPIO 时钟,配置该 IO 口的复用功能输出
首先开启 ADCx 的时钟,然后配置 GPIO 为复用功能输出。本实验我们默认用到 ADC3 通道 5,对应 IO 是 PF7,它们的时钟开启方法如下:
__HAL_RCC_ADC3_CLK_ENABLE (); /* 使能 ADC3 时钟 /
__HAL_RCC_GPIOF_CLK_ENABLE(); /
开启 GPIOF 时钟 */
IO 口复用功能是通过函数 HAL_GPIO_Init 来配置的。
2)设置 ADC3,开启内部温度传感器
调用 HAL_ADC_Init 函数来设置 ADC3 时钟分频系数、分辨率、模式、扫描方式、对齐方式等信息。
注意:该函数会调用:HAL_ADC_MspInit 回调函数来完成对 ADC 底层的初始化,包括:ADC3 时钟使能、ADC3 时钟源的选择等。
3)配置 ADC 通道并启动 AD 转换器
调用 HAL_ADC_ConfigChannel()函数配置 ADC3 通道 5,根据需求设置通道、序列、采样时间和校准配置单端输入模式或差分输入模式等。然后通过 HAL_ADC_Start 函数启动 AD 转换器。
4)读取 ADC 值,转换为光线强度值
这里选择查询方式读取,在读取 ADC 值之前需要调用 HAL_ADC_PollForConversion 等待上一次转换结束。然后就可以通过 HAL_ADC_GetValue 来读取 ADC 值。最后把得到的 ADC 值转换为 0~100 的光线强度值。

33.3.2 程序解析

1. LSENS 驱动代码
首先是光敏传感器初始化函数,其定义如下:(STM32CubeMX配置,自动生成)

void MX_ADC3_Init(void)
{

  /* USER CODE BEGIN ADC3_Init 0 */

  /* USER CODE END ADC3_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC3_Init 1 */

  /* USER CODE END ADC3_Init 1 */

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  hadc3.Instance = ADC3;
  hadc3.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc3.Init.Resolution = ADC_RESOLUTION_12B;
  hadc3.Init.ScanConvMode = DISABLE;
  hadc3.Init.ContinuousConvMode = DISABLE;
  hadc3.Init.DiscontinuousConvMode = DISABLE;
  hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc3.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc3.Init.NbrOfConversion = 1;
  hadc3.Init.DMAContinuousRequests = DISABLE;
  hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&hadc3) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
  sConfig.Channel = ADC_CHANNEL_5;    /*配置ADC3的通道5*/
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC3_Init 2 */

  /* USER CODE END ADC3_Init 2 */

}

void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(adcHandle->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspInit 0 */

  /* USER CODE END ADC1_MspInit 0 */
    /* ADC1 clock enable */
    __HAL_RCC_ADC1_CLK_ENABLE();
  /* USER CODE BEGIN ADC1_MspInit 1 */

  /* USER CODE END ADC1_MspInit 1 */
  }
  else if(adcHandle->Instance==ADC3)
  {
  /* USER CODE BEGIN ADC3_MspInit 0 */

  /* USER CODE END ADC3_MspInit 0 */
    /* ADC3 clock enable */
    __HAL_RCC_ADC3_CLK_ENABLE();

    __HAL_RCC_GPIOF_CLK_ENABLE();
    /**ADC3 GPIO Configuration
    PF7     ------> ADC3_IN5
    */
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

  /* USER CODE BEGIN ADC3_MspInit 1 */

  /* USER CODE END ADC3_MspInit 1 */
  }
}

光敏传感器初始化函数其实就是初始化 PF7 为不带上下拉的模拟模式和配置ADC3 通道5。

最后是读取光敏传感器值,函数定义如下:

uint32_t adc3_get_result(uint32_t ch)
{
    /* 设置通道,序列和采样时间 */
    //adc_channel_set(&hadc3, ch, 1, ADC_SAMPLETIME_480CYCLES);
    
    HAL_ADC_Start(&hadc3); /* 开启 ADC */
    HAL_ADC_PollForConversion(&hadc3, 10); /* 轮询转换 */

    /* 返回最近一次 ADC3 规则组的转换结果 */
    return (uint16_t)HAL_ADC_GetValue(&hadc3);
}

uint32_t adc3_get_result_average(uint32_t ch, uint8_t times)
{
    uint32_t temp_val = 0;
    uint8_t t;
    for (t = 0; t < times; t++) /* 获取 times 次数据 */
    {
        temp_val += adc3_get_result(ch);
    }
    
    return temp_val / times; /* 返回平均值 */
}

/**
* @brief 读取光敏传感器值
* @param 无
* @retval 0~100:0,最暗;100,最亮
*/
uint32_t adc_get_lsens_val(void)
{
    uint32_t temp_val = 0;
    /* 读取平均值 */
    temp_val = adc3_get_result_average(ADC_CHANNEL_5, 10);
    temp_val /= 40;
    
    if (temp_val > 100)
    {
        temp_val = 100;
    }
        
    return (100 - temp_val);
}

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

2. main.c 代码
在 main.c 里面编写如下代码:

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_TIM6_Init();
  MX_RTC_Init();
  MX_ADC1_Init();
  MX_ADC3_Init();
  /* USER CODE BEGIN 2 */
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    uart_debug_task();

    XL_TIME6_time_show();

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

该部分的代码逻辑很简单,初始化各个外设之后,进入死循环,XL_TIME6_time_show 周期输出光敏的值。

3.光敏传感器获取及输出

void XL_TIME6_time_show(void)
{
    if (g_time_1s >= TIME_PERIODIC_1S)
    {
        g_time_1s = g_time_1s - TIME_PERIODIC_1S;

        float tmp_value = adc_get_temperature();
        printf("tmp_value:%f\r\n", tmp_value);

        uint32_t lsens_value = adc_get_lsens_val();
        printf("lsens:%d\r\n", lsens_value);

    }

    if (g_time_10s >= TIME_PERIODIC_10S)
    {
        g_time_10s = g_time_10s - TIME_PERIODIC_10S;

        RTC_TimeTypeDef sTime = {0};
        HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
        printf("H:%d, M:%d, S:%d\r\n", sTime.Hours, sTime.Minutes, sTime.Seconds);

        RTC_DateTypeDef sDate = {0};
        HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
        printf("Y:%d, M:%d, W:%d, D:%d\r\n", sDate.Year, sDate.Month, sDate.WeekDay, sDate.Date);

    }
}

33.4 下载验证

将程序下载到开发板后,光敏值周期输出,通过餐厅纸遮盖光敏传感器,光敏值在不断地变化:
在这里插入图片描述

我们可以通过给 LS1 不同的光照强度,来观察 LSENS_VAL 值的变化,光照越强,该值越大,光照越弱,该值越小,LSENS_VAL 值的范围是 0~100。

33.5 STM32CubeMX

在这里插入图片描述

33.6 源码路径

git clone git@gitee.com:xiaoliangliangcong/stm32.git
STM32F407ZGT6/11. light_sensor

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐