目录:
- 1.系统实现目标
- 2.硬件选型和软件准备
- 2.1. 硬件选型
- 2.2 软件准备
- 3. 硬件IO表
- 4.各个模块的驱动函数
- 4.1. 红外遥控模块
- 4.2. 按键模块
- 4.3. LED灯
- 4.4. BH1750光照度传感器
- 4.5. 红外检测模块
1.系统实现目标
本文所设计的基于单片机的灯光控制系统主要由模式选择功能、手动模式和自动模式组成。
其中模式选择功能由以下功能组成:
- 当最小系统板上电以后,自动进入模式选择界面,通过按键和红外遥控器可以进行模式的选择,选择的模式可用通过显示屏显示。
- 在选择完成相应的模式以后,也可以通过按键或者红外遥控器选择进入手动模式或者自动模式,进入相应模式以后,就可以执行相应的功能。
- 当进入相应模式以后,如果想要切换模式,可以通过手动退出,然后继续在模式选择界面进行选择。
手动模式主要包括红外控制模式,语音控制模式和按键控制模式,下面来详细介绍一下这几种模式:
- 红外控制模式:使用带有红外遥控器的用户可以通过遥控器来对智能灯光进行控制。用户可以通过遥控器上的按键来打开/关闭灯光。
- 语音控制模式:使用语音助手的用户可以通过语音命令来控制智能灯光。用户可以说出指令,如“开灯”、“关灯”等,系统会解析语音指令并执行相应的操作。
- 按键控制模式:用户可以通过智能灯光控制系统配备的按键来控制灯光。
以上三种模式都可供用户选择,并且用户可以在不同场景下,选择最为方便、快捷的方式来对智能灯光进行控制。
自动模式主要是通过红外检测传感器和光照度传感器来实现的,主要由以下功能:
- 当外部环境变暗的时候,光照度传感器会检测到光照度的降低并输出相应的信号,这些信号会传递给单片机进行处理,并且会使得相应的标志位进行标记。
- 当红外检测传感器检测到周围有人的时候,也会产生相应的信号传递给单片机,将相应的标志位进行置位。
- 当同时满足外部环境变暗并且周围有人的时候,这是系统会自动处理事件,将灯打开;当其中一个事件不满足的时候,灯光就会关闭。
视频演示链接:
链接: 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
** 需要源码可以私信我!!!**