单片机光敏传感器型号_51单片机光敏传感器怎么显示数据 - CSDN
  • 光敏传感器是最常见的传感器之一,它的种类繁多,主要有:光电管、光电倍增管、光敏电阻、光敏三极管、太阳能电池、红外线传感器、紫外线传感器、光纤式光电传感器、色彩传感器、CCD和CMOS图像传感器等。光传感器是...

    光敏二极管

    光敏二极管简介

    光敏传感器是最常见的传感器之一,它的种类繁多,主要有:光电管、光电倍增管、光敏电阻、光敏三极管、太阳能电池、红外线传感器、紫外线传感器、光纤式光电传感器、色彩传感器、CCD和CMOS图像传感器等。光传感器是目前产量最多、应用最广的传感器之一,它在自动控制和非电量电测技术中占有非常重要的地位。

    光敏传感器是利用光敏元件将光信号转换为电信号的传感器,它的敏感波长在可见光波长附近,包括红外线波长和紫外线波长。光传感器不只局限于对光的探测,它还可以作为探测元件组成其他传感器,对许多非电量进行检测,只要将这些非电量转换为光信号的变化即可。

    光敏二极管工作原理

    光敏二极管也叫光电二极管。光敏二极管与半导体二极管在结构上是类似的,其管芯是一个具有光敏特征的PN结,具有单向导电性,因此工作时需加上反向电压。

    • 当无光照时,有很小的饱和反向漏电流,即暗电流,此时光敏二极管截止;
    • 当受到光照时,饱和反向漏电流大大增加,形成光电流,它随入射光强度的变化而变化。当光线照射PN结时,可以使PN结中产生电子一空穴对,使少数载流子的密度增加。这些载流子在反向电压下漂移,使反向电流增加。

    因此,可以利用光照强弱来改变电路中的电流(光线越强,电流越大)

    简而言之:照射光敏二极管的光强不同,通过光敏二极管的电流大小就不同,所以可以通过检测电流大小,达到检测光强的目的。利用这个电流变化,我们串接一个电阻,就可以转换成电压的变化,从而通过ADC读取电压值,判断外部光线的强弱。

     

    STM32监测光强

    硬件连接

    • 单片机:STM32F103ZET6
    • 模块:光敏二极管模块
    • 引脚连接:LIGHT SENSOR:PF8
    • 硬件资源:指示灯DS0、TFTCLD模块、ADC

    具体的硬件连接的图如下所示:

    图中,LS1是光敏二极管,R34为其提供反向电压,当环境光线变化时,LS1两端的电压也会随之改变,从而通过ADC3_IN6通道,读取LIGHT_SENSOR(PF8)上面的电压,即可得到环境光线的强弱。光线越强,电压越低;光线越暗,电压越高。

     

    STM32控制程序

    STM32监测光强的主要步骤:

    • 初始化GPIO、开启ADC;
    • 读取ADC结果,并将结果转化为光强。
    //初始化ADC3
    //这里我们仅以规则通道为例
    //我们默认仅开启通道6																	   
    void  Adc3_Init(void)
    {      
    	ADC_InitTypeDef ADC_InitStructure; 
    
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3	, ENABLE );	  //使能ADC3通道时钟
    	
      RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,ENABLE);//ADC复位
    	
      RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,DISABLE);//复位结束	    
    	
    	ADC_DeInit(ADC3);  //复位ADC3,将外设 ADC3的全部寄存器重设为缺省值
    	
    	
    	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式: 独立模式
    	ADC_InitStructure.ADC_ScanConvMode = DISABLE;	//模数转换工作在单通道模式
    	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;	//模数转换工作在单次转换模式
    	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//转换由软件而不是外部触发启动
    	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//ADC数据右对齐
    	ADC_InitStructure.ADC_NbrOfChannel = 1;	//顺序进行规则转换的ADC通道的数目
    	ADC_Init(ADC3, &ADC_InitStructure);	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器  
    	
    	
    	ADC_Cmd(ADC3, ENABLE);	//使能指定的ADC3
    	
    	ADC_ResetCalibration(ADC3);	//使能复位校准  
    	 
    	while(ADC_GetResetCalibrationStatus(ADC3));	//等待复位校准结束
    	
    	ADC_StartCalibration(ADC3);	 //开启AD校准
     
    	while(ADC_GetCalibrationStatus(ADC3));	 //等待校准结束
    }		 
    //获得ADC3某个通道的值
    //ch:通道值 0~16
    //返回值:转换结果
    u16 Get_Adc3(u8 ch)   
    {
      //设置指定ADC的规则组通道,一个序列,采样时间
    	ADC_RegularChannelConfig(ADC3, ch, 1, ADC_SampleTime_239Cycles5 );	//ADC3,ADC通道,采样时间为239.5周期	  			    
      
    	ADC_SoftwareStartConvCmd(ADC3, ENABLE);		//使能指定的ADC3的软件转换启动功能	
    	 
    	while(!ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC ));//等待转换结束
    
    	return ADC_GetConversionValue(ADC3);	//返回最近一次ADC3规则组的转换结果
    } 
    //初始化光敏传感器
    void Lsens_Init(void)
    {
      GPIO_InitTypeDef GPIO_InitStructure;
    	
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);//使能PORTF时钟	
    	
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//PF8 anolog输入
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
    	GPIO_Init(GPIOF, &GPIO_InitStructure);	
    	Adc3_Init();
    }
    //读取Light Sens的值
    //0~100:0,最暗;100,最亮 
    u8 Lsens_Get_Val(void)
    {
    	u32 temp_val=0;
    	u8 t;
    	for(t=0;t<LSENS_READ_TIMES;t++)
    	{
    		temp_val+=Get_Adc3(LSENS_ADC_CHX);	//读取ADC值
    		delay_ms(5);
    	}
    	temp_val/=LSENS_READ_TIMES;//得到平均值 
    	if(temp_val>4000)temp_val=4000;
    	return (u8)(100-(temp_val/40));
    }
     int main(void)
     {	 
     	u8 adcx; 
    	delay_init();	    	 //延时函数初始化	  
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
    	uart_init(115200);	 	//串口初始化为115200
    	LED_Init();		  		//初始化与LED连接的硬件接口
      	LCD_Init();				//初始化LCD
    	Lsens_Init(); 			//初始化光敏传感器
    	POINT_COLOR=RED;//设置字体为红色  	
    	//显示提示信息											      
    	LCD_ShowString(30,50,200,16,16,"WarShip STM32");	
    	LCD_ShowString(30,70,200,16,16,"LSENS TEST");	
    	LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
    	LCD_ShowString(30,110,200,16,16,"2015/1/14");	  
    	POINT_COLOR=BLUE;//设置字体为蓝色
    	LCD_ShowString(30,130,200,16,16,"LSENS_VAL:");	             
    	while(1)
    	{
    		adcx=Lsens_Get_Val();
    		LCD_ShowxNum(30+10*8,130,adcx,3,16,0);//显示ADC的值 
    		LED0=!LED0;
    		delay_ms(250);	
    	}
    }

    整个程序还是比较简单、基础的,如果对于ADC的相关知识不是很理解的话,可以转到【STM32】ADC的基本原理、寄存器(超基础、详细版)【STM32】ADC库函数、一般步骤详解(实例:内部温度传感器实验)

     

    展开全文
  • 2)掌握光敏和热敏传感器在协议栈的用法。 2实验设备 硬件:PC 机一台;ZB2530(底板、核心板、仿真器、USB 线)一套;光敏或热敏传感器一个 软件:win7 系统,IAR 8.20 集成开发环境 3实验相关电路图 ...

    1实验目的

    1)通过实验掌握 CC2530 芯片 GPIO 的配置方法;
    2)掌握光敏和热敏传感器在协议栈的用法。

    2实验设备

    硬件:PC 机一台;ZB2530(底板、核心板、仿真器、USB 线)两套;光敏或热敏传感器一个

    软件:win7 系统,IAR 8.20 集成开发环境

    3实验相关电路图

    接线方法:
    1)、VCC:接电源正极(3V3)
    2)、GND:接电源负极
    3)、DO:TTL 开关信号输出
    4)、AO:模拟信号输出(悬空没有使用)

    4实验分析

    仔细核对引脚后将传感器插到 J8 光敏传感器、热敏传感器、气体传感器、一氧化碳传感器、酒精传感器等共用 P0.5 引脚,不过配置不同, 使用光敏传感器、热敏传感器、气体传感器、一氧化碳传感器、酒精传感器时配置成输入引脚。

    光敏只需读取与之相连 IO 口电平, 来判断当前环境是亮还是暗。所以在协议栈增加光敏传感器检测程序也比较简单,我们只需要配置好 IO 口,然后周期性检测、输出显示,上传给协调器即可。
    (1)步骤一:配置光敏传感器用到的 IO 引脚
    这里写图片描述
    (2)步骤二:读取检测引脚电平,并输出显示,上传给协调。
    这里写图片描述
    (3)步骤三:接收数据。
    这里写图片描述

    (4)步骤四:再利用周期性点播的定时器,间隔 1 秒定时采集、输出显示,将采集到的信息发送给协调器,协调器通过串口调试助手显示,有 LCD 的朋友可以在协调器接收处加上 LCD 显示。

    5实验现象

    1)选择 CoodinatorEB-Pro, 下载到开发板 A;作为协调器,通过 USB 线跟电脑连接;
    2)选择 EndDeviceEB-Pro, 下载到开发板 B;作为终端设备无线发送数据给协调器,并接上光敏模块;
    3)给两块开发板上电,打开串口调试助手,设为:9600 8N1 并打开串口串口请选择自己的端口号。终端连网成功后会向协调器发数据,没有 LCD 请使用调试助手观察实验结果。

    本章参考代码

    点击进入

    展开全文
  • 最近在项目搞STM32和光敏电阻传感器,辛辛苦苦地找了厂家客服和很多资料,发现都没有光敏电阻阻值或者电压转换成光照强度的公式。 百度了一下,发现我还是太高估了光敏电阻的精确度了,大多数光敏电阻.

    这次来分享一次突发奇想的经历。文章主要是要实现将ADC模块获取的光敏电阻数值转换成标准单位勒克斯的光照强度,虽然说由于实验方法和实验环境,最终结果并不是很准确,但也算是一次创意小实验。
    最终的代码可以来这里下载:
    Android端
    MCU端

    一、前言(无关技术的废话,可以跳过)

    最近在项目搞STM32和光敏电阻传感器,辛辛苦苦地找了厂家客服和很多资料,发现都没有光敏电阻阻值或者电压转换成光照强度的公式。
    百度了一下,发现我还是太高估了光敏电阻的精确度了,大多数光敏电阻传感器只是提供一个大概的明暗程度的判断,有一些三线的光敏电阻传感器只是提供一个DO口,输出就是1位二进制表示的明和暗。四线的光敏电阻传感器就有提供一个AO口,输出的是12位二进制表示的电压,相比来说就准确了许多,但是不同厂家甚至是同一个厂家的不同光敏电阻,对于光照强度的转换相去甚远,所以基本上找不到一条符合所有光敏电阻的转换公式。如果要更加精准的光照强度,更多的是使用光敏二极管或者是数字照度仪等。
    但是,我不甘心呀,项目需要的是光照强度,而到手的光敏电阻传感器我也不想就这么地废了。所以我就想:能不能自己给光敏电阻传感器测试一下,计算出属于它的公式。实际上,我只要有一个能够测光照强度的东西就可以实现。突然想到,手机不就有这个玩意吗?虽然手机的也不够精准,但还是值得一试的,于是,我就有一个大胆的思路:

    1. 开发一个可以获取手机光线传感器数据并且存入数据库的APP;
    2. 将手机和我的光敏电阻传感器同步同向测试,获取两者的数据;
    3. 将手机获取的以勒克斯(lx)作为单位的数据,和光敏电阻的数据进行拟合,获得两者之间的转换公式。

    二、具体实现思路

    具体实现思路

    三、实验设备和环境

    1.软件

    • Keil uVision5
    • Android Studio 3.6.3
    • 串口调试助手
    • SQLite Expert Personal
    • Excel 2016

    2.硬件

    • 光敏电阻传感器(4线)
    • STM32F103C8T6
    • ST-Link
    • CH340(USB转TTL)
    • 手机(使用Android系统的)

    3.其它

    • 绝缘胶带
    • 手机支架(方便测试而已,没有的话可以不用)
    • 光源(比如:手电筒、台灯之类,本文用的是另一个手机的手电筒)

    四、Android端开发

    1.搭建界面

    首先在Android Studio中创建一个空白工程,因为本实验比较简单,所以只需要一个MainActivity就可以了。在activity_main.xml中添加简单的文字说明、两个按钮(开始和删除数据)以及ListView(用于展示数据)。
    搭建界面
    除此之外还要完成一个listview_item.xml的界面,这个就是用在ListView的每个item中的界面,在Adapter中适配。
    搭建界面2

    2.搭建SQLite数据库

    2.1创建类

    这里虽然我们要存储的数据很简单,只有光照强度这一个浮点数值,但是为了方便操作数据库,还是要创建一个类,同时为了和MCU端收集的数据进行匹配,也添加了一个自增的变量id。

    package com.peanuo.lighttest;
    
    public class Light {
    
        private int id;
        private double lux;
    
        public double getLux() {
            return lux;
        }
    
        public int getId() {
            return id;
        }
    
        public void setLux(double lux) {
            this.lux = lux;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    }
    
    

    2.2创建数据库Helper

    这里继承了SQLiteOpenHelper类来完成我们自定义的数据库的创建。在构造方法中创建数据库,在重写的onCreate方法中使用SQL语句创建数据表。然后是增加两个方法insertData和deleteData来方便操作数据库。最后的query方法是查询数据表的中全部,也就是用来给ListView展示数据的。

    package com.peanuo.lighttest.database;
    
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    import com.peanuo.lighttest.Light;
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    public class LightHelper extends SQLiteOpenHelper {
        private SQLiteDatabase sqLiteDatabase;
    
        public LightHelper(Context context) {
            super(context,"lightDB",null, 1);
            sqLiteDatabase = this.getWritableDatabase();
        }
    
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE light(id INTEGER PRIMARY KEY AUTOINCREMENT, light REAL)");
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }
    
    
        public boolean insertData(double light) {
            ContentValues values  = new ContentValues();
            values.put("light",light);
            return sqLiteDatabase.insert("light", null, values)>0;
        }
    
        public boolean deleteData(Context context){
            return context.deleteDatabase("lightDB");
        }
    
        public List<Light> query(){
            List<Light> list = new ArrayList<Light>();
            Cursor cursor = sqLiteDatabase.query("light", null, null, null, null, null, "id desc");
            if (cursor != null){
                while (cursor.moveToNext()){
                    Light info = new Light();
                    int id = cursor.getInt(cursor.getColumnIndex("id"));
                    double lux = cursor.getDouble(cursor.getColumnIndex("light"));
                    info.setId(id);
                    info.setLux(lux);
                    list.add(info);
                }
                cursor.close();
            }
            return list;
        }
    }
    
    

    2.3创建Adapter

    创建这个适配器就是用来给契合ListView和数据库的。这里要继承BaseAdapter类,然后实现getCount、getItem、getItemId、getView这些方法,最后的ViewHoled类是使用了优化加载list的方法。

    package com.peanuo.lighttest.database;
    
    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.TextView;
    
    import com.peanuo.lighttest.Light;
    import com.peanuo.lighttest.R;
    
    import java.util.List;
    
    public class LightAdapter extends BaseAdapter {
        private LayoutInflater layoutInflater;
        private List<Light> list;
    
        public LightAdapter(Context context, List<Light> list){
            this.layoutInflater = LayoutInflater.from(context);
            this.list = list;
        }
    
        @Override
        public int getCount() {
            return list==null ? 0 :list.size();
        }
    
        @Override
        public Object getItem(int position) {
            return list.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if(convertView == null){
                convertView = layoutInflater.inflate(R.layout.listview_item, null);
                holder = new ViewHolder();
                holder.id = convertView.findViewById(R.id.item_id);
                holder.light = convertView.findViewById(R.id.item_light);
                convertView.setTag(holder);
            }else  {
                holder = (ViewHolder) convertView.getTag();
            }
            Light info = (Light) getItem(position);
            holder.id.setText(String.valueOf(info.getId()));
            holder.light.setText(String.valueOf(info.getLux()));
            return convertView;
        }
    
        class ViewHolder{
            TextView id,light;
        }
    }
    
    

    3.在MainAcivity使用传感器服务获取数据

    MainAcivity中要先创建SensorManager和Sensor,分别是所有传感器服务的变量以及具体传感器的变量。TYPE_LIGHT就是指向光线传感器的。在activity处于onResume时,要注册传感器服务,在处于onPause时要注销。

            sensorManager = (SensorManager)this.getSystemService(SENSOR_SERVICE);
            assert sensorManager != null;
            sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
            
        @Override
        protected void onResume() {
            super.onResume();
            sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            sensorManager.unregisterListener(sensorEventListener);
        }
    

    创建自定义的线程来实现测试,同时在其中使用runOnUiThread来更新界面数据。(由于我对Android的线程还不太了解,所以此处这样处理并不是最佳的)

    //开始测试的线程
        private class TestThread extends Thread{
            @Override
            public void run() {
                super.run();
                while (isTesting){
                    try {
                        Thread.sleep(4000);
                        if (lightHelper.insertData(light)) {
                            Log.i("Light","光照强度:"+light);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        Log.i("Thread","测试线程已停止");
                        //break;
                    }
    
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            showData();
                        }
                    });
                }
            }
        }
    

    五、MCU端开发

    1.使用STM32CubeMX配置引脚

    首先根据自己的MCU型号创建工程,然后在SYS中根据自己使用的调试器选择Debug。我这里是使用ST-Link,所以选择Serial Wire。
    MCU端开发
    接着,就是配置ADC和USAR了。根据自己的板子随便选,我这里选择的是ADC1 IN8和USART1。这两个外设的配置都保持默认的参数就行了。
    最后在生成代码之前,要去Project设置好名称、文件位置和对应的IDE。然后点击靠近右上角的GENERATE CODE就行了。
    MCU端开发2
    注意一下,可以点击Code Generator里面在下面图示的位置打一个钩,这样生成的代码文件就会分类放好,如果没有打勾的话,就会全部放在main文件中。
    MCU端开发3

    2.在Keil中设置串口发送和ADC电压数值的读取

    找到STM32CubeMX生成的代码文件,打开Keil工程文件。
    首先,我们要将芯片的启动文件添加进去工程。(不知道此处是我的Keil配置问题还是STM32Cube本身的问题,生成的代码总是得自己亲手添加启动文件进去工程)
    注意,在使用STM32CubeMX生成的代码,在Keil中编辑的时候自己添加的代码要在BEGIN注释和END注释之间,否则用STM32CubeMX重新生成代码之后,不在两个注释之间的代码会被清除。

    2.1设置串口发送的函数

    要现在usart.h头文件中声明函数void u1_printf(char* fmt, …);
    然后在usart.c文件中添加串口发送的函数。这里使用的是类似于printf的方法。

    void u1_printf(char* fmt,...)  
    {  
    	uint8_t i,j; 
    	va_list ap; 
    	va_start(ap,fmt);
    	vsprintf((char*)USART1_TX_BUF,fmt,ap);
    	va_end(ap);
    	i=strlen((const char*)USART1_TX_BUF);		//此次发送数据的长度
    	for(j=0;j<i;j++)							//循环发送数据
    	{
    		while((USART1->SR&0X40)==0);			//循环发送,直到发送完毕   
    		USART1->DR=USART1_TX_BUF[j];  
    	} 
    }
    

    2.2设置ADC的读取函数

    同样,首先要在头文件中声明函数uint16_t read_adc(void);
    然后到c文件中添加函数。
    注意此函数读取的数字不是标准单位的电压伏特值,而是ADC读取得使用12位二进制表示的电压,还未按照比例换算成电压伏特值。由于我的目标是转换成光照强度,所以先按比例转换成电压没有必要,所以就直接使用ADC读取的数值了。

    uint16_t read_adc(void)
    {
    	uint16_t temp;
    	HAL_ADC_Start(&hadc1);
    	HAL_ADC_PollForConversion(&hadc1, 50);
      if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
    	{
    		temp = HAL_ADC_GetValue(&hadc1);
    	}
    	return temp;
    }
    

    3.在主循环中读取电压和发送数据

      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
    		HAL_Delay(3000);
    		id++;
    		light = read_adc();
    		u1_printf("id: %d  light: %d\r\n", id, light);
      }
      /* USER CODE END 3 */
    

    六、实际测试

    1.测试前准备

    • 找一个比较暗的地方做实验。
    • 将实验手机开启开发者模式,然后允许USB安装,使用USB连接到电脑,通过Android Studio调试的方法将app安装到手机上。
    • 在Keil将开发好的工程编译下载到开发板上
    • 把光敏电阻传感器的引脚接到开发板对应的GPIO口引脚。
    • 将CH340连接到电脑以及串口对应的开发板的引脚。
    • 用绝缘胶带将光敏电阻传感器贴在手机靠近光线传感器的部分,并且朝向要和手机正面一样,每个手机的位置都不太一样,不过基本都是在屏幕上部听筒的附近。
    • 打开串口助手和端口,准备接收串口数据。
    • 打开手机APP,这里最好的话手机可以一直开着开发者模式,保持充电,而且开启“充电时保持不休眠”的功能。

    2.正式测试

    • 做好上述的准备工作之后,就可以着手开始了。
    • 要先启动单片机,之后MCU端会有3秒的等待时间(LED灯会闪烁),时间到了就点击手机APP的开始按钮。这样才能保证两端是同时开始的。
    • 然后就使用外部的光源逐渐地改变亮度。尽量让亮度的范围广一点,才能让拟合计算出来的数据更加准确。

    实际测试

    七、数据拟合处理

    1.数据汇总

    • 将串口助手收集到的数据复制到excel中,在excel中使用分列的方式将id和light数据提取出来放在工作表Sheet1中
    • Android端的数据库可以通过Android Studio提取出来,将手机连接到电脑,打开Android Studio的Device File Explorer,查看连接至电脑的设备,可以直接获取我们的数据库文件,路径是:data/data/自己应用的包名/databases,然后就可以看到我们的数据库文件了,点击save as保存,此处要自己添加一个后缀名.db。

    数据拟合处理2

    • 在SQLite Expert Personal打开我们保存的数据库,然后复制数据到excel的Sheet2。
    • 在excel的Sheet3中将两个数据的id进行匹配,最后整理成两列数据,如下图所示。

    数据拟合处理

    2.拟合处理

    • 选中两列数据,然后插入散点图。
    • 选择散点图,添加趋势线,然后在趋势线选项中点击显示R平方值。
    • 选择“指数”、“多项式”、“乘幂”等不同的趋势线,对比R平方值,选择最接近1的一个,就大功告成了,可以看到实验拟合的结果了。

    数据拟合处理3

    展开全文
  • BH1750FVI光照传感器超详细攻略 一、文章说明 之所以写这篇文章,原因有两个。 一是:有个师弟跟我说我发布的文章都偏向于工作者,能不能写一些大学生能用到的东西,我想了一下,确实是,我写的文章大多是我在工作中...

    一、文章说明

    之所以写这篇文章,原因有两个。
    一是:有个师弟跟我说我发布的文章都偏向于工作者,能不能写一些大学生能用到的东西,我想了一下,确实是,我写的文章大多是我在工作中总结出来的心得,对于初学者来说确实有点难以理解。
    二是:我觉得这个光照传感器很多大学生都能用到,但是网上的教程虽多却也不一定能够帮助大家深入了解这款传感器。大家更多的是看完攻略之后能够驱动,但是其实并不了解它的工作原理,想要在光照传感器的基础上增加别的功能也无从下手。
    所以,我觉得我还是有必要写一篇更加详细更加深入的攻略来帮助大家理解。我觉得能驱动一个芯片和会驱动一个芯片是不一样的,如果你学会了如何去驱动一个芯片,那么换了别的类似的芯片你也能够得举一反三。不然的话你每次换一个芯片都只能去找人家写好的代码。
    好了,废话不多说了,BH1750的讲解马上开始。(注:请一定要从头到尾看下去,粗略看一下也行,因为内容是环环相扣的,一直看,一直爽!!!)
    我再多说一句,就一句,真的,接下来我讲的所有代码以及相关的所有文件都可以免费发给你们,链接在文章底部,自己去下载吧。

    二、芯片介绍

    BH1750FVI是一款数字型光强度传感器集成芯片。某宝上面很多写着GY30模块,那些其实也是用BH1750FVI芯片,只不过是它把BH1750FVI芯片以及外围的一些电路做到了一个板子上面,然后把BH1750FVI的通讯引脚引出来方便你们用单片机控制而已。(话说大部分国产芯片都是这个套路,把人家的芯片拿过来,加一点外围电路,然后重新包一层外壳,换个型号,就变成自己的产品了)
    电路工作原理:如图1所示,BH1750的内部由光敏二极管、运算放大器、ADC采集、晶振等组成。PD二极管通过光生伏特效应将输入光信号转换成电信号,经运算放大电路放大后,由ADC采集电压,然后通过逻辑电路转换成16位二进制数存储在内部的寄存器中(注:进入光窗的光越强,光电流越大,电压就越大,所以通过电压的大小就可以判断光照大小,但是要注意的是电压和光强虽然是一一对应的,但不是成正比的,所以这个芯片内部是做了线性处理的,这也是为什么不直接用光敏二极管而用集成IC的原因)。BH1750引出了时钟线和数据线,单片机通过I2C协议可以与BH1750模块通讯,可以选择BH1750的工作方式,也可以将BH1750寄存器的光照度数据提取出来。
    在这里插入图片描述
    引脚定义:

    引脚号 名称 说明
    1 VCC 供电电压源正极
    2 SCL IIC时钟线,时钟输入引脚,由MCU输出时钟
    3 SDA IIC数据线,双向IO口,用来传输数据
    4 ADDR IIC地址线,接GND时器件地址为0100011 ,接VCC时器件地址为1011100
    5 GND 供电电压源负极

    三、IIC通讯介绍

    IIC通讯过程简介

    既然BH1750是用IIC通讯的,那么我们就要先了解IIC的通讯原理。IIC由时钟线(SCL)和数据线(SDA)组成。时钟线,听这个名字就知道和时间有关系,没错,它其实管理着IIC的通讯时间。而数据线,顾名思义就是用来传输数据的线。那么时钟线和数据线它们是什么关系呢?你可以把时钟线理解为红绿灯,高电平是绿灯,低电平是红灯,而数据线传输的每一个数据则相当于一辆汽车,高电平是奔驰,低电平是宝马。当绿灯亮了的时候,汽车就可以过去,只不过这里的交通规则是每亮一次绿灯,只能通过一辆汽车。所以,IIC通讯的过程就是红绿灯交替闪烁(也就是时钟线输出方波脉冲),汽车跟着一辆一辆的过去,过去的是奔驰,就是传输了一个“1”,过去的宝马,就是传输了一个“0”,连续传输8次,就可以组成一个8位的二进制数,也就是一个字节的数据,反复这个过程就能实现两个设备之间的通讯。
    好,上面已经大概讲解了IIC的通讯过程,那么下面来补充一些细节。IIC通讯的两个设备是有主从关系的,比如我们的单片机在这里就是主设备,BH1750是从设备。
    时钟线是由主设备输出,从设备输入的,也就是单片机和BH1750通讯的时候,单片机的IO口要给SCL引脚输出一个方波脉冲,因为IIC设备支持的最大通讯频率一般都是400kHz,也就是说一个时钟周期(一个高电平加一个低电平为一个周期)不能小于2.5us。单片机输出时钟的时候一定要注意高低电平延时的时间,延时的时间越长,通讯的速率越慢。另外,时钟线不会一直输出脉冲,只会在需要通讯的时候输出,并且要遵循一定的规则。需要通讯的时候时钟线先要输出一个“起始信号”告诉从设备我要开始通讯了,其实就是电平由高到低跳变,但是这个高电平的持续时间不能太短,具体最少要多少时间需要看芯片手册,反正延长一点准没错。然后再根据固定的时间输出高低脉冲,直到到了要停止通讯的时候,时钟线要输出一个“结束信号”告诉从设备我不通讯了,其实就是电平一直拉高。
    而数据线传输的数据是双向的,单片机可以给BH1750发数据,也可以读取BH1750的数据(也就是BH1750给单片机发)。需要注意的,单片机给BH1750发的数据不是随便发的,也要符合一定的规则。首先,单片机要先发一个器件地址(器件地址是7位的,详细的内容我后面再说),再发送一个读写位(0表示是写入,1表示读取),器件地址和读写位加起来刚好是一个字节,然后BH1750会给你回一个应答位,意思就是“我收到了”。然后单片机就可以接着发送数据了,每次都是以1个字节为间隔发。收也是类似的,只是把单片机发数据改成收数据,这里就不多说了,后面会详细讲。(注:器件地址是用来区分从设备的,因为有时候同一根时钟线和数据线可能会连接多个从设备,也就是说主设备发送的数据所有的从设备都可以收到,所以主设备要先发送一个器件地址,告诉所有的从设备我是给哪个设备发命令,其他设备收到了也不要执行)。

    IIC通讯实例

    下面我们看一个实际的例子。图2是OPT3001通讯的读写过程,(OPT3001是我在项目中用到一款低功耗光照传感器,和BH1750类似,也是IIC通讯协议,感兴趣的同学可以看一下我发之前的博文,有讲解这个IC的驱动方式),看懂了这个图你就理解IIC的通讯方式了,你就可以当着博主的面大声地说“你写的博文有毛用,你说的我全都知道”,如果你还有不理解的地方,那么就坐下来好好听我解说吧。
    首先,我们看一下IIC的写入过程,最左边先是有一个“Start by Master”,也就是单片机先给一个“起始信号”,然后后面接着传输了8位数据(1 0 0 0 1 A1 A0 R/W)。其中,“1 0 0 0 1 A1 A0”是器件地址,因为这里的器件地址有4个可选,所以用了A1和A0表示,(注:BH1750只有2个器件地址),“R/W”是读写位,上面我有说到,这里是写入,所以这里的R/W应该是一个“0”。 接着是“ACK by OPT3001”,这是从设备给主设备发的应答,就是说“你发的数据我收到了,你可以接着发了”,然后接下来的RA7-RA0是寄存器地址(因为寄存器不止一个所以要先发地址,告诉它你接下来要把数据存到哪里),再后面的D15-D0是两个字节的数据(这些数据就是存到前面发的那个地址的寄存器里面)。
    读取的过程和写入类似,先是“起始信号”,再是器件地址+读写位,接着是应答,然后开始接收数据(单片机的IO口要从输出改成输入了),D15-D0是接收到两个字节的数据,“ACK by Master”是单片机给OPT3001发的应答。(只要是接收的一方都要发应答,不应答的话通讯就会结束,比如读取的第二个字节后面的“No ACK by Master”)
    在这里插入图片描述
    好,如果你能坚持看到这里,那我敬你是条汉子!!如果你看懂了,那么恭喜你,如果没看懂,那也没关系,上面那是IIC一般的通讯方式,后面BH1750的通讯要更加简单。
    (问:那你为什么不直接讲BH1750。答:我喜欢,你咬我呀,略略略….啪,略略啪,略别别….我错了。)

    BH1750的通讯过程

    其实前面之所以要先讲这个OPT3001而不是直接讲BH1750,是因为BH1750的IIC其实算是一个简化版的,不具有通用性,你学会了OPT3001的通讯方法,你再去驱动BH1750就很简单,相反,如果你只会驱动BH1750,那么换成别的IIC的芯片你就不一定会了。
    好了,接下来我们来看一下BH1750的通讯,BH1750的通讯过程可以分成5步,中间3步如图3所示。
    (啪啪,问:为什么要用英文的图,别以为我不知道有中文版的手册,说你是不是在装*。答:冤枉,真不是,那个中文版的图太糊了,而且英文版其实也不影响大家去看,老实说我是一个英语学渣,我还写了一篇博文讲述一个学渣如何看懂英文数据手册,有兴趣的同学可以看一下。真不是打广告哦。)
    在这里插入图片描述
    第1步:发送上电命令。(上电命令是0x01)。
    因为这里没有图,我就不详细说了,发送的过程和第2步基本一致。就是把测量命令(0x10)改成上电命令(0x01)。
    第2步:发送测量命令。
    下面图片上的例子,ADDR引脚是接GND的,发送的测量命令是“连续高分辨率测量(0x10)”。
    发送数据的过程和之前讲的OPT3001写入的过程基本一样,先是“起始信号(ST)”,接着是“器件地址+读写位”(器件地址我在上面引脚定义那里有写),然后是应答位,紧接着就是测量的命令“00010000”(关于测量命令,下面会详细说明),然后应答,最后是“结束信号(SP)”。(相比于OPT3001的写入过程,BH1750少了一个发送寄存器地址的步骤,因为它只有一个寄存器,所以就没必要了)
    第3步:等待测量结束。
    测量的时间手册上面有写,我这里就不列出来了,高分辨率连续测量需要等待的时间最长,手册上面写的是平均120ms,最大值180ms,所以为了保证每次读取到的数据都是最新测量的,程序上面可以延时200ms以上,当然也不用太长,浪费时间。如果你用别的测量模式,等待时间都比这个模式要短。
    第4步:读取数据。
    先是“起始信号(ST)”,接着是“器件地址+读写位”,然后是应答位,紧接着接收1个字节的数据(单片机在这个时候要把SDA引脚从输出改成输入了),然后给BH1750发送应答,继续接收1个字节数据,然后不应答(因为我们接收的数据只有2个字节,收完就可以结束通讯了),最后是“结束信号(SP)”。
    第5步:计算结果。
    接收完两个字节还不算完成,因为这个数据还不是测量出来的光照强度值,我们还需要进行计算,计算公式是:光照强度 =(寄存器值[15:0] * 分辨率) / 1.2 (单位:勒克斯lx)
    因为我们从BH1750寄存器读出来的是2个字节的数据,先接收的是高8位[15:8],后接收的是低8位[7:0],所以我们需要先把这2个字节合成一个数,然后乘上分辨率,再除以1.2即可得到光照值。
    例如:我们读出来的第1个字节是0x12(0001 0010),第2个字节是0x53(0101 0011),那么合并之后就是0x1253(0001 0010 0101 0011),换算成十进制也就是4691,乘上分辨率(我用的分辨率是1),再除以1.2,最后等于3909.17 lx。

    四、BH1750的命令

    BH1750所有的命令都在图4。这次我用的是中文版的图,方便大家看,有点糊勿怪。 这里的指令虽然多,但是实际上如果仅仅是测光照值,只用两条就够了,通电指令和测量指令。这里的几条测量指令我就不详细说了,手册上是有讲的,如果后面你们需要的话我再补上吧。寄存器也只有一个,没什么好说的。(才不是因为懒也不是因为天天都要加班)

    在这里插入图片描述

    五、BH1750编程教学

    下面的编程我以stm32为例,其实换成51,stm8或者别的单片机,程序也基本一样的,不同的单片机在程序上只是引脚配置的写法不太一样,别的基本没差别。
    我的这个程序是用OLED显示光照强度的,想用串口,蓝牙或者别的方式也可以。
    注:我下面展示的程序跟我发给你们的工程会有一点不一样,主要是备注,因为为了让你们更好理解,我展示的代码是加了很多备注的,而工程是以前的,备注会少一点。

    1、IIC驱动代码

    //IIC的驱动程序没必要自己去写,能够看懂每一个函数的作用,知道IIC的通讯过程即可,我这里用的是正点原子的例程
    //IIC通讯最基本的几个函数是:起始信号,结束信号,发送应答(或不应答),发送1个字节数据,接收1个字节数据
    //这些我前面都有讲到,如果你前面看懂了,将上面OPT3001的时序图和这个程序结合起来看你就很容易想明白
    
    //闲话(可以跳过):这一份程序是以前大学做项目的时候写的,其实大部分都是抄的,当时对程序的理解也是一知半解
    //现在回头看,发现这个程序的兼容性很差
    //虽然在这个工程上面运行是没有问题的,但是如果移植到别的工程或者用别的单片机,需要改动的地方就很多了
    //比如引脚的拉高拉低,这里是直接写“SDA=1;”,但是这个SDA的定义是在正点原子自己写的一个库里面的
    //如果你用别的工程,没有把这个库加进来,那么这个定义就不成立了
    //最好的写法我觉得是分成两个定义SDA_High和SDA_Low
    //然后在头文件声明#define SDA_High GPIO_SetBits(GPIOB,GPIO_Pin_0) 
    //#define SDA_GPIO_ResetBits(GPIOB,GPIO_Pin_0) 
    //这样写的好处是如果要移植,只需要把 GPIO_SetBits(GPIOB,GPIO_Pin_0) 这部分换掉就行了
    //比如用51,我们就可以把GPIO_SetBits(GPIOB,GPIO_Pin_0)换成P1_0=1
    //同样的IIC通讯的延时函数delay_us,这里用的是定时器,也是要用到正点原子的库delay.c
    //其实这里我觉得可以用for函数延时,因为延时的时间比较短,也不需要很精确
    //如果换了一个单片机,晶振频率不同,只需要改一下for函数延时的次数
    //然后用示波器量一下这个时间,确保是在正常通讯的时间范围内即可
    
    /***起始信号***/
    void BH1750_Start()
    {
      SDA=1;                    //拉高数据线
      SCL=1;                   //拉高时钟线
      delay_us(5);                 //延时
      GPIO_ResetBits(bh1750_PORT, sda);                    //产生下降沿
      delay_us(5);                 //延时
      GPIO_ResetBits(bh1750_PORT, scl);                    //拉低时钟线
    }
    
    /*****停止信号******/
    void BH1750_Stop()
    {
        SDA=0;                   //拉低数据线
        SCL=1;                      //拉高时钟线
        delay_us(5);                 //延时
        GPIO_SetBits(bh1750_PORT, sda);                    //产生上升沿
        delay_us(5);                 //延时
    }
    
    /**************************************
    发送应答信号
    入口参数:ack (0:ACK 1:NAK)
    **************************************/
    void BH1750_SendACK(int ack)
    {
    	GPIO_InitTypeDef GPIO_InitStruct;
    	
      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;  
      GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStruct.GPIO_Pin = sda;
      GPIO_Init(bh1750_PORT, &GPIO_InitStruct);  
    	
    	if(ack == 1)   //写应答信号
    		SDA=1; 
    	else if(ack == 0)
    		SDA=0; 
    	else
    		return;			
      SCL=1;     //拉高时钟线
      delay_us(5);                 //延时
      SCL=0;      //拉低时钟线
      delay_us(5);                //延时
    }
    
    /**************************************
    接收应答信号
    **************************************/
    int BH1750_RecvACK()
    {
      GPIO_InitTypeDef GPIO_InitStruct;
      GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;  /*这里一定要设成输入上拉,否则不能读出数据*/
      GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
      GPIO_InitStruct.GPIO_Pin=sda;
      GPIO_Init(bh1750_PORT,&GPIO_InitStruct);
    	
      SCL=1;            //拉高时钟线
      delay_us(5);               //延时	
      if(GPIO_ReadInputDataBit(GPIOA,sda)==1)//读应答信号
        mcy = 1 ;  
      else
        mcy = 0 ;				
      SCL=0;                    //拉低时钟线
      delay_us(5);                 //延时
      GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
      GPIO_Init(bh1750_PORT,&GPIO_InitStruct);
      return mcy;
    }
    
    /**************************************
    向IIC总线发送一个字节数据
    **************************************/
    void BH1750_SendByte(uchar dat)//dat是要发送的一个字节的数据
    {
      uchar i;
      for (i=0; i<8; i++)         //8位计数器
      {
    	if( 0X80 & dat )         	//如果要发送的是1
          GPIO_SetBits(bh1750_PORT,sda);
        else                        //如果要发送的是0    
          GPIO_ResetBits(bh1750_PORT,sda);
    	dat <<= 1;      //for循环每执行一次,要发送的数据左移1位,循环8次就把一个字节的数据发送出去了
        SCL=1;               //拉高时钟线
        delay_us(5);             //延时
        SCL=0;                //拉低时钟线
        delay_us(5);            //延时
      }
      BH1750_RecvACK();
    }
    
    /**************************************
    在IIC总线接收一个字节数据
    **************************************/
    uchar BH1750_RecvByte()
    {
      uchar i;
      uchar dat = 0;  //dat是存放接收到的一个字节的数据
      uchar bit;
    	
      GPIO_InitTypeDef GPIO_InitStruct;
      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;   /*这里一定要设成输入上拉,否则不能读出数据*/
      GPIO_InitStruct.GPIO_Pin = sda;
      GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(bh1750_PORT,&GPIO_InitStruct );
    	
      GPIO_SetBits(bh1750_PORT,sda);          //使能内部上拉,准备读取数据,
      for (i=0; i<8; i++)         //8位计数器
      {
        dat <<= 1;       //循环8次,每次接收一个位,8次之后完成一个字节数据的接收
        SCL=1;               //拉高时钟线
        delay_us(5);             //延时
    			
    	if( SET == GPIO_ReadInputDataBit(bh1750_PORT,sda))//读取SDA引脚的电平,如果是高电平,就是传输“1”
          bit = 0X01;
        else                     //电平传输的是“0”
          bit = 0x00;  
    	dat |= bit;           //读数据    
    	SCL=0;                //拉低时钟线
        delay_us(5);          //延时
      }		
      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
      GPIO_Init(bh1750_PORT, &GPIO_InitStruct );
      return dat;
    }
    

    2、BH1750写入和读取的函数

    //上面讲了IIC的几个基本的函数,包括了发送1字节和接收1字节
    //但是和BH1750通讯,不仅仅是发送1个字节或者接收1个字节那么简单
    //我们对BH1750发送命令的时候,是要先发送器件地址+写入位,然后发送指令
    //读取数据的时候,需要先发送器件地址+读取位,然后连续读取2个字节
    //如果我上面说的你都懂了,那么你就可以去看代码了,如果能跟时序图一一对应上,你就理解代码了
    
    //另外,如果你用的不是BH1750,而是别的IIC通讯的芯片,这两个函数的写法可能也是不同的
    //比如OPT3001,发送命令的时候不仅发发送命令数据还需要发送寄存器地址,所以一般函数定义的时候就要定义两个变量
    //又或者一些命令是两个字节的,那么你定义的变量类型就需要注意了,函数里面也需要多发送一个字节数据
    //这里不理解也无所谓,不影响学习BH1750的驱动,以后你做项目用到了别的芯片你可能就突然理解了
    
    //写入指令
    void Single_Write_BH1750(uchar REG_Address)//REG_Address是要写入的指令
    {
      BH1750_Start();                  //起始信号
      BH1750_SendByte(SlaveAddress);   //发送设备地址+写信号
      BH1750_SendByte(REG_Address);    //写入指令
      BH1750_Stop();                   //发送停止信号
    }
    
    //读取指令
    void mread(void)
    {   
      uchar i;	
      BH1750_Start();                          //起始信号
      BH1750_SendByte(SlaveAddress+1);         //发送设备地址+读信号
    
      //注意:这里的for函数的i<2和下面的if函数的i==2,我发现以前的工程写的居然是3
      //这里其实我们只需要读取2个字节就行了,后面的合成数据也是只用了BUF的前2个字节
      //工程文件我没改,这个驱动程序以前也用在了多个项目上,读取3个字节肯定是也可以正常运行的
      //但是我觉得还是改成2比较好,你们可以测试一下改成2有没有问题,测试之后一定要告诉我结果,谢谢!!
      for (i=0; i<2; i++)                      //连续读取2个数据,存储到BUF里面
      {
        BUF[i] = BH1750_RecvByte();          //BUF[0]存储高8位,BUF[1]存储低8位
        if (i == 2)
        {
          BH1750_SendACK(1);                //最后一个数据需要回NOACK
        }
        else
        {		
          BH1750_SendACK(0);                //回应ACK
        }
      }
      BH1750_Stop();                          //停止信号
      delay_ms(5);
    }
    

    3、BH1750初始化函数

    //初始化BH1750,根据需要请参考pdf进行修改****
    void Init_BH1750()
    {
      GPIO_InitTypeDef GPIO_InitStruct;
      /*开启GPIOB的外设时钟*/ 
      RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE); 
      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;  
      GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStruct.GPIO_Pin = sda | scl ;
      GPIO_Init(bh1750_PORT,&GPIO_InitStruct); 
    	
      Single_Write_BH1750(0x01);  
      delay_ms(180);            //延时180ms
    }
    

    4、获取光照度函数

    float read_BH1750(void)
    {
      int dis_data;                       //变量	
      float temp1;
      float temp2;
      Single_Write_BH1750(0x01);   //发送上电命令(0x01)
      Single_Write_BH1750(0x10);   //发送高分辨率连续测量命令(0x10)
      delay_ms(200); //等待测量结束,其实延时180ms就行了,延时200ms只是预留多一点时间,保证通讯万无一失
      mread();       //连续读出数据,存储在BUF中
      dis_data=BUF[0];
      dis_data=(dis_data<<8)+BUF[1]; //2个字节合成数据 
      temp1=dis_data/1.2;//计算光照度
      temp2=10*dis_data/1.2;//把光照度放大10倍,目的是把小数点后一位数据也提取出来	
      temp2=(int)temp2%10;//求余得到小数点后一位
      OLED_ShowString(87,2,".",12); //OLED显示小数点
      OLED_ShowNum(94,2,temp2,1,12);//OLED显示小数	
      return temp1;//返回整数部分
    }
    //这里写的程序还是有点乱的,小数部分直接在read_BH1750()显示,整数部分返回,在main()函数调用的时候显示
    //这...其实最好是要么都在这个函数显示,要么把temp1和temp2改成全局变量,然后都在main函数显示
    //这个变量的名字也是[捂脸],算了算了,往事不堪回首。要吐槽的地方有点多,也没时间去一一改了
    //不过其实也不影响你们学IIC通讯的编程方式,就这样吧
    

    5、main函数

    int main(void)
    { 
      float light;
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
      delay_init();	    	 //延时函数初始化	  
      uart_init(9600);	 	//串口初始化为9600
      LED_Init();				//初始化与LED连接的硬件接口
      Init_BH1750();       //初始化BH1750
      OLED_Init();     //初始化OLED
      OLED_Clear();     //清屏
    	
      while(1)
      {
        	light=read_BH1750();  //读取BH1750的光强数据
    		OLED_ShowString(0,2,"light:",12);  //显示光照强度
    		OLED_ShowNum(48,2,light,6,12);	//显示光照度
    		OLED_ShowString(110,2,"lx",12); //显示“lx”
    		if(light<100)//光照度小于100lx,点亮LED灯
    		{
    			LED1=0;
    			OLED_ShowString(38,5,"LED-ON ",12);
    		}
    		else
    		{
    			LED1=1;
    			OLED_ShowString(38,5,"LED-OFF",12);
    		}
      }		
    }	
    

    六、测试

    我这个程序是大学的时候做的一个课程设计,现在也没有实物可以测试了,我就发我以前报告里的图给你们看一下效果吧。图5是亮度大于100lx的时候,图6是低于100lx的时候。
    在这里插入图片描述在这里插入图片描述

    七、总结

    要驱动BH1750,或者其他IIC通讯的芯片,最好还是先了解IIC通讯的时序,了解通讯的原理,然后才是写驱动程序。驱动程序也可以分成三部分,第一部分是IIC通讯基本的协议(一般抄就完事了),第二部分是芯片的读写过程,需要根据实际芯片的通讯方式写。第三部分是指令控制相关的函数,BH175比较简单,只有测量和计算。有些可能还有多个设置不同的模式的函数,校验数据的函数等等,不过它们其实都是发送指令,只是发不同的指令执行不同的操作而已。
    OPT3001的驱动教程你们可以大概看一下:https://blog.csdn.net/ShenZhen_zixian/article/details/102876443

    最后再说点闲话吧,写到这里刚好有点感触,其实写博文的初衷只是为了把工作中总结出来的一些经验记录下来,加强记忆。因为像我们这种做硬件研发的,经验是最值钱的,然后上传资源也只是为了赚点积分,因为还在大学那会想下载别人的程序的时候总是恨自己没有积分。后来发现我写的博文和上传的资源能够帮助到一些人,所以我就一直坚持着更新,哪怕每天加班也会抽空写一下文章写一下代码,即使我自己赚的积分其实一次都没用过。可能这就是传承吧,以前遇到问题的时候总能在前辈的博文中找到答案,现在轮到自己分享自己的经验给后来者了,希望我的文章也能够帮助到你。

    源码下载链接:https://pan.baidu.com/s/1HnedCg3sC4HU8iEOf4dYOw ,提取码:xs8o

    创作不易,希望你们尊重别人的劳动,点赞+关注支持一下吧,谢谢大家了,博主也会继续更新更多的大学生专栏,如果你们还有什么问题,可以评论留言或者私信给我。

    展开全文
  • 使上面的环境光传感器开启模数转换(ADC),并将数据通过串口printf实时打印到屏幕。 这便是开发板的外形啦!哈哈哈!麻雀虽小,五脏俱全! 不同开发板各个通道可能不同,具体参考对应的说明...

    前言:本人上一篇博客介绍了关于STM32CubeMX的安装,这一篇博客将在STM32CubeMX进行实操。最近得到一块低功耗MCU:STM32L433CBT6的STM32开发板,于是利用无聊的寒假在上面打发时间。使上面的环境光传感器开启模数转换(ADC),并将数据通过串口printf实时打印到屏幕。

    这便是开发板的外形啦!哈哈哈!麻雀虽小,五脏俱全!
    不同开发板各个通道可能不同,具体参考对应的说明文档。
    在这里插入图片描述
    好的,下面开始操作……

    1.打开STM32CubeMX新建项目
    在这里插入图片描述
    2.在搜索框内搜索到自己的芯片型号,我的自然就是STM32L433CB
    在这里插入图片描述
    3.芯片构造,配置也在上面进行。在我的开发板上面可以看到环境光传感器对应的端口通道是PA4,选择ADC1外设,如图,可以看到颜色变成黄色,名字变成ADC1_IN9。
    在这里插入图片描述
    4.选择Alalog下的ADC1选项进行配置,将IN9选择IN9 Single-ended。可以看到ADC1使能变绿。
    在这里插入图片描述
    各项参数设置如下,都为默认设置。
    在这里插入图片描述

    5.同样对串口进行配置,发送和接收分别是PA9和PA10,名字分别是USART1_TX和USART1_RX。此时颜色为黄色,下面进行串口配置。
    在这里插入图片描述
    选择Connectivity下的USART1,通信方式为异步通信。
    在这里插入图片描述
    基本参数要记住,后面串口通信会用到。
    在这里插入图片描述
    串口配置初始化完成

    6.时钟树配置。因为本人对这个不是很了解,大概知道这个开发板的的串口通信是80MHz,直接输入80即可配置好。
    在这里插入图片描述
    7.项目管理,包括项目名字,路径,打开项目工具,本人使用的是keil 5。
    在这里插入图片描述
    勾选此处
    在这里插入图片描述
    8.初始化配置都已经完成了,生成报告以及代码。
    在这里插入图片描述
    有两种打开方式……
    在这里插入图片描述
    9.用keil 5打开代码可以看到各个模块c文件,其中有初始化函数,在Drivers/STM32l4xx_HAL_Driver文件夹中可以找到各个模块的操作函数,接下来就是编写代码了。
    在这里插入图片描述

    在main()函数前面声明变量保存AD采集的值。

    /* Private variables ---------------------------------------------------------*/
    
    /* USER CODE BEGIN PV */
    uint16_t AD_Light=0;
    /* USER CODE END PV */
    

    在main()函数while(1)循环里面添加函数声明变量保存AD采集的值

     while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
    		HAL_ADC_Start(&hadc1);//启动ADC转换
            HAL_ADC_PollForConversion(&hadc1, 50);//表示等待转换完成,第二个参数表示超时时间,单位ms
            if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))//获取ADC状态。判断转换完成标志位是否设置
    		{
    		     AD_Light=HAL_ADC_GetValue(&hadc1);//读取ADC转换数据
    			 printf("Environmental Light=%1.3fV\r\n",AD_Light*3.3f/4096);//串口打印到屏幕,*3.3f/4096是模数转换公式
    		}
    		HAL_Delay(1000);//延时1s
    	}
      /* USER CODE END 3 */
    }
    

    在usart.c中添加串口printf()函数

    /* USER CODE BEGIN 1 */
    #include<stdio.h>
    #ifdef __GNUC__
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif /* __GNUC__ */
    PUTCHAR_PROTOTYPE
    {
      HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
      return ch;
    }
    /* USER CODE END 1 */
    

    在usart.h中声明printf()函数

    #include "main.h"
    
    /* USER CODE BEGIN Includes */
    #include<stdio.h>
    /* USER CODE END Includes */
    
    

    10.编译程序并下载到开发板。本人采用的是J-LINK。步骤如下
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    11.打开串口调试助手,设置如下图,和之前的串口配置一致。接下来就可以测试了,手里拿着开了灯光的手机,串口助手上会不断显示环境光大小。
    在这里插入图片描述
    后序:这只是一个简单的ADC采集传感器通道电压,然后通过公式计算出光强的实例。之后还会介绍通过DMA的方式采集两个ADC通道电压。过两天再研究研究……

    展开全文
  • BH1750FVI是一种用于两线式串行总线接口的数字型光强度传感器采集范围:1-65535lx采用标准的I2C总线传输方式,BH1750...单片机通过I2C总线向传感器发送起始信号,并向传感器发送设备地址和写信号,发送完毕,主机等...
  • LRAO接单片机引脚,原理就是串联分压,当外界光发生变化的时候,光敏电阻的阻值发生变化,LRAO读取的就是光敏电阻分压的数值。 C4电容是用来滤除杂波降噪。 程序设计 LRAO接的是A1脚 程序使用ADC1,使用了DMA搬数据 ...
  • 声音传感器程序-STM32

    2020-07-30 23:32:18
    对声音信号进行采集,通过麦克风,以及LM386,采集声音和放大声音,通过STM32单片机对其数字信号和模拟信号进行处理。
  • SHT10型传感器属于SHT1×系列(其他常用型号还有SHT11、SHT15),SHT1×属于Sensirion温湿度传感器家族中的贴片封装系列。传感器将传感元件和信号处理电路集成在一块微型电路板上,输出完全标定的数字信号。传感器...
  • 51单片机作为基础入门的单片机应用十分广泛,一直以来基于51单片机的作品就层出不穷,推陈出新,有一段时间没有给大家整理关于51单片机的作品了,今天给大家分享电路城上最新的基于51单片机的作品,尤其是智能家居...
  • 经典更要有新思路,51单片机花式玩法,把经典玩出新高度 51单片机作为基础入门的一个单片机,还是应用最广泛的一种。因此有基于51单片机设计的经典之作,经典固然是经典,怎么在经典的基础上设计出更经典的作品应该...
  • 其中AI模式控灯是通过红外模块感应外界是否有人靠近来控制灯的亮灭及通过光敏传感器模块感应外界光照强度进而动态调整PWM占空比的输出来控制灯的亮度。 物联网是新一代信息技术的重要组成部分,也是“信息化”时代...
  • 上一篇提到了传感器,我们用的是红外灰度传感器,这里我们就对红外灰度传感器展开来说,理清原理实现,最后能够自己动手做出来。 一、原理分析  我们先来说一个最基本的电路原理:  在一个灯泡两端通电,灯泡...
  • 最近研究了隧道磁阻式传感器(以下简称为TMR)在汽车检测领域的应用,它是一种根据磁性材料的磁阻效应制成的新型传感器,被广泛应用于汽车、电力电子、磁信息读写、工业自动控制等领域。TMR将车辆引起的地磁扰动准确...
  • 旋转编码器是将旋转机械位移量转换为电器信号,对该信号进行处理后检测位置,速度等的传感器 旋转编码器可分为 “增量式”编码器和 “绝对值”式编码器 1.增量式编码器 旋转盘转动时,光敏二极管断续收到...
  • 一、机器人常用的单片机使用经验 一 学习单片机的捷径是什么? 所谓捷径就是少走弯路。我刚开始学单片机时走了不少弯路,很多朋友和我都有相似的经历,刚开始接触单片机,面对琳琅满目的图书教材,不知选择哪...
  • 想要快速入门单片机,就要多动手实践。也许你的手上有不止一块的单片机开发板,但是你有没有亲自动手实现自己的想法?单片机实现数字时钟是一个不错的想法。有12864液晶显示屏、有按键、有DS18B20。最关键的,你能从...
  • 控制系统是使用Arduino Mega2560单片机、继电器等;定位系统主要使用单片机控制舵机实现。测试结果显示:当几v电源供电时,单片机利用程序控制舵机水平、垂直角度。根据发射炮管角度的变化控制弹丸的落点,使得弹丸...
  • 本系统设计是从低成本、低功耗、高效率等概念出发,采用32位单片机STM32F103C8T6为处理核心,在数据采集节点端控制一系列的传感器(如DS18B20、DHT11、MQ-135、光敏二极管、雨滴传感器、土壤水分传感器等)来采集...
1 2 3 4 5 6
收藏数 103
精华内容 41
关键字:

单片机光敏传感器型号