《嵌入式-STM32开发指南》第三部分 外设篇 - 第5章 光敏传感器

Posted Bruceoxl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《嵌入式-STM32开发指南》第三部分 外设篇 - 第5章 光敏传感器相关的知识,希望对你有一定的参考价值。

5.1 理论分析

5.1.1光敏电阻概述

光敏电阻或光导管,常用的制作材料为硫化镉,另外还有硒、硫化铝、硫化铅和硫化铋等材料。这些制作材料具有在特定波长的光照射下,其阻值迅速减小的特性,其工作原理是基于内光电效应。这是由于光照产生的载流子都参与导电,在外加电场的作用下作漂移运动,电子奔向电源的正极,空穴奔向电源的负极,从而使光敏电阻器的阻值迅速下降。

光敏电阻对光线十分敏感,其在无光照时,呈高阻状态,暗电阻一般可达1.5MΩ。光敏电阻的特殊性能,随着科技的发展将得到极其广泛应用。

光敏电阻器一般用于光的测量、光的控制和光电转换(将光的变化转换为电的变化)。常用的光敏电阻器硫化镉光敏电阻器,它是由半导体材料制成的。光敏电阻器对光的敏感性(即光谱特性)与人眼对可见光(0.4~0.76)μm的响应很接近,只要人眼可感受的光,都会引起它的阻值变化。设计光控电路时,都用白炽灯泡(小电珠)光线或自然光线作控制光源,使设计大为简化。

通常,光敏电阻器都制成薄片结构,以便吸收更多的光能。当它受到光的照射时,半导体片(光敏层)内就激发出电子—空穴对,参与导电,使电路中电流增强。为了获得高的灵敏度,光敏电阻的电极常采用梳状图案,它是在一定的掩膜下向光电导薄膜上蒸镀金或铟等金属形成的。一般光敏电阻器结构如下图所示。

光敏电阻器通常由光敏层、玻璃基片(或树脂防潮膜)和电极等组成。光敏电阻器在电路中用字母“R”或“RL”、“RG”表示。

光敏电阻常用硫化镉(CdS)制成。它分为环氧树脂封装和金属封装两款,同属于导线型(DIP型),环氧树脂封装光敏电阻按陶瓷基板直径分为Ø3mm、Ø4mm、Ø5mm、Ø7mm、Ø11mm、Ø12mm、Ø20mm、Ø25mm 。



5.2 实验详解

5.2.1实验目的

1.通过实验掌握STM32 ADC获取方法

5.2.2实验设备

硬件:PC 机一台;STM32开发板一套;光敏电阻传感器模块一个
软件:Windows 10系统,Keil5集成开发环境

5.2.3硬件连接

光敏电阻传感器模块电路如下图所示:

DO:数字开关量输出(0 和 1),DO 输出端可以与单片机直接相连,通过单片机来检测高低电平,由此来检测环境的光线亮度改变

AO:模拟信号输出,最简单的模拟输出的电路如下:

光敏电阻传感器模块一般有有三线和四线的接法,四线相比三线多了AO。AO的使用相对比较复杂,需要用到ADC,本文将讲解如何使用AO来控制LED。DO就不讲了,就一个检测高低电平,比较简单。

本文使用的光敏电阻模块如下所示:

模块接线图如下:

STM32F103ZET6的ADC通道表如下:

本文将使用STM32的PC2作为光敏电阻的模拟输入,这里选择ADC1的通道12。



5.2.4 Lib_V3.5.0库实现

光敏电阻的本质就是将光信号转化为电信号,通过ADC的值来反映环境的灯光状态。

模拟输入输出-ADC

1.硬件初始化

SysTick_Init();

//LED初始化
LED_GPIO_Config();

/* USART1 配置模式为 115200 8-N-1,中断接收 */
USART1_Config();

/* ADC 初始化 */
ADCx_Init();

当然这里主要就是ADC的初始化,前面的章节也有讲过,但是这里还是再来看看。

首先是GPIO 的初始化,GPIO设置为模拟输入。

/**
  * @brief  ADC GPIO 初始化
  * @param  None
  * @retval None
  */
static void ADCx_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;  
	
	/* 打开 ADC IO端口时钟 */
	ADC_GPIO_APBxClock_FUN(ADC_GPIO_CLK, ENABLE );
	
	/* 配置 ADC IO 引脚模式 */
	GPIO_InitStructure.GPIO_Pin = ADC_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	
	/* 初始化 ADC IO */
	GPIO_Init(ADC_PORT, &GPIO_InitStructure);	
}

接下来就是配置ADC和DMA。

/**
  * @brief  配置ADC工作模式
  * @param  None
  * @retval None
  */
static void ADCx_Mode_Config(void)
{
	ADC_InitTypeDef ADC_InitStructure;	
  DMA_InitTypeDef DMA_InitStructure;

	/* 打开ADC时钟 */
	ADC_APBxClock_FUN(ADC_CLK,ENABLE );
	/* 打开DMA时钟 */
	RCC_AHBPeriphClockCmd(ADC_DMAx_CLK, ENABLE);
  
  /* 复位DMA控制器 */
	DMA_DeInit(ADC_DMA_CHANNEL);
	
	/* 配置 DMA 初始化结构体 */
	/* 外设基址为:ADC 数据寄存器地址 */
	DMA_InitStructure.DMA_PeripheralBaseAddr =(uint32_t)(&(ADCx->DR));	
	/* 存储器地址,实际上就是一个内部SRAM的变量 */
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue;	
	/* 数据源来自外设 */
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;	
	/* 数据长度 */
	DMA_InitStructure.DMA_BufferSize = 1;	
	/* 外设寄存器只有一个,地址不用递增 */
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	/* 存储器地址固定 */
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; 	
	/* 外设数据大小为半字,即两个字节 */
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	
	/* 内存数据大小也为半字,跟外设数据大小相同 */
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;	
	/* 循环传输模式 */
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;	
	/* DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响 */
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;	
	/* 禁止存储器到存储器模式,因为是从外设到存储器 */
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;	
	/* 初始化DMA */
	DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
	
	/* 使能 DMA 通道 */
	DMA_Cmd(ADC_DMA_CHANNEL , ENABLE);
  
	/* ADC 模式配置 */
	/* 只使用一个ADC,属于单模式 */
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	
	/* 禁止扫描模式,多通道才要,单通道不需要 */
	ADC_InitStructure.ADC_ScanConvMode = DISABLE ; 
	/* 连续转换模式 */
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
	/* 不用外部触发转换,软件开启即可 */
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	/* 转换结果右对齐 */
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	
	/* 转换通道1个 */
	ADC_InitStructure.ADC_NbrOfChannel = 1;			
	/* 初始化ADC */
	ADC_Init(ADCx, &ADC_InitStructure);
	
	/* 配置ADC时钟为PCLK2的8分频,即9MHz */
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	
	/* 配置 ADC 通道转换顺序为1,第一个转换,采样时间为55.5个时钟周期 */
	ADC_RegularChannelConfig(ADCx, ADC_CHANNEL, 1, ADC_SampleTime_55Cycles5);
		
  /* 使能ADC DMA 请求 */
	ADC_DMACmd(ADCx, ENABLE);
  
	/* 开启ADC ,并开始转换 */
	ADC_Cmd(ADCx, ENABLE);
	
	/* 初始化ADC 校准寄存器   */
	ADC_ResetCalibration(ADCx);
	/*等待校准寄存器初始化完成 */
	while(ADC_GetResetCalibrationStatus(ADCx));	
	/* ADC开始校准*/
	ADC_StartCalibration(ADCx);
	/*等待校准完成 */
	while(ADC_GetCalibrationStatus(ADCx));
	
	/* 由于没有采用外部触发,所以使用软件触发ADC转换  */
	ADC_SoftwareStartConvCmd(ADCx, ENABLE);
}

2.ADC读取

ADC_ConvertedValueLocal =(float)ADC_ConvertedValue*3.3/4096; 
printf("AD转换原始值 = 0x%04X \\r\\n", ADC_ConvertedValue); 
printf("计算得出电压值 = %f V \\r\\n",ADC_ConvertedValueLocal);

在main函数的循环中使用不断获取ADC的值,并打印出来。值得注意的是,这里使用的DMA读取,好处就是可以让出CPU干其他的事情。

3.控制LED
根据读取的ADC值来控制LED的状态。

最简单的实现如下:

if(ADC_ConvertedValueLocal > 2.0)
{
	LED1_ON;
}
else
{
	LED1_OFF;
}

当时这样存在一个问题,不管LED是否开着,只要读取的ADC的值大于阈值都会去操作LED,显然有些不合理,因此需要去读取LED的状态。

/**
  * @brief  读取LED的状态
  * @param  LED_number:LED编号
  * @retval LEDState_OFF:关
  *         LEDtate_ON:  开
  */
uint8_t LED_GetState(uint8_t LED_number)
{
  uint8_t led_state = LEDState_ON;
  
  switch(LED_number)
  {
    case 0:
      if((GPIOB->ODR & GPIO_Pin_0) == 0
        led_state = LEDState_OFF;
      break;
    default:
      led_state = LEDState_ON;
      break;
  }
  return led_state;
  
}

这得注意的是,不同的板子的电路有些不一样,有的是输出高电平点亮,有的是低电平,因此LED的亮灭需要根据自己的板子调整以下代码。

if((GPIOB->ODR & GPIO_Pin_0) == 0

笔者这里是高电平点亮LED

最后整个主函数的代码如下:

/**
  * @brief  mian
  * @param  None
  * @retval int
  */
int main(void)
{	
	SysTick_Init();

	//LED初始化
	LED_GPIO_Config();

	/* USART1 配置模式为 115200 8-N-1,中断接收 */
	USART1_Config();

	/* ADC 初始化 */
	ADCx_Init();
	
	printf("光敏电阻传感器\\r\\n");

  /* 无限循环读取ADC */  
  while (1)
  {     
		
    ADC_ConvertedValueLocal =(float)ADC_ConvertedValue*3.3/4096; 
	
		printf("AD转换原始值 = 0x%04X \\r\\n", ADC_ConvertedValue); 
		printf("计算得出电压值 = %f V \\r\\n",ADC_ConvertedValueLocal); 
		
		if(ADC_ConvertedValueLocal > 2.0)
		{
			if(LED_GetState(0) == 0)
				LED1_ON;
		}
		else
		{
			if(LED_GetState(0) == 1)
				LED1_OFF;
		}
		
		Delay_ms(1000);		
  }
}

5.2.5 HAL库实现

我们在串口的例子的基础上进行配置。

串口通信(HAL库)

模拟输入输出-ADC(HAL库)

打开工程,打开Analog选项,配置ADC参数。

具体配置参数如下

使能连续转换模式(Continuous Conversion Mode)。设置转换周期。其他为默认设置。

值得注意的是,ADC 的输入时钟不得超过14MHz,它是由PCLK2经分频产生,需要调整ADC输入的分频系数。

根据DMA通道预览可以知道,我们用的ADC1对应DMA1 的通道1。

点击DMASettings 点击 Add 添加通道,选择ADC1传输速率设置为中速,DMA传输模式为正常模式,DMA内存地址自增,每次增加一个word(字)。

值得注意的是,STM32cubeMX默认开启了DMA的中断。

还需要设置中断优先级,进入NVIC配置表,简单配置下即可,如下图所示。

另外还需要将LED的引脚配置为输出。

最后生成代码即可。

接下来是实现代码。

HAL库的好处是用户不用谢太多代码,主需要在主函数开启DMA传输即可。

HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&ADC_ConvertedValue, 1);

其他就可标准库是一样的逻辑了。

最后给出主函数代码:

/**
  * @brief  The application entry point.
  * @retval int
  */
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_ADC1_Init();
  /* USER CODE BEGIN 2 */
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 1);
	HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&ADC_ConvertedValue, 1);
	
	printf("光敏电阻传感器\\r\\n");
		
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		ADC_ConvertedValueLocal =(float)ADC_ConvertedValue*3.3/4096; 
	
		printf("AD转换原始值 = 0x%04X \\r\\n", ADC_ConvertedValue); 
		printf("计算得出电压值 = %f V \\r\\n",ADC_ConvertedValueLocal); 
		
		if(ADC_ConvertedValueLocal > 2.0)
		{
			if(LED_GetState(0) == 0)
				HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);;
		}
		else
		{
			if(LED_GetState(0) == 1)
				HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);;
		}
		
		HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

是不是很简单。

5.2.6实验现象

打开串口助手,将模块放在不同的光照条件下,串口会打印出ADC的值,也就代表了光的强度。

ADC的值越小,说明光照越小。




代码获取方法

1.长按下面二维码,关注公众号[嵌入式实验楼]
2.在公众号回复关键词[STM32F1]获取资料




欢迎访问我的网站

BruceOu的哔哩哔哩
BruceOu的主页
BruceOu的博客
BruceOu的CSDN博客
BruceOu的简书

以上是关于《嵌入式-STM32开发指南》第三部分 外设篇 - 第5章 光敏传感器的主要内容,如果未能解决你的问题,请参考以下文章

《嵌入式-STM32开发指南》第三部分 外设篇 - 第4章 超声波测距

《嵌入式-STM32开发指南》第三部分 外设篇 - 第4章 超声波测距

《嵌入式-STM32开发指南》第三部分 外设篇 - 第5章 光敏传感器

《嵌入式-STM32开发指南》第三部分 外设篇 - 第5章 光敏传感器

《嵌入式-STM32开发指南》第三部分 外设篇 - 第1章 温湿度传感器DHT11

《嵌入式-STM32开发指南》第三部分 外设篇 - 第1章 温湿度传感器DHT11