手把手教你做基于stm32的红外、语音、按键智能灯光控制(上)

目录:

  • 1.系统实现目标
  • 2.硬件选型和软件准备
    • 2.1. 硬件选型
    • 2.2 软件准备
  • 3. 硬件IO表
  • 4.各个模块的驱动函数
    • 4.1. 红外遥控模块
    • 4.2. 按键模块
    • 4.3. LED灯
    • 4.4. BH1750光照度传感器
    • 4.5. 红外检测模块

1.系统实现目标

本文所设计的基于单片机的灯光控制系统主要由模式选择功能、手动模式和自动模式组成。

其中模式选择功能由以下功能组成:

  1. 当最小系统板上电以后,自动进入模式选择界面,通过按键和红外遥控器可以进行模式的选择,选择的模式可用通过显示屏显示。
  2. 在选择完成相应的模式以后,也可以通过按键或者红外遥控器选择进入手动模式或者自动模式,进入相应模式以后,就可以执行相应的功能。
  3. 当进入相应模式以后,如果想要切换模式,可以通过手动退出,然后继续在模式选择界面进行选择。

手动模式主要包括红外控制模式,语音控制模式和按键控制模式,下面来详细介绍一下这几种模式:

  1. 红外控制模式:使用带有红外遥控器的用户可以通过遥控器来对智能灯光进行控制。用户可以通过遥控器上的按键来打开/关闭灯光。
  2. 语音控制模式:使用语音助手的用户可以通过语音命令来控制智能灯光。用户可以说出指令,如“开灯”、“关灯”等,系统会解析语音指令并执行相应的操作。
  3. 按键控制模式:用户可以通过智能灯光控制系统配备的按键来控制灯光。
    以上三种模式都可供用户选择,并且用户可以在不同场景下,选择最为方便、快捷的方式来对智能灯光进行控制。

自动模式主要是通过红外检测传感器和光照度传感器来实现的,主要由以下功能:

  1. 当外部环境变暗的时候,光照度传感器会检测到光照度的降低并输出相应的信号,这些信号会传递给单片机进行处理,并且会使得相应的标志位进行标记。
  2. 当红外检测传感器检测到周围有人的时候,也会产生相应的信号传递给单片机,将相应的标志位进行置位。
  3. 当同时满足外部环境变暗并且周围有人的时候,这是系统会自动处理事件,将灯打开;当其中一个事件不满足的时候,灯光就会关闭。

视频演示链接:

链接: link

2.硬件选型和软件准备

2.1. 硬件选型

1.最小系统板
这次项目使用的板子是stm32f103c8t6最小系统板,这个板子在tb上都能够买到,随便一个最小系统板都可以,== 注意在买最小系统板的时候需要买一个stlink下载器来下载程序 。==
板子链接: link
在这里插入图片描述

在这里插入图片描述

2.显示模块
显示模块是使用的0.96英寸的OLED屏幕,注意是使用的IIC通信协议的OLED屏幕,不是使用SPI总线协议的。买的时候要看清楚。
链接: link
在这里插入图片描述
3.温湿度传感器
这次写的代码还顺便集成了检测温湿度的功能,所以要使用DHT11温湿度传感器,来采集温度。
链接: link
在这里插入图片描述
4. 红外检测模块
红外检测模块主要是用来在自动模式下检测周围环境是否有人存在的
链接: link
在这里插入图片描述
5. 红外遥控模块
红外遥控模块主要是在手动模式下来控制灯光的开关
链接: link
在这里插入图片描述
6. BH1750光照度传感器
链接: link
在这里插入图片描述
7.语音识别模块
链接: link
在这里插入图片描述
8.按键
按键是在手动模式下来实现按键控制LED灯目的的。
链接: link
在这里插入图片描述
9.LED灯珠
链接: link
在这里插入图片描述

2.2 软件准备

当我们把所有的硬件都准备完活以后,就要开始编写软件代码,编写软件代码之前,需要进行软件环境的准备,如果没有软件环境,我把软件环境放在了以下连接中:
https://download.csdn.net/download/nbbskk/87239099

3. 硬件IO表

在这里插入图片描述

4.各个模块的驱动函数

4.1. 红外遥控模块

红外遥控模块在手动模式下进行模式切换和手动开关灯。
.c文件

#include "remote.h"
#include "oled.h"

extern uint32_t flag_auto_manu;//自动模式,手动模式切换代码
extern uint32_t flag_in_system;//进入自动模式,手动模式的标志位
extern uint32_t flag_break_mode;//退出模式的标志位
extern uint32_t flag_led_on_off;//LED灯亮灭标志位
 
void Remote_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef Tim_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    RCC_APB2PeriphClockCmd( IR_GPIOx_LCK, ENABLE );             //使能端口x时钟
    RCC_APBxPeriphClockCmd( TIMx_LCK, ENABLE );                 //使能TIMx时钟
 
    GPIO_InitStructure.GPIO_Pin = IR_IO;                        //输入IO口 
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;		//浮空输入 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init( IR_GPIOx, &GPIO_InitStructure );                 //初始化I0口
 
/*--------------------时基结构体初始化-----------------------------*/
    Tim_TimeBaseStructure.TIM_Period = (10000-1);                           //设定计数器自动重装值 最大10ms溢出  
    Tim_TimeBaseStructure.TIM_Prescaler = (72-1);                           //预分频器,1M的计数频率,1us加1.   
    Tim_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;             //设置时钟分频系数
    Tim_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;         //TIM向上计数模式
    //Tim_TimeBaseStructure.TIM_RepetitionCounter = 0;                  //重复计数设置,高级计时器才有
    TIM_TimeBaseInit( IR_TIME, &Tim_TimeBaseStructure );                //初始化 TIMx定时器
 
/*--------------------输入捕获结构体初始化--------------------------*/
    TIM_ICStructure.TIM_Channel = TIM_Channel_x;                        //配置输入捕获的通道,根据具体的 GPIO 来配置
    TIM_ICStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;             //上升沿捕获
    TIM_ICStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;         //捕获输入通道选择
    TIM_ICStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;                   //不分频,每个变化沿都捕获 
    TIM_ICStructure.TIM_ICFilter = 0;                                   //被捕获的信号的滤波系数                                   
    TIM_ICInit( IR_TIME, &TIM_ICStructure );                            //初始化 定时器输入捕获
 
/*--------------------中断优先级配置--------------------------------*/
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_x );                   //设置中断组为 0
    NVIC_InitStructure.NVIC_IRQChannel = TIMx_IRQn;                     //设中断源
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;           //抢占优先级0级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;                  //子优先级1级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                     //使能IRQ通道
    NVIC_Init( &NVIC_InitStructure );                                   //初始化NVIC寄存器
    
    TIM_ClearFlag( IR_TIME, TIM_IT_Update|TIM_IT_CCx );                 // 清除更新和捕获中断标志位
    TIM_Cmd( IR_TIME, ENABLE );                                         //使能定时器x
    TIM_ITConfig( IR_TIME, TIM_IT_Update|TIM_IT_CCx, ENABLE );          // 开启更新和捕获中断    
    
}
 
u8  IRdatas = 0;                //接收到数据次数
u8  IRval  = 0;                 //接收到的按键值           
u8  RmtCnt = 0;                 //按键重复按下的次数
u8  Remote = 0;                 //遥控按键处理值,全局变量
TIM_ValueTypeDef TIM_Values;    //初始化结构体 
 
void TIMx_IRQHandler(void)                              //定时器x中断服务程序
{
    if ( TIM_GetITStatus( IR_TIME, TIM_IT_Update ) != RESET )
    {
        if ( TIM_Values.DataFlag == 1 )                 //是否接收到了引导码
        {
            if( TIM_Values.Period > 3 )                 //如果4次溢出中断(40ms)没收到数据,则要么开始收重复码,要么数据收完了
            {
                if( RmtCnt == 0 || TIM_Values.Period > 9 )                          //如果收到了领导码,且在4次溢出中间没有收到重复码,则判断收完数据,清0标志退出                                                       
                    TIM_Values.DataFlag = 0;                                                //或者收到重复码,且中间90ms没再触发中断,则判断收完数据,清0退出
            }
            TIM_Values.Period++;
        }
    } 
    if ( TIM_GetITStatus( IR_TIME, TIM_IT_CCx ) != RESET )                              //发生了上升沿或者下降沿事件?
    {
        if ( TIM_Values.StartFlag == 0 )                            //第一次上升沿捕获
        {   
            TIM_SetCounter( IR_TIME, 0 );                               //清0计数器
            TIM_OCxPolarityConfig( IR_TIME, TIM_ICPolarity_Falling );       //设置为下降沿捕获
            TIM_Values.CrrValue = 0;                //捕获值清0             
            TIM_Values.StartFlag = 1;               //开始下降沿捕获
            TIM_Values.Period = 0;              //自动重装载寄存器清0
        }
        else                //第二次捕获,下降沿捕获
        {
            TIM_Values.CrrValue = TIM_GetCapturex( IR_TIME );           //获取通道4 捕获寄存器的值
            TIM_OCxPolarityConfig( IR_TIME, TIM_ICPolarity_Rising );                        //设置为上升沿捕获
            TIM_Values.StartFlag = 0;                                   //开始标志复0,重新判断捕获上升沿
            TIM_Values.FinishFlag = 1;                                  //标记完成1次捕获流程
 
            if ( TIM_Values.FinishFlag == 1 )                           //判断是否完成一次捕获流程
            {
                if ( TIM_Values.DataFlag == 1 )                                     //是否接收到了引导码
                {
                    if ( TIM_Values.CrrValue > 300 && TIM_Values.CrrValue < 800 )           //560为标准值,560us
                    {
                        TIM_Values.Data <<= 1;                  //左移一位
                        TIM_Values.Data |= 0;                   //接收到0
                        IRdatas++;                      //接收到的数据次数加1.
                    }
                    else if ( TIM_Values.CrrValue > 1400 && TIM_Values.CrrValue < 1800 )    //1680为标准值,1680us
                    {
                        TIM_Values.Data <<= 1;                  //左移一位
                        TIM_Values.Data |= 1;                   //接收到1
                        IRdatas++;              //接收到的数据次数加1
                    }
 
                    /*这是 NEC 码规定的110ms连发码(由 9ms 低电平+2.5m 高电平+0.56ms 低电平
                    +97.94ms 高电平组成),如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,
                    即连发码,可以通过统计连发码的次数来标记按键按下的长短/次数。结束码 :0.56ms 高电平*/
 
                    else if ( TIM_Values.CrrValue > 2100 && TIM_Values.CrrValue < 2500 )    //得到按键键值增加的信息 2250为标准值2.25ms
                    {
                        if( RmtCnt > 3 )            //防止松开慢而误收到重复码,而抛弃前3次收到的重复码 
                            IRdatas++;              //接收到的数据次数加1
                        RmtCnt++;                   //按键次数增加1次  
                    }   
                }   
                else if ( TIM_Values.CrrValue > 4200 && TIM_Values.CrrValue < 4700 )        //4500为标准值4.5ms
                {
                    TIM_Values.DataFlag = 1;            //成功接收到了引导码,数据开始标志为1
                    IRdatas = 0;                        //等于1时收到引导码
                    RmtCnt = 0;                         //清除按键次数计数器
                    
                }
            }
            
/*---------------------------------------接收数据处理---------------------------------------*/
            
            switch ( IRdatas )      
            {
//              case 8:     //接收完特征码
//                  if( TIM_Values.Data != IR_ID )                      //如果接收到的特征码和使用遥控器不相同,则数据全部清0重来
//                  {
//                      IRdatas = 0;                    //接收数据次数清0
//                      TIM_Values.DataFlag = 0;        //开始接收数据标志复位为0  
//                  }
//                  TIM_Values.Data = 0;                //接收到的数据清0,只需要数据码
//                  break;
 
                case 16:    //接收完特征反码
                    //if ( (u8)~TIM_Values.Data != IR_ID )                  //如果知道遥控ID,想只能特定遥控器控制,则用这句与上面一句
                    if ( (u8)(TIM_Values.Data>>8) != (u8)~(TIM_Values.Data&0xff) )      //如果特征码和收到的特征反码,比较发现不同,则数据全部清0重来
                    {
                        IRdatas = 0;                    //接收数据次数清0
                        TIM_Values.DataFlag = 0;        //开始接收数据标志复位为0
                    }
                    TIM_Values.Data = 0;                //接收到的数据清0
                    break;
 
                case 24:    //接收完数据码
                    IRval = TIM_Values.Data;        //把数据码存到IRval
                    TIM_Values.Data = 0;            //接收到的数据清0,准备接收数据反码
                    break;
 
                case 32:    //接收完数据反码
                    if ( IRval != (u8)~TIM_Values.Data )        //如果数据码和接收到的数据反码不同,则数据码清0,重新开始
                    {
                        IRdatas = 0;                    //接收数据次数清0
                        IRval = 0;                      
                    }
                    TIM_Values.Data = 0;            //接收到的数据清0,准备接收下一次中断数据
                    Remote = IRval;          //把收到的按键值赋值给全局变量 Remote
                    IRdatas = 33;            //赋值为33防止在结束码时再进入中断,触发32的判断,导致数据清0
                    break;
                    
                case 34:    //重复码,如果想x个重复码算收到1次重复按键,就把34+x                  
                    Remote = IRval;
                    IRdatas = 33;            //重新赋值回33,防止重复按键次数太多,导致数值溢出,且不需要写后面的switch选择
                    break;
            }            
        }
    }
    TIM_ClearITPendingBit( IR_TIME, TIM_IT_Update | TIM_IT_CCx );       //清除中断标志位
}
 
//移植时根据不同的遥控按键值,更改case
void Remote_Scan( u8 str )              //遥控按键处理
{        
    Remote = 0;
    switch( str )
    {
            case 0XA2:
              	OLED_ShowNum(10,10,1,3,16,1);
				OLED_Refresh();
                break;
            case 0X68:
                 OLED_ShowNum(10,10,2,3,16,1);
				OLED_Refresh();
                break;  
            case 0X30:
				OLED_ShowNum(10,10,3,3,16,1);
				OLED_Refresh();
                break;
               
            case 0X18:
//				 OLED_ShowNum(10,10,4,3,16,1);
//				OLED_Refresh();
			     flag_led_on_off=!flag_led_on_off;//按下上键以后,选择开灯或者关灯
                break;
                //LCD_ShowString( 30, 70,"I SEE YOU" );  // 显示字符串
//                break;
            case 0x7A:
				 OLED_ShowNum(10,10,5,3,16,1);
				OLED_Refresh();
                //LCD_Image( 0, 0, 240, 135, imageLoge );
                break;
            case 0x10:
			     flag_auto_manu=!flag_auto_manu;//按下左键以后,选择自动模式和手动模式切换
                break;
            case 0x38://按下OK键以后,进入这个模式
				flag_in_system=1;
                break;
            case 0x5A:
				flag_break_mode=1;//按下右键以后,模式退出
                break;
            case 0x42:
				 OLED_ShowNum(10,10,9,3,16,1);
				OLED_Refresh();
                //LED_GPIO_Confing();
                //time = 0;
                //LCD_ShowNum( 100, 55, time, sizeof(time)-1 );
                break;            
    }
}

void remote_receive(void)
{
	if( Remote != 0 )              //如果变量里面有值则代表收到遥控按键了,如果有遥控按键值为0就需要改Remote的初始值和这里的判断了  
        {
            Remote_Scan( Remote );     //遥控按键处理
        }  
}



.h文件

#ifndef _REMOTE_H
#define _REMOTE_H
 
#include "stm32f10x.h"
 
//程序用到的是TIM4_CH4,移植时只需要改这里的IO口就可以了
#define IR_TIME         TIM3        //哪个时钟
#define TIM_Channel_x   TIM_Channel_3                           //配置输入捕获的通道,根据具体的 GPIO 来配置
#define RCC_APBxPeriphClockCmd  RCC_APB1PeriphClockCmd          //TIMx时钟使能函数,TIM在APBx?
 
#define TIMx_LCK        RCC_APB1Periph_TIM3               //TIMx的时钟
#define IR_GPIOx_LCK    RCC_APB2Periph_GPIOB             //红外输入IO口的时钟
#define IR_GPIOx        GPIOB                             //红外输入IO口
#define IR_IO           GPIO_Pin_0
 
// 中断相关宏定义
#define TIM_IT_CCx              TIM_IT_CC3                           //捕获中断
#define TIMx_IRQn               TIM3_IRQn                            //设触发中断源,不同的中断中断源不一样,IRQn_Type
#define TIMx_IRQHandler         TIM3_IRQHandler                      //TIMx的触发中断服务函数
 
// 捕获信号极性函数宏定义
#define TIM_GetCapturex            TIM_GetCapture3             //对应寄存器CRRx,获取寄存器的值,该寄存器用来存储捕获发生时, TIMx_CNT的值
#define TIM_OCxPolarityConfig      TIM_OC3PolarityConfig        //OCxPolarity即TIMX通道X的极性,上升下降中心对齐
#define NVIC_PriorityGroup_x       NVIC_PriorityGroup_2         //设置中断组2,( 2:2 )
 
#define IR_ID          0x80                         //遥控器ID识别码
 
typedef struct {
    uint8_t     FinishFlag;        // 捕获高电平结束标志位
    uint8_t     StartFlag;         // 捕获开始标志位
    uint16_t    CrrValue;          // 捕获寄存器的值
    uint16_t    Period;            // 自动重装载寄存器更新标志,产生了多少次中断
    uint8_t     DataFlag;          // 收到引导码标志
    uint16_t    Data;              // 接收到的数据
 
} TIM_ValueTypeDef;
 
 
 
void Remote_Init(void);                //红外初始化
void TIMx_IRQHandler(void);            //定时器TIMx中断服务函数
void Remote_Scan( u8 str );            //遥控按键处理  
void remote_receive(void);
 
 
#endif /* _REMOTE_H */

4.2. 按键模块

按键模块主要是用来在手动模式下进行模式切换和按键开关LED用
.c文件

#include "stm32f10x.h"                  // Device header
#include "delay.h"
#include "key.h"


extern uint32_t flag_auto_manu;//选择自动模式或者手动模式的标志位
extern uint32_t flag_in_system;
extern uint32_t flag_break_mode;
extern uint32_t flag_led_on_off;

void key_mode_option_init(void)//PA2引脚初始化
{
	GPIO_InitTypeDef    GPIO_InitStructure;
	EXTI_InitTypeDef      EXTI_InitStructure;
	NVIC_InitTypeDef     NVIC_InitStructure;
	
	//开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	//配置GPIO
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,& GPIO_InitStructure);
	
	//配置AFIO
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource2);

	
	//配置EXIT
	EXTI_InitStructure.EXTI_Line=EXTI_Line2;
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;	
	EXTI_Init(&EXTI_InitStructure);
	
	
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
	 NVIC_Init(&NVIC_InitStructure);
}


void key_mode_in_init(void)//PA3引脚初始化
{
	GPIO_InitTypeDef    GPIO_InitStructure;
	EXTI_InitTypeDef      EXTI_InitStructure;
	NVIC_InitTypeDef     NVIC_InitStructure;
	
	//开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	//配置GPIO
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,& GPIO_InitStructure);
	
	//配置AFIO
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource3);
	
	
	//配置EXIT
	EXTI_InitStructure.EXTI_Line=EXTI_Line3;
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;	
	EXTI_Init(&EXTI_InitStructure);
	
	
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStructure.NVIC_IRQChannel=EXTI3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
	NVIC_Init(&NVIC_InitStructure);
}


void EXTI2_IRQHandler(void)//PA2引脚中断
{
	delay_ms(10);
	if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2) == 0)
	{
		flag_auto_manu=!flag_auto_manu;//触发中断以后,手动模式和自动模式相互切换
		flag_break_mode=1;//如果已经进入模式以后,按下这个键可以退出,重新进入模式选择界面
	}
		
	
	EXTI_ClearITPendingBit(EXTI_Line2);//清除中断标志位
}


void EXTI3_IRQHandler(void)//PA3引脚中断
{
	delay_ms(10);
	if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3) == 0)
	{
		flag_in_system=1;//按下这个键后,可以进入这个模式
		flag_led_on_off=!flag_led_on_off;//当进入手动模式以后,按下这个键,可以切换LED灯的亮灭
	}
		
	EXTI_ClearITPendingBit(EXTI_Line3);
}




.h文件

#ifndef   __KEY_H
#define  __KEY_H


void key_mode_option_init(void);
void key_mode_in_init(void);



#endif


4.3. LED灯

LED灯自然就是要控制的部件了
.c文件

#include "stm32f10x.h"                  // Device header
#include "LED.h"

void  LED_Init(void)
{
	GPIO_InitTypeDef   GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);
}


void  LED1_on(void)
{
	GPIO_SetBits(GPIOA,GPIO_Pin_0);
}
void  LED1_off(void)
{
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);
}




.h文件

#ifndef    __LED_H
#define   __LED_H

void  LED_Init(void);
void  LED1_on(void);
void  LED1_off(void);




#endif

4.4. BH1750光照度传感器

.c文件

#include "delay.h"
#include "bh1750.h"

typedef   unsigned char BYTE;

void Single_Write_BH1750(uchar REG_Address)
{
   IIC_Start();                  //起始信号
   IIC_Send_Byte(BHAddWrite);   //发送设备地址+写信号
   IIC_Send_Byte(REG_Address);    //内部寄存器地址,
   IIC_Stop();                   //发送停止信号
}

void BH1750_GPIO_Init(void)       //BH1750 GPIO的初始化
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);     //使能端口时钟
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;     
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;          //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
     GPIO_Init(GPIOB, &GPIO_InitStructure);      
     GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);    
}

void BH1750_Config_Init(void)              //BH1750配置初始化
{
    BH1750_GPIO_Init();            //GPIO引脚配置
    Single_Write_BH1750(0x01);    //是一个信号,打开设备的信号 我的理解,,没看原理
}

void bh_data_send(u8 command)           
{
    do{
    IIC_Start();                      //iic起始信号
    IIC_Send_Byte(BHAddWrite);       //发送器件地址
    }while(IIC_Wait_Ack());           //等待从机应答
    IIC_Send_Byte(command);          //发送指令
    IIC_Wait_Ack();                   //等待从机应答
    IIC_Stop();                       //iic停止信号
}

u16 bh_data_read(void)
{
    u16 buf;
    IIC_Start();                       //iic起始信号
    IIC_Send_Byte(BHAddRead);         //发送器件地址+读标志位
    IIC_Wait_Ack();                     //等待从机应答
    buf=IIC_Read_Byte(1);              //读取数据
    buf=buf<<8;                        //读取并保存高八位数据
    buf+=0x00ff&IIC_Read_Byte(0);      //读取并保存低八位数据
    IIC_Stop();                        //发送停止信号 
    return buf; 
}


/*******************************
下面的都是 IIC 的一些操作  1113lc
***********************************/
//产生IIC起始信号
void IIC_Start(void)
{
    SDA_OUT();     //sda线输出
    IIC_SDA=1;            
    IIC_SCL=1;
    delay_us(4);
     IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
    delay_us(4);
    IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}      
//产生IIC停止信号
void IIC_Stop(void)
{
    SDA_OUT();//sda线输出
    IIC_SCL=0;
    IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
     delay_us(4);
    IIC_SCL=1; 
    IIC_SDA=1;//发送I2C总线结束信号
    delay_us(4);                                   
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
    u8 ucErrTime=0;
    SDA_IN();      //SDA设置为输入  
    IIC_SDA=1;delay_us(1);       
    IIC_SCL=1;delay_us(1);     
    while(READ_SDA)
    {
        ucErrTime++;
        if(ucErrTime>250)
        {
            IIC_Stop();
            return 1;
        }
    }
    IIC_SCL=0;//时钟输出0        
    return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=0;
    delay_us(2);
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
}
//不产生ACK应答            
void IIC_NAck(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=1;
    delay_us(2);
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
}                                          
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答              
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
    SDA_OUT();         
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        //IIC_SDA=(txd&0x80)>>7;
        if((txd&0x80)>>7)
            IIC_SDA=1;
        else
            IIC_SDA=0;
        txd<<=1;       
        delay_us(2);   //对TEA5767这三个延时都是必须的
        IIC_SCL=1;
        delay_us(2); 
        IIC_SCL=0;    
        delay_us(2);
    }     
}         
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
    unsigned char i,receive=0;
    SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
    {
        IIC_SCL=0; 
        delay_us(2);
        IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
        delay_us(1); 
    }                     
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

.h文件

#ifndef  __BH1750_H
#define __BH1750_H

//IO方向设置
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}       
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}

#define IIC_SCL    PBout(6) //SCL
#define IIC_SDA    PBout(7) //SDA     
#define READ_SDA   PBin(7)  //输入SDA 
#define ADDR 0x23//0100011
#define uchar unsigned char 

#define BHAddWrite     0x46      //从机地址+最后写方向位
#define BHAddRead      0x47      //从机地址+最后读方向位
#define BHPowDown      0x00      //关闭模块
#define BHPowOn        0x01      //打开模块等待测量指令
#define BHReset        0x07      //重置数据寄存器值在PowerOn模式下有效
#define BHModeH1       0x10      //高分辨率 单位1lx 测量时间120ms
#define BHModeH2       0x11      //高分辨率模式2 单位0.5lx 测量时间120ms
#define BHModeL        0x13      //低分辨率 单位4lx 测量时间16ms
#define BHSigModeH     0x20      //一次高分辨率 测量 测量后模块转到 PowerDown模式
#define BHSigModeH2    0x21      //同上类似
#define BHSigModeL     0x23      // 上类似

//BH1750 功能函数
void BH1750_Config_Init(void);
void bh_data_send(u8 command);
u16 bh_data_read(void);

//IIC所有操作函数    这些是必须要声明的,因为在c文件内部实现,顺序问题,在使用之前,必须对IIC函数声明,,           
void IIC_Start(void);                //发送IIC开始信号
void IIC_Stop(void);                  //发送IIC停止信号
void IIC_Send_Byte(u8 txd);            //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void);                 //IIC等待ACK信号
void IIC_Ack(void);                    //IIC发送ACK信号
void IIC_NAck(void);                //IIC不发送ACK信号




#endif

4.5. 红外检测模块

红外检测模块就是用来在自动模式下检测周围环境是否有人存在的。
.c文件

#include "stm32f10x.h"                  // Device header
#include "infrared.h"

uint32_t flag_infrared=0;


void infrared_Init(void)
{
	GPIO_InitTypeDef    GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//配置GPIO
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,& GPIO_InitStructure);
}

void infrared_read(void)
{
	if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)==0)//如果检测到引脚是低电平,说明有人
	{
		flag_infrared=1;
	}
	else//如果是高电平,则说明没人
	{
		flag_infrared=0;
	}
}





.h文件

#ifndef   __INFRARED_H
#define  __INFRARED_H

void infrared_Init(void);
void infrared_read(void);



#endif

** 需要源码可以私信我!!!**

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/217419.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Http和WebSocket

客户端发送一次http请求&#xff0c;服务器返回一次http响应。 问题&#xff1a;如何在客户端没有发送请求的情况下&#xff0c;返回服务端的响应&#xff0c;网页可以得服务器数据&#xff1f; 1&#xff1a;http定时轮询 客户端定时发送http请求&#xff0c;eg&#…

layui+ssm实现数据批量删除

layuissm实现数据的批量删除 //数据表格table.render({id: adminList,elem: #adminList,url: ctx "/admin/getAdminList", //数据接口cellMinWidth: 80,even: true,toolbar: #toolbarDemo,//头部工具栏limit: 10,//每页条数limits: [10, 20, 30, 40],defaultToolba…

Facebook推广工具功能科普!

随着社交媒体的普及&#xff0c;Facebook已经成为全球使用最广泛的社交平台之一&#xff0c;对于广大营销人员来说&#xff0c;利用Facebook推广工具进行营销已经成为不可或缺的一部分。 那么&#xff0c;这些推广工具到底有哪些功能呢?本文将为您揭秘Facebook推广工具的强大…

安全测试之推荐工具(一)

文章目录 一、前言二、Web安全&#xff08;一&#xff09;AppScan&#xff08;推荐&#xff09;&#xff08;二&#xff09;AWVS&#xff08;推荐&#xff09;&#xff08;三&#xff09;Burp Suite&#xff08;推荐&#xff09;&#xff08;四&#xff09;OWASP ZAP 三、主机安…

写 SVG 动画必看!SVG系列文章3-动画标签

1、SMIL animation概览 SMIL不是指「水蜜梨」&#xff0c;而是Synchronized Multimedia Integration Language&#xff08;同步多媒体集成语言&#xff09;的首字母缩写简称&#xff0c;是有标准的。本文所要介绍的SVG动画就是基于这种语言。 SMIL允许你做下面这些事情&#…

Harmony Ble蓝牙App(三)特性和属性

Ble蓝牙App&#xff08;三&#xff09;特性使用 前言正文一、获取属性列表二、属性提供者三、获取特性名称四、特性提供者五、加载特性六、源码 前言 在上一篇中我们完成了连接和发现服务两个动作&#xff0c;那么再发现服务之后要做什么呢&#xff1f;发现服务只是让你知道设备…

zxjy001-项目整体介绍

1、项目类型 全栈项目 前端&#xff1a;系统后台&#xff0c;系统前台后端&#xff1a;提供API接口 2、项目技术栈 前端 Vue,Element,Axios,NodeJs后端 Spring Boot,Spring Cloud,MybatisPlus,Spring Security,Redis,Maven,JWT,OAuth2其他技术 阿里云oss服务阿里云视频点播…

SL4010森利威尔DC3.7V升压5V、12V、24V/5A升压恒压电源芯片

SL4010是一款专用的DC-DC升压芯片&#xff0c;可以将3.7V的输入电压升压为5V、12V、24V的输出电压&#xff0c;并能够提供5A的输出电流。该芯片具有恒压输出、高效率、低发热等优点&#xff0c;广泛应用于各种需要高电压、大电流电源的应用中&#xff0c;如LED照明、电动汽车、…

GPIO的使用--点亮外接小灯泡--开关控制

目录 一、确定引脚接线模式 接线时注意以下几点&#xff1a; 二、外接小灯泡引脚连接(以F12引脚为例) 1.正极接GPIOF3.3v电压引脚、负极接F12 2.正极接GPIOF3.3v电压引脚、负极接F12 三、问题检查 一、确定引脚接线模式 小灯泡有两级&#xff1a;正极、负极&#xff0c;…

“影响力”经济:抖音为什么更值得商家、达人长期深耕?

文&#xff5c;新熔财经 作者&#xff5c;叶一城 数亿的活跃用户&#xff0c;简单而自然的切入方式&#xff0c;快速、高频的执行效率&#xff0c;让抖音对电商界的冲击无可阻挡。 这背后&#xff0c;流量玩法登峰造极&#xff0c;是很多人的直接观感。 但实际上&#xff0…

快手直播间自动发言评论软件:开发技术分析与核心代码分享

先来看实操成果&#xff0c;↑↑需要的同学可看我名字↖↖↖↖↖&#xff0c;或评论888无偿分享 **一、引言** 随着互联网的飞速发展&#xff0c;网络直播已经成为了人们日常生活的一部分。作为中国最大的短视频平台之一&#xff0c;快手也成为了许多主播和观众的首选。然而&am…

【数值计算方法(黄明游)】函数插值与曲线拟合(二):Newton插值【理论到程序】

​ 文章目录 一、近似表达方式1. 插值&#xff08;Interpolation&#xff09;2. 拟合&#xff08;Fitting&#xff09;3. 投影&#xff08;Projection&#xff09; 二、Lagrange插值1. 拉格朗日插值方法2. Lagrange插值公式a. 线性插值&#xff08;n1&#xff09;b. 抛物插值&…

Android Framework 电池提醒相关Dialog熄屏消失的问题

记录一下花了三四天干一天就能完成的需求的傻事。 说在前头&#xff0c;这篇文章记录了电池提醒dialog相关&#xff0c;弹出dialog且熄屏再亮屏dialog不会消失的代码&#xff0c;这篇废话比较多&#xff0c;看正常代码直接跳到代码3。 故事背景 需求要求添加非法电池的弹窗&a…

最强AI之风袭来,你爱了吗?

2017年&#xff0c;柯洁同阿尔法狗人机大战&#xff0c;AlphaGo以3比0大获全胜&#xff0c;一代英才泪洒当场...... 2019年&#xff0c;换脸哥视频“杨幂换朱茵”轰动全网&#xff0c;时至今日AI换脸仍热度只增不减&#xff1b; 2022年&#xff0c;ChatGPT一经发布便轰动全球&a…

【自然语言处理】利用sklearn库函数绘制三维瑞士卷

一&#xff0c;原理介绍 sklearn.datasets.make_swiss_roll&#xff08;&#xff09;函数提供了三维瑞士卷的数据集&#xff0c;我们可以利用他来生成瑞士卷&#xff0c;该函数的用法见sklearn官方文档&#xff1a;官网文档&#xff1a;sklearn.datasets.make_swiss_roll&…

【VTKWidgetRepresentation】第二期 vtkHandleRepresentation

很高兴在雪易的CSDN遇见你 &#xff0c;给你糖糖 欢迎大家加入雪易社区-CSDN社区云 前言 本文系统分享vtkHandleRepresentation及其子类&#xff0c;希望对各位小伙伴有所帮助&#xff01; 感谢各位小伙伴的点赞关注&#xff0c;小易会继续努力分享&#xff0c;一起进步&am…

SpringBoot——嵌入式 Servlet容器

一、如何定制和修改Servlet容器的相关配置 前言&#xff1a; SpringBoot在Web环境下&#xff0c;默认使用的是Tomact作为嵌入式的Servlet容器&#xff1b; 【1】修改和server相关的配置&#xff08;ServerProperties实现了EmbeddedServletContainerCustomizer&#xff09;例如…

Unity Meta Quest 一体机开发(九):【手势追踪】通过录制抓取手势实现自定义抓取姿势

文章目录 &#x1f4d5;教程说明&#x1f4d5;录制前的准备&#x1f4d5;第一种录制方法&#xff08;Hand Grab Pose Tool 场景&#xff09;⭐在运行模式中确认录制⭐保存录制的手势&#xff0c;将物体做成 Prefab⭐在编辑阶段调整抓取手势&#x1f50d;Fingers Freedom&#x…

Redis部署-哨兵模式

目录 redis sentinel相关名词 redis sentinel架构 故障转移流程 基于docker搭建redis哨兵 准备工作 搭建过程 模拟主节点宕机,观察哨兵节点的工作流程 哨兵重新选取主节点的流程 1.主观下线 2.客观下线 3.哨兵节点推举出一个leader节点 4.leader选举完毕,leader挑选…

10、pytest通过assert进行断言

官方实例 # content of test_assert1.pydef f():return 3def test_function():assert f() 4def test_assert_desc():a f()# assert a % 2 0assert a % 2 0, "value was odd, should be even"解读与实操 pytest允许你使用标准python断言来验证测试中的期望和值&…