目录
前言
对于刚刚入门单片机的同学来说,有可能会接触到制作智能台灯,那么其中有一个很常见也很重要的功能——台灯亮度随外界环境光的大小而变化。那么我们应该怎么实现这个功能,首先肯定需要有可以检测外界环境光强度的传感器,很多人会想到光敏电阻,确实网上有很多现成的光敏电阻模块可以检测外界光强,但这里我们是使用另一种模块来检测光强——TEMT6000模块。学会了这个模块的使用后,智能台灯就只剩下PWM调灯功能了。
但在这里,着重讲解TEMT6000传感器怎么使用,同时也简单说明一下什么是ADC。
一、TEMT6000环境光传感器
产品概述: 基于TEMT6000的环境光(可见光传感器,对可见光照度的反应特性与人眼的特性类似,可以模拟人对环境光线的强度的判断,从而方便做出与人友好互动的应用。可应用于照明控制、屏幕背光控制等。
产品特性:仅对可见光敏感,不需要额外的过滤镜良好的线性输出
产品参数:工作电压:DC3.3-5.5V工作温度:-40--85°C照度范围:1--1000Lux
输出信号:模拟电压,在5工作电压情况范围0-5V
产品引脚定义:
二、ADC
1.什么是ADC
ADC(analog to digital conversion) 模拟数字转换。意思就是将模拟信号转化成数字信号(将模电转化为数电),举个简单的例子:用万用表测量某电路电压的操作我相信大家都会,但大家有没有想过一个问题,万用表的两个表笔怼到电路上时,万用表凭什么说这一条电路是5V,3.3V等,难不成这一条电路有名字叫5V吗?肯定不是,是因为电路中原始的电压模拟量进入万用表后,万用表自动将这个我们不知道的模拟量计算并转化为我们可以看到的数字量5V或3.3V等,这个就叫做ADC(analog to digital conversion) 模拟数字转换。
2.环境光传感器TEMT6000与ADC的关系
由传感器实物图可以看到,传感器有三个引脚,VCC,GND,还有一个信号输出脚,那么信号输出脚是输出什么信号呢——它输出的是原始的电压信号,该原始的电压信号会随着外界环境光的增大而电压增大,所有我们要是获得了这个电压信号就等于可以知道外界环境光强度有多大了。
那我们要怎么获取传感器的输出电压信号呢?就需要用到ADC,将传感器原始电压信号转化为我们看得懂的数字信号,进而由单片机来处理这个数字信号,实现灯光亮度变化。
三、单片机选型
直接选用STM32为主控,使用其内部的ADC进行采集,ADC采集的精度比STC单片机的高。
四、代码
1.sensor_AD.c
代码中的ADC不是使用ADC+DMA的方式,如果需要使用多个ADC的话,可以在使用各个传感器的时候把各自的ADC开关打开。
环境光传感器的电压变量存放在TEMT6000_sensor_data字符串里,然后电压转化为Lux单位是存放在Lux字符串中,可供后续串口输出或显示屏输出使用。
#include "stm32f10x.h" // Device header
//----------------------------------------------------------------
unsigned int TEMT6000_sensor_ADC_ConvertedValue;
float TEMT6000_sensor_ADC_ConvertedValueLocal;//环境光传感器电压值变量
char TEMT6000_sensor_data[7];
float TEMT6000_sensor_Lux;
char Lux[8];
//----------------------------------------------------------------
/*单通道的ADC采集*/
void Adc_Config(void)
{
/*定义两个初始化要用的结构体,下面给每个结构体成员赋值*/
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/*
使能GPIOA和ADC1通道时钟
注意:除了RCC_APB2PeriphClockCmd还有RCC_APB1PeriphClockCmd,那么该如何选择?
APB2:高速时钟,最高72MHz,主要负责AD输入,I/O,串口1,高级定时器TIM
APB1:低速时钟,最高36MHz,主要负责DA输出,串口2、3、4、5,普通定时器TIM,USB,IIC,CAN,SPI
*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72M/6=12, ADC的采样时钟最快14MHz
/*配置输入电压所用的PA0引脚*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //GPIO_Mode_AIN:模拟输入(还有其他什么模式?请看下面的附录图1)
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位,将ADC1相关的寄存器设为默认值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //工作模式:ADC1和ADC2独立工作模式 (还有其他什么模式?请看下面的附录图2)
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //数模转换工作:扫描(多通道)模式=ENABLE、单次(单通道)模式=DISABLE
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//数模转换工作:连续=ENABLE、单次=DISABLE
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //ADC转换由软件触发启动 (还有其他什么模式?请看下面的附录图3)
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐 除了右就是左:ADC_DataAlign_Left
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目 范围是1-16
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADC1的寄存器
/*为啥要设置下面这一步?
细心的你可以发现上面初始化了一个引脚通道,初始化了一个ADC转换器,但ADC转换器并不知道你用的是哪个引脚吧?
这一步目的是:设置指定ADC的规则组通道(引脚),设置它们的转化顺序和采样时间
函数原型:void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, u8 ADC_Channel, u8 Rank, u8 ADC_SampleTime)
参数1 ADCx:x可以是1或者2来选择ADC外设ADC1或ADC2
参数2 ADC_Channel:被设置的ADC通道 范围ADC_Channel_0~ADC_Channel_17
参数3 Rank:规则组采样顺序。取值范围1到16。
ADC_SampleTime:指定ADC通道的采样时间值 (取值范围?请看下面的附录图4)
*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5 );
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC 注意:函数ADC_Cmd只能在其他ADC设置函数之后被调用
/*下面4步按流程走,走完就行*/
ADC_ResetCalibration(ADC1); //重置指定的ADC的校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待上一步操作完成
ADC_StartCalibration(ADC1); //开始指定ADC的校准状态
while(ADC_GetCalibrationStatus(ADC1));//等待上一步操作按成
}
void TEMT6000_ADC1(void)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_239Cycles5 );
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));
TEMT6000_sensor_ADC_ConvertedValue=ADC_GetConversionValue(ADC1);
TEMT6000_sensor_ADC_ConvertedValueLocal =(float) TEMT6000_sensor_ADC_ConvertedValue/4096*5.0; //环境光传感器读取数据
TEMT6000_sensor_data[0]='0'+((int)(TEMT6000_sensor_ADC_ConvertedValueLocal*1000))/1000;
TEMT6000_sensor_data[1]='.';
TEMT6000_sensor_data[2]='0'+((int)(TEMT6000_sensor_ADC_ConvertedValueLocal*1000))/100%10;
TEMT6000_sensor_data[3]='0'+((int)(TEMT6000_sensor_ADC_ConvertedValueLocal*1000))/10%10;
TEMT6000_sensor_data[4]='0'+((int)(TEMT6000_sensor_ADC_ConvertedValueLocal*1000))%10;
TEMT6000_sensor_data[5]='V';
TEMT6000_sensor_data[6]='\0';
TEMT6000_sensor_Lux=464.99*TEMT6000_sensor_ADC_ConvertedValueLocal-58.92;//电压与光照强度的函数
Lux[0]='0'+((int)TEMT6000_sensor_Lux)/1000;
Lux[1]='0'+((int)TEMT6000_sensor_Lux)/100%10;
Lux[2]='0'+((int)TEMT6000_sensor_Lux)/10%10;
Lux[3]='0'+((int)TEMT6000_sensor_Lux)%10;
Lux[4]='L';
Lux[5]='u';
Lux[6]='x';
Lux[7]='\0';
}
2.sensor_AD.h
#ifndef __sensor_AD_H
#define __sensor_AD_H
//#include "sensor_AD.H"
void Adc_Config(void);
void TEMT6000_ADC1(void);
#endif