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

news2024/12/23 22:55:16

目录:

  • 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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】树型结构关联式容器:map/multimap/set/multisetの使用指南(27)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.键值对二.关联式容器&#xff06;序列…

国产API调试插件:Apipost-Helper

前言 Idea 是一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它可以帮助开发人员更加高效地编写、调试和部署软件应用程序,Idea 还具有许多插件和扩展&#xff0c;可以根据开发人员的需要进行定制和扩展&#xff0c;从而提高开发效率,今天我们就来介绍一款…

uniapp-距离distance数字太长,截取保留前3为数字

1.需求 将接口返回的距离的字段&#xff0c;保留三位数显示。 2.实现效果 3.代码&#xff1a; 1.这是接口返回的数据&#xff1a; 2.调取接口&#xff0c;赋值前先处理每条数据的distance <view class"left">距你{{item.distance}}km</view>listFun() …

C语言面试之旅:掌握基础,探索深度(面试实战之ARM架构一)

从前不会回头&#xff0c;往后不会将就。 ----小新 一.ARM采用32位架构 ARM约定一个Byte是8 bits&#xff0c;一个Halfword是16 bits (2 byte)&#xff0c;一个Word是32 bits (4 byte)。大部分ARM core …

回溯法及例题(C++实现)

回溯法概念 概念&#xff1a;在包含问题所有解的解空间树中&#xff0c;按照深度优先搜索的策略&#xff0c;根据根结点&#xff08;开始节点&#xff09;出发搜索解空间树。 流程&#xff1a;首先根结点成为活节点&#xff0c;同时也成为当前的扩展结点。在当前的扩展结点处…

蓝桥杯真题:四平方和-Java版

import java.io.*;/*先找后两个数for(int i 0; 2 * i * i < n;i)for(int j i; i * i j * j < n;j ) 再找前两个数 for(int i 0;4 * i * i < n; i )for(int j i;2 * (j * j i * i) < n;j )//这样就可以让后两个数尽量大,前两个数尽量小 这样就可以确定后…

Kubernetes学习笔记-Part.07 Harbor搭建

目录 Part.01 Kubernets与docker Part.02 Docker版本 Part.03 Kubernetes原理 Part.04 资源规划 Part.05 基础环境准备 Part.06 Docker安装 Part.07 Harbor搭建 Part.08 K8s环境安装 Part.09 K8s集群构建 Part.10 容器回退 第七章 Harbor搭建 Docker-Compose是用来管理容器的…

【React 开发】增强你的React 技能:2024年要掌握的4种高级模式

React由于其简单和灵活&#xff0c;近年来已成为最受欢迎的前端库之一。然而&#xff0c;当应用程序的复杂性扩展时&#xff0c;管理状态、处理异步输入和维护可扩展的体系结构可能会变得困难。我们将在本文中介绍四种高级React模式&#xff0c;它们将帮助您克服这些困难以及如…

Unity中C#使用协程控制Shader材质变化

文章目录 前言一、协程是什么二、在Unity中使用协程1、我们在 Start 中测试一下协程的执行顺序2、我们实现一个点击按钮实现角色受击效果 三、协程中的动画过渡1、首先&#xff0c;在协程内实现中毒并且消散的效果2、在 OnGUI 内&#xff0c;给一个新按钮使用刚刚定义的协程 四…

算法基础六

搜索插入位置 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2 示例 2: 输入: nums [1,3,5,6], target 2 输…

TCP 半连接队列和全连接队列

在 TCP 三次握手的时候&#xff0c;Linux 内核会维护两个队列&#xff0c;分别是&#xff1a; 半连接队列&#xff0c;也称 SYN 队列&#xff1b; 全连接队列&#xff0c;也称 accept 队列&#xff1b; 服务端收到客户端发起的 SYN 请求后&#xff0c;内核会把该连接存储到半连…

创新零售巨头:揭开山姆与Costco蓬勃发展背后的秘密

会员制商店这个冷门的业态突然之间硝烟弥漫&#xff0c;更多的资本开始涌向付费会员商店这一业态&#xff0c;本文即将探讨的是付费会员制的成功秘诀和零售企业可行的发展路径。Costco的发展经验对国内超市巨头的崛起具有显著的借鉴意义&#xff0c;以优质低价商品服务为中心&a…

辛普森距离(SD,Sampson Distance)

定义 Sampson误差是复杂性介于代数误差和几何误差之间&#xff0c;但非常近似于几何误差的一种误差。 应用 SLAM对极几何中使用到SD来筛选内点&#xff1a; 1.随机采样8对匹配点 2.8点法求解基础矩阵 ​&#xff1b; 3.奇异值约束获取基础矩阵F&#xff1b; 4.计算误差&…

前端开发_CSS

CSS定义 层叠样式表 (Cascading Style Sheets&#xff0c;缩写为 CSS&#xff09;&#xff0c;是一种 样式表 语言&#xff0c;用来描述 HTML 文档的呈现&#xff08;美化内容&#xff09; 书写位置&#xff1a;title 标签下方添加 style 双标签&#xff0c;style 标签里面书…

【华为OD题库-064】最小传输时延I-java

题目 某通信网络中有N个网络结点&#xff0c;用1到N进行标识。网络通过一个有向无环图.表示,其中图的边的值表示结点之间的消息传递时延。 现给定相连节点之间的时延列表times[]{u&#xff0c;v&#xff0c; w)&#xff0c;其中u表示源结点&#xff0c;v表示目的结点&#xff0…

Windows本地搭建WebDAV服务并使用内网穿透远程访问【无公网IP】

windows搭建WebDAV服务&#xff0c;并内网穿透公网访问【无公网IP】 文章目录 windows搭建WebDAV服务&#xff0c;并内网穿透公网访问【无公网IP】1. 安装IIS必要WebDav组件2. 客户端测试3. cpolar内网穿透3.1 打开Web-UI管理界面3.2 创建隧道3.3 查看在线隧道列表3.4 浏览器访…

Unity Meta Quest 一体机开发(八):【手势追踪】实现 Hand Grab 扔物体功能

文章目录 &#x1f4d5;教程说明&#x1f4d5;设置刚体和碰撞体&#x1f4d5;给物体添加 Physics Grabbable 脚本&#x1f4d5;给手部添加 Hand Velocity Calculator 物体 此教程相关的详细教案&#xff0c;文档&#xff0c;思维导图和工程文件会放入 Spatial XR 社区。这是一…

SAS聚类分析介绍

1 聚类分析介绍 1.1基本概念 聚类就是一种寻找数据之间一种内在结构的技术。聚类把全体数据实例组织成一些相似组&#xff0c;而这些相似组被称作聚类。处于相同聚类中的数据实例彼此相同&#xff0c;处于不同聚类中的实例彼此不同。聚类技术通常又被称为无监督学习&#xff0…

2023博思高科技智慧车行、人行专项研讨会成功召开

来源&#xff1a;智安物联网 11月30日&#xff0c;深圳市博思高科技有限公司&#xff08;以下简称“博思高科技”&#xff09;在其总部成功举办了智慧车行、人行专项研讨会议。本次会议邀请了来自国家发改委综合运输研究所的程世东主任&#xff0c;中国安全防范产品行业协会原…

13、pytest为失败的断言定义自己的解释

官方实例 # content of ocnftest.py from test_foocompare import Foodef pytest_assertrepr_compare(op, left, right):if isinstance(left, Foo) and isinstance(right, Foo) and op "":return["Comparing Foo instances:",f" vals:{left.val} !…