在接着(上)写之前,首先来看一下效果:
链接: link
这个模块主要就是用来监测环境温度的,没有什么其他的作用
.c文件
#include "stm32f10x.h"
#include "dht11.h"
#include "delay.h"
uint8_t dat[5]={0x00,0x00,0x00,0x00,0x00}; //存储读取的温湿度信息
uint32_t sum=0; //存放校验时的求和
/****************************************
设置PB12端口为输出
*****************************************/
void DHT11_PortOutput(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出50MHz
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
/****************************************
设置PB12端口为输入
*****************************************/
void DHT11_PortInput(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//推挽输出
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出50MHz
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
/****************************************
函数名称:void DHT_Read_Byte()
函数功能:对DHT的数据中的一个字节读取函数
输入参数:无
返回值:无
*****************************************/
uint8_t DHT_Read_Byte(void)
{
uint8_t temp; //存放读取到的位数据
uint8_t ReadDat = 0;
uint8_t i;
uint8_t retry = 0;
for(i=0;i<8;i++)
{
while(DHT11 ==0 && retry < 100) //等待DHT11输出高电平
{
delay_us(1);
retry++;
}
retry = 0;
delay_us(30); //延时30us,由于‘0’代码高电平时间26~28us,'1'代码高电平时间70us,延时30us可判断出是1,还是0
temp = 0; //先将寄存器清零
if(DHT11 ==1) //延时30us之后如果还是高电平,证明为1代码
temp = 1; //将1存储
while(DHT11 ==1 && retry < 100) //等待信号被拉低,跳出
{
delay_us(1);
retry++;
}
retry = 0;
ReadDat<<=1; //数据左移1位,存放新得到的数据
ReadDat|= temp; //新得到的数据放到最后1位
}
return ReadDat;
}
/*********************************************
函数名称:unsigned char DHT_Read()
函数功能:读取DHT11的温湿度
输入参数:无
返回值: flag--数据读取、校验成功标志
*********************************************/
uint8_t DHT_Read(void)
{
uint8_t i;
uint8_t retry = 0;
DHT11_PortOutput(); //端口方向设置为输出
DHT11_LOW; //端口数据拉低
delay_ms(18); //延时18ms,时序要求
DHT11_HIGH; //端口数据拉高
delay_us(40); //延时40us,
DHT11_PortInput(); //方向设置为输入
delay_us(20); //延时20us
if(DHT11 ==0) //如果读取到低电平,证明DHT11响应
{
while(DHT11 ==0 && retry < 100) //等待变高电平
{
delay_us(1);
retry++;
}
retry = 0;
while(DHT11 ==1 && retry < 100) //等待变低电平
{
delay_us(1);
retry++;
}
retry = 0;
for(i=0;i<5;i++) //循环5次将40位读出
{
dat[i] = DHT_Read_Byte(); //读出1个字节
}
delay_us(50); //最后延时等待50us
}
sum=dat[0]+dat[1]+dat[2]+dat[3]; //前4个字节数据的和
if(dat[4]==(u8)(sum)) //前4个数据和的末8位要和第5个数据相等,才算读取正确
{
return 1; //校验正确,返回1
}
else
return 0; //校验错误,返回0
}
.h文件
#ifndef __DHT11_H__
#define __DHT11_H__
#define DHT11 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)
#define DHT11_HIGH GPIO_SetBits(GPIOB,GPIO_Pin_12)
#define DHT11_LOW GPIO_ResetBits(GPIOB,GPIO_Pin_12)
void DHT11_PortOutput(void);
void DHT11_PortInput(void);
uint8_t DHT_Read_Byte(void);
uint8_t DHT_Read(void);
#endif
这里要注意,语音识别模块本身也是一个51单片机,所以说这个语音识别模块的代码仅仅是语音模块的51单片机与stm32进行交互的代码,并不是51单片机的代码,但是一般来讲,商家给你发货以后,都会在里面提前烧录上代码,所以我们不用去管51里面的代码。
.c文件
#include "stm32f10x.h" // Device header
#include "voice.h"
/*
PB10 :TXD
PB11 : RXD
*/
extern uint32_t flag_led_on_off;
//变量引用
//变量声明
u8 RXBUF[20]; //串口存储数组
u8 RXOVER=0; //串口接收标志位
u8 RXCOUNT=0; //串口计数变量
u8 i; //清空数组变量
//串口IO初始化函数
void USART3_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure; //IO端口的初始化
USART_InitTypeDef USART_InitStructure; //串口的初始化
NVIC_InitTypeDef NVIC_InitStructure; //使能中断接收
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能IO端口的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //使能串口的时钟
//发送
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //发送引脚
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//接收
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //接收引脚
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = bound; //设置传输的波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置传输一帧数据的数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一位停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //能使接收的发送
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控制
USART_Init(USART3,&USART_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //优先级分组
NVIC_InitStructure.NVIC_IRQChannel= USART3_IRQn; //开启串口2的中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART3,USART_IT_RXNE,ENABLE); //使能串口接收
USART_Cmd(USART3,ENABLE); //使能串口2
}
/*
函数名:USART3中断服务函数
功能: 接收数据
注意:接收数据长度可调:RXCOUNT
*/
void USART3_IRQHandler(void)
{
u8 temp;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(USART3,USART_IT_RXNE);
temp = USART_ReceiveData(USART3);
if(temp == '\n' || RXCOUNT == 20) //判断是否接收到一个完整字符
{
RXCOUNT = 0;
RXOVER =1; //接收数据完成标志位置1
USART_ITConfig(USART3,USART_IT_RXNE,DISABLE);//失能串口接收中断标志
}
else
{
RXBUF[RXCOUNT] = temp; //依次存放到数组中
RXCOUNT++; //字符长度变化
}
}
}
//串口处理函数
void USART_Deal(void)
{
if(RXOVER)
{
RXOVER = 0; //清除接收标志位
switch(RXBUF[0]-48)
{
case 1:
flag_led_on_off=1; //点亮小灯
break;
case 2:
flag_led_on_off=0; //熄灭小灯
break;
default:
break;
}
for(i=0;i<20;i++) //将已接收数据的数组清空:共20个字符长度
{
RXBUF[i] = 0; //重置数据缓存区
}
USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//始能串口接收
}
}
.h文件
#ifndef __VOICE_H
#define __VOICE_H
void USART3_Init(u32 bound);
void USART_Deal(void);
#endif
如果51单片机里面没有代码,就要使用串口工具进行烧录,需要问商家要源码,然后将hex文件烧录到里面。
OLED显示模块主要是用来显示和调试用的
OLED显示程序包括字库程序太大,放到以下链接中去下载,下载设置了0积分。
https://download.csdn.net/download/nbbskk/87238933
以上第四章节完成了各个模块的驱动程序,那么接下来就要实现手动模式、自动模式和模式切换的代码了。
模式切换:
.c文件
#include "stm32f10x.h" // Device header
#include "oled.h"
#include "mode_option.h"
#include "auto_mode.h"
#include "manu_mode.h"
#include "remote.h"
extern uint32_t flag_auto_manu;
extern uint32_t flag_in_system;
uint32_t flag_break_mode=0;
void mode_option_show(void)//文字显示
{
OLED_ShowChinese(35,0,0,16,1);//选
OLED_ShowChinese(51,0,1,16,1);//择
OLED_ShowChinese(67,0,2,16,1);//模
OLED_ShowChinese(83,0,3,16,1);//式
OLED_Refresh();
}
void auto_manu_option(void)//选择自动模式或者手动模式的函数
{
if(flag_auto_manu==0)
{
OLED_ShowChinese(50,30,4,16,1);//手
OLED_ShowChinese(66,30,5,16,1);//动
OLED_Refresh();
}
else
{
OLED_ShowChinese(50,30,6,16,1);//自
OLED_ShowChinese(66,30,5,16,1);//动
OLED_Refresh();
}
}
void system_in_auto_manu(void)//选择是否进入系统的函数
{
if(flag_auto_manu==0&&flag_in_system==1)//如果手自动标志位为0并且进入系统标志位为1,则进入手动模式
{
flag_break_mode=0;//退出标志位置为0
flag_in_system=0;//进入系统标志位置为0
manu_mode_init();//手动模式初始化
manu_go();//手动模式运行
}
else if(flag_auto_manu==1&&flag_in_system==1)//如果手自动标志位为1并且进入系统标志位为1,则进入自动模式
{
flag_break_mode=0;
flag_in_system=0;
auto_mode_init();
auto_go();
}
}
.h文件
#ifndef __MODE_OPTION_H
#define __MODE_OPTION_H
void mode_option_show(void);
void auto_manu_option(void);
void system_in_auto_manu(void);
#endif
手动模式:
#include "stm32f10x.h" // Device header
#include "manu_mode.h"
#include "LED.h"
#include "remote.h"
#include "dht11.h"
#include "oled.h"
#include "voice.h"
extern uint32_t flag_break_mode;
extern uint32_t flag_led_on_off;
extern uint8_t dat[5];//存放温度数值
void manu_mode_init(void)
{
LED_Init();//led灯初始化
USART3_Init(9600);//语音模块初始化
OLED_Clear();
OLED_ShowChinese(0,0,7,16,1);//灯
OLED_ShowChinese(16,0,8,16,1);//光
OLED_ShowChar(32,0,':',16,1);
OLED_ShowChinese(0,16,9,16,1);//温
OLED_ShowChinese(16,16,10,16,1);//度
OLED_ShowChar(32,16,':',16,1);
}
void manu_go(void)
{
flag_led_on_off=0;//进入模式后,默认关灯
while(1)
{
remote_receive();//循环检测红外遥控
USART_Deal();//循环检测语音模块
if(flag_break_mode==1)//判断是否需要退出
{
flag_break_mode=0;
OLED_Clear();
break;
}
if(flag_led_on_off==0)//如果LED灯标志位为0,LED灯关闭
{
LED1_off();
OLED_ShowChinese(40,0,12,16,1);//关
}
else//如果LED灯标志位为1,LED灯打开
{
LED1_on();
OLED_ShowChinese(40,0,13,16,1);//开
}
if(DHT_Read())//读到DHT11温湿度传感器的数值并显示
{
OLED_ShowNum(40,16,dat[2],2,16,1);
OLED_ShowChar(56,16,'.',16,1);
OLED_ShowNum(60,16,dat[3],1,16,1);
OLED_ShowChinese(70,16,10,16,1);//度
OLED_Refresh();
}
}
}
.h文件
#ifndef __MANU_MODE_H
#define __MANU_MODE_H
void manu_mode_init(void);
void manu_go(void);
#endif
自动模式:
#include "stm32f10x.h" // Device header
#include "auto_mode.h"
#include "LED.h"
#include "remote.h"
#include "delay.h"
#include "bh1750.h"
#include "oled.h"
#include "infrared.h"
#include "dht11.h"
extern uint32_t flag_break_mode;
extern uint32_t flag_infrared;
uint16_t light_value=0;
extern uint8_t dat[5];
void auto_mode_init(void)
{
LED_Init();
infrared_Init();
BH1750_Config_Init(); //BH1750的初始化
bh_data_send(BHPowOn); //打开模块等待测量命令
bh_data_send(BHReset); //重置数据寄存器值在PowerOn模式下有效
bh_data_send(BHModeL); //低分辨率 单位4lx 测量时间16ms
delay_ms(180); //等待测量结束延时180ms,保证通讯
OLED_Clear();
OLED_ShowChinese(0,0,7,16,1);//灯
OLED_ShowChinese(16,0,8,16,1);//光
OLED_ShowChar(32,0,':',16,1);
OLED_ShowChinese(0,16,9,16,1);//温
OLED_ShowChinese(16,16,10,16,1);//度
OLED_ShowChar(32,16,':',16,1);
OLED_ShowChinese(0,32,11,16,1);//亮
OLED_ShowChinese(16,32,10,16,1);//度
OLED_ShowChar(32,32,':',16,1);
}
void auto_go(void)
{
while(1)
{
remote_receive();
if(flag_break_mode==1)
{
flag_break_mode=0;
OLED_Clear();
break;
}
light_value = bh_data_read()*4/1.2; //光照强度的计算公式 = 寄存器值*分辨率/1.2
infrared_read();//检测红外检测模块的值
OLED_ShowNum(40,32,light_value,4,16,1);//显示亮度值
OLED_ShowString(72,32,(uint8_t*)"lx",16,1);//显示单位
if(light_value<1000&&flag_infrared==1)//当亮度小于1000lx,并且红外检测模块标志位置为1,则开灯
{
LED1_on();
OLED_ShowChinese(40,0,13,16,1);//开
}
else//否则关灯
{
LED1_off();
OLED_ShowChinese(40,0,12,16,1);//关
}
if(DHT_Read())
{
OLED_ShowNum(40,16,dat[2],2,16,1);
OLED_ShowChar(56,16,'.',16,1);
OLED_ShowNum(60,16,dat[3],1,16,1);
OLED_ShowChinese(70,16,10,16,1);//度
OLED_Refresh();
}
}
}
.h文件
#ifndef __AUTO_MODE_H
#define __AUTO_MODE_H
void auto_mode_init(void);
void auto_go(void);
#endif
基于stm32的红外、语音、按键智能灯光控制基本就可以实现了,如果想要完整的源码、原理图、PCB、流程图可以私信我,谢谢大家!!!