基于 CC2530 设计的智能风扇
- 2022 年 2 月 23 日
本文字数:8351 字
阅读完需:约 27 分钟
1. 项目介绍
随着空调降温设备的频繁使用,全球气候不断变暖空调降温设备排放出的物质对环境的影响越来越大。二是人们在熟睡之后经常因为温度太低而感冒或者温度升高而不适,风扇相比空调更加适用于老人儿童和体质较弱的人使用。通过物联网技术的智能风扇设计可以解决因为睡熟导致降温设备依旧运行工作,实现更加节能更加智能的控制。
通过温度传感器对环境温度进行数据采集和语音控制模块来调节风速实现对风扇的智能控制,使风扇随温度变化来自动调节风力大小。
具体实现功能如下:
实现语音控制,可以识别到语音命令并自动做出相应的工作;
实现温度实时监测;
实现通过实时温度来自动控制风扇转速;
实现通电后可以进行风扇的开光和转数调节:
实现思路:
采用 DHT11 温湿度传感器,采集环境温度,根据设置的温度阀值与采集的环境温度做对比,控制风扇的开关,也可以通过语音识别模块检测语音完成语音控制。
实现的功能总结如下:
按下开发板上的按键控制风扇的开关(LED 灯的开关)
通过语音控制风扇的开关(LED 灯的开关)语音由语音模块进行识别。
在主函数里每 500ms 采集一次 DHT11 温度,然后通过 OLED 显示屏显示。
完整项目源码下载地址: https://download.csdn.net/download/xiaolong1126626497/75318366
2. 硬件介绍
2.1 OLED 显示屏
OLED 采用 0.96 寸的 SPI 接口显示屏。分辨率是 128x64.
具体接线如下:
#define LCD_SCL P1_2 //SCLK 时钟 D0(SCLK)
#define LCD_SDA P1_3 //SDA D1(MOSI) 数据
#define LCD_RST P1_7 //_RES hardware reset 复位
#define LCD_DC P0_0 //A0 H/L 命令数据选通端,H:数据,L:命令
VCC接3.3V
GND接GND
2.2 DHT11 温湿度传感器
DHT11 是单总线数字温湿度传感器,电压兼容 3v~5v,单片机可以直接 IO 口连接模拟时序完成数据读取。
具体接线如下:
DATA接在P0_7口
VCC接3.3V
GND接GND
2.3 语音模块
语音模块采用 LD3320 芯片进行识别,可以设置内部识别的词条,支持二次开发编程。
默认内置的识别词条如下:
下面是语音识别模块的实物图:
3. 案例代码
3.1 语音识别模块代码
/*******************************************************
** CPU: STC11L08XE
** 晶振:22.1184MHZ
** 波特率:9600 bit/S
** 口令模式: 即每次识别时都需要说“小杰”这个口令 ,才能够进行下一级的识别
/*********************************************************/
#include "config.h"
/************************************************************************************/
// nAsrStatus 用来在main主程序中表示程序运行的状态,不是LD3320芯片内部的状态寄存器
// LD_ASR_NONE: 表示没有在作ASR识别
// LD_ASR_RUNING: 表示LD3320正在作ASR识别中
// LD_ASR_FOUNDOK: 表示一次识别流程结束后,有一个识别结果
// LD_ASR_FOUNDZERO: 表示一次识别流程结束后,没有识别结果
// LD_ASR_ERROR: 表示一次识别流程中LD3320芯片内部出现不正确的状态
/***********************************************************************************/
uint8 idata nAsrStatus = 0;
void MCU_init();
void ProcessInt0(); //识别处理函数
void delay(unsigned long uldata);
void User_handle(uint8 dat);//用户执行操作函数
void Delay200ms();
void Led_test(void);//单片机工作指示
uint8_t G0_flag = DISABLE; //运行标志,ENABLE:运行。DISABLE:禁止运行
sbit LED = P4 ^ 2; //信号指示灯
sbit SRD1 = P1 ^ 7;
sbit SRD2 = P1 ^ 6;
sbit SRD3 = P1 ^ 5;
sbit SRD4 = P1 ^ 4;
/***********************************************************
* 名 称: void main(void)
* 功 能: 主函数 程序入口
* 入口参数:
* 出口参数:
* 说 明:
* 调用方法:
**********************************************************/
void main(void)
{
uint8 idata nAsrRes;
uint8 i = 0;
P1M0 = 0xFF;
P1M1 = 0x00;
SRD1 = SRD2 = SRD3 = SRD4 = 0;
Led_test();
MCU_init();
LD_Reset();
UartIni(); /*串口初始化*/
nAsrStatus = LD_ASR_NONE; // 初始状态:没有在作ASR
PrintCom("<G>欢迎使用");
while(1)
{
switch(nAsrStatus)
{
case LD_ASR_RUNING:
case LD_ASR_ERROR:
break;
case LD_ASR_NONE:
{
nAsrStatus = LD_ASR_RUNING;
if (RunASR() == 0) /* 启动一次ASR识别流程:ASR初始化,ASR添加关键词语,启动ASR运算*/
{
nAsrStatus = LD_ASR_ERROR;
}
break;
}
case LD_ASR_FOUNDOK: /* 一次ASR识别流程结束,去取ASR识别结果*/
{
nAsrRes = LD_GetResult(); /*获取结果*/
User_handle(nAsrRes);//用户执行函数
nAsrStatus = LD_ASR_NONE;
break;
}
case LD_ASR_FOUNDZERO:
default:
{
nAsrStatus = LD_ASR_NONE;
break;
}
}// switch
}// while
}
/***********************************************************
* 名 称: LED灯测试
* 功 能: 单片机是否工作指示
* 入口参数: 无
* 出口参数:无
* 说 明:
**********************************************************/
void Led_test(void)
{
LED = ~ LED;
Delay200ms();
LED = ~ LED;
Delay200ms();
LED = ~ LED;
Delay200ms();
LED = ~ LED;
Delay200ms();
LED = ~ LED;
Delay200ms();
LED = ~ LED;
}
/***********************************************************
* 名 称: void MCU_init()
* 功 能: 单片机初始化
* 入口参数:
* 出口参数:
* 说 明:
* 调用方法:
**********************************************************/
void MCU_init()
{
P0 = 0xff;
P1 = 0x00;
P2 = 0xff;
P3 = 0xff;
P4 = 0xff;
AUXR &= 0x7F; //定时器时钟12T模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x28; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
LD_MODE = 0; // 设置MD管脚为低,并行模式读写
IE0 = 1;
EX0 = 1;
EA = 1;
WDT_CONTR = 0x3D;
}
/***********************************************************
* 名 称: 延时函数
* 功 能:
* 入口参数:
* 出口参数:
* 说 明:
* 调用方法:
**********************************************************/
void Delay200us() //@22.1184MHz
{
unsigned char i, j;
_nop_();
_nop_();
i = 5;
j = 73;
do
{
while (--j);
}
while (--i);
}
void delay(unsigned long uldata)
{
unsigned int j = 0;
unsigned int g = 0;
while(uldata--)
Delay200us();
}
void Delay200ms() //@22.1184MHz
{
unsigned char i, j, k;
i = 17;
j = 208;
k = 27;
do
{
do
{
while (--k);
}
while (--j);
}
while (--i);
}
/***********************************************************
* 名 称: 中断处理函数
* 功 能:
* 入口参数:
* 出口参数:
* 说 明:
* 调用方法:
**********************************************************/
void ExtInt0Handler(void) interrupt 0
{
ProcessInt0();
}
/***********************************************************
* 名 称:用户执行函数
* 功 能:识别成功后,执行动作可在此进行修改
* 入口参数: 无
* 出口参数:无
* 说 明:
**********************************************************/
void User_handle(uint8 dat)
{
if(0 == dat)
{
G0_flag = ENABLE;
PrintCom("<G>你好,主人");
LED = 0;
}
else if(ENABLE == G0_flag)
{
G0_flag = DISABLE;
LED = 1;
switch(dat)
{
case CODE_1: /*命令“开灯”*/
SRD1 = 1;
PrintCom("<G>灯已打开");
break;
case CODE_2: /*命令“关灯”*/
SRD1 = 0;
PrintCom("<G>灯已关闭\r\n");
break;
case CODE_3: /*命令“打开电视”*/
SRD2 = 1;
PrintCom("<G>电视机已打开\r\n");
break;
case CODE_4: /*命令“关闭电视”*/
SRD2 = 0;
PrintCom("<G>电视机已关闭\r\n");
break;
case CODE_5: /*命令“打开冰箱”*/
SRD3 = 1;
PrintCom("<G>冰箱已打开\r\n");
break;
case CODE_6: /*命令“关闭冰箱”*/
SRD3 = 0;
PrintCom("<G>冰箱已关闭\r\n");
break;
case CODE_7: /*命令“打开空调”*/
SRD4 = 1;
PrintCom("<G>空条已打开\r\n");
break;
case CODE_8: /*命令“关闭空调”*/
SRD4 = 0;
PrintCom("<G>空条已关闭\r\n");
break;
case CODE_9: /*命令“全部打开”*/
SRD1 = 1;
SRD2 = 1;
SRD3 = 1;
SRD4 = 1;
PrintCom("<G>已全部打开\r\n");
break;
case CODE_10: /*命令“全部关闭”*/
SRD1 = 0;
SRD2 = 0;
SRD3 = 0;
SRD4 = 0;
PrintCom("<G>已全部关闭\r\n");
break;
case CODE_11: /*命令“.....”*/
PrintCom("");
break;
case CODE_12: /*命令“.....”*/
PrintCom("");
break;
case CODE_13: /*命令“.....”*/
PrintCom("");
break;
case CODE_14: /*命令“.....”*/
PrintCom("");
break;
case CODE_15: /*命令“.....”*/
PrintCom("");
break;
case CODE_16: /*命令“.....”*/
PrintCom("");
break;
case CODE_17: /*命令“.....”*/
PrintCom("");
break;
case CODE_18: /*命令“.....”*/
PrintCom("");
break;
case CODE_19: /*命令“.....”*/
PrintCom("");
break;
case CODE_20: /*命令“.....”*/
PrintCom("");
break;
case CODE_21: /*命令“.....”*/
PrintCom("");
break;
case CODE_22: /*命令“.....”*/
PrintCom("");
break;
case CODE_23: /*命令“.....”*/
PrintCom("");
break;
case CODE_24: /*命令“.....”*/
PrintCom("");
break;
case CODE_25: /*命令“.....”*/
PrintCom("");
break;
case CODE_26: /*命令“.....”*/
PrintCom("");
break;
case CODE_27: /*命令“.....”*/
PrintCom("");
break;
case CODE_28: /*命令“.....”*/
PrintCom("");
break;
case CODE_29: /*命令“.....”*/
PrintCom("");
break;
case CODE_30: /*命令“.....”*/
PrintCom("");
break;
case CODE_31: /*命令“.....”*/
PrintCom("");
break;
case CODE_32: /*命令“.....”*/
PrintCom("");
break;
case CODE_33: /*命令“.....”*/
PrintCom("");
break;
case CODE_34: /*命令“.....”*/
PrintCom("");
break;
case CODE_35: /*命令“.....”*/
PrintCom("");
break;
case CODE_36: /*命令“.....”*/
PrintCom("");
break;
case CODE_37: /*命令“.....”*/
PrintCom("");
break;
case CODE_38: /*命令“.....”*/
PrintCom("");
break;
case CODE_39: /*命令“.....”*/
PrintCom("");
break;
case CODE_40: /*命令“.....”*/
PrintCom("");
break;
case CODE_41: /*命令“.....”*/
PrintCom("");
break;
case CODE_42: /*命令“.....”*/
PrintCom("");
break;
case CODE_43: /*命令“.....”*/
PrintCom("");
break;
case CODE_44: /*命令“.....”*/
PrintCom("");
break;
case CODE_45: /*命令“.....”*/
PrintCom("");
break;
case CODE_46: /*命令“.....”*/
PrintCom("");
break;
case CODE_47: /*命令“.....”*/
PrintCom("");
break;
case CODE_48: /*命令“.....”*/
PrintCom("");
break;
case CODE_49: /*命令“.....”*/
PrintCom("");
break;
default:/*text.....*/
break;
}
}
else
{
//PrintCom("请说出一级口令\r\n"); /*text.....*/
}
}
void tm0_isr() interrupt 1
{
TL0 = 0x00; //设置定时初值
TH0 = 0x28; //设置定时初值
WDT_CONTR=0x3D;
}
3.2 cc2530 串口代码
#include "uart.h"
/*
函数功能:串口0初始化
*/
void Init_Uart0(void)
{
PERCFG&=~(1<<0); //串口0的引脚映射到位置1,即P0_2和P0_3
P0SEL|=0x3<<2; //将P0_2和P0_3端口设置成外设功能
U0BAUD = 216; //32MHz的系统时钟产生115200BPS的波特率
U0GCR&=~(0x1F<<0);//清空波特率指数
U0GCR|=11<<0; //32MHz的系统时钟产生115200BPS的波特率
U0UCR |= 0x80; //禁止流控,8位数据,清除缓冲器
U0CSR |= 0x3<<6; //选择UART模式,使能接收器
}
/*
函数功能:UART0发送字符串函数
*/
void UR0SendString(char *str)
{
while(*str!='\0')
{
U0DBUF = *str; //将要发送的1字节数据写入U0DBUF
while(UTX0IF == 0);//等待数据发送完成
UTX0IF = 0; //清除发送完成标志,准备下一次发送
str++;
}
}
/*
函数功能: 模仿printf风格的格式化打印功能
*/
char USART0_PRINT_BUFF[200]; //格式化数据缓存数据
void USART0_Printf(const char *format,...)
{
char *str=NULL;
/*1. 格式化转换*/
va_list ap; // va_list---->char *
va_start(ap,format); //初始化参数列表
vsprintf(USART0_PRINT_BUFF,
format,
ap); //格式化打印
va_end(ap); //结束参数获取
/*2. 串口打印*/
str=USART0_PRINT_BUFF;//指针赋值
while(*str!='\0')
{
U0DBUF=*str; //发送一个字节的数据
str++; //指针自增,指向下一个数据
while(UTX0IF == 0);//等待数据发送完成
UTX0IF = 0; //清除发送完成标志,准备下一次发送
}
}
3.3 OLED 显示屏代码
typedef unsigned char uchar;
typedef unsigned int uint;
#define LCD_SCL P1_2 //SCLK 时钟 D0(SCLK)
#define LCD_SDA P1_3 //SDA D1(MOSI) 数据
#define LCD_RST P1_7 //_RES hardware reset 复位
#define LCD_DC P0_0 //A0 H/L 命令数据选通端,H:数据,L:命令
#define XLevelL 0x00
#define XLevelH 0x10
#define XLevel ((XLevelH&0x0F)*16+XLevelL)
#define Max_Column 128
#define Max_Row 64
#define Brightness 0xCF
#define X_WIDTH 128
#define Y_WIDTH 64
void DelayMS(unsigned int msec)
{
unsigned int i,j;
for (i=0; i<msec; i++)
for (j=0; j<530; j++);
}
/*********************LCD 延时1ms************************************/
void LCD_DLY_ms(unsigned int ms)
{
unsigned int a;
while(ms)
{
a=1800;
while(a--);
ms--;
}
return;
}
/*********************LCD写数据************************************/
void LCD_WrDat(unsigned char dat)
{
unsigned char i=8, temp=0;
LCD_DC=1;
for(i=0;i<8;i++) //发送一个八位数据
{
LCD_SCL=0;
temp = dat&0x80;
if (temp == 0)
{
LCD_SDA = 0;
}
else
{
LCD_SDA = 1;
}
LCD_SCL=1;
dat<<=1;
}
}
/*********************LCD写命令************************************/
void LCD_WrCmd(unsigned char cmd)
{
unsigned char i=8, temp=0;
LCD_DC=0;
for(i=0;i<8;i++) //发送一个八位数据
{
LCD_SCL=0;
temp = cmd&0x80;
if (temp == 0)
{
LCD_SDA = 0;
}
else
{
LCD_SDA = 1;
}
LCD_SCL=1;
cmd<<=1;;
}
}
/*********************LCD 设置坐标************************************/
void LCD_Set_Pos(unsigned char x, unsigned char y)
{
LCD_WrCmd(0xb0+y);
LCD_WrCmd(((x&0xf0)>>4)|0x10);
LCD_WrCmd((x&0x0f)|0x01);
}
/*********************LCD全屏************************************/
void LCD_Fill(unsigned char bmp_dat)
{
unsigned char y,x;
for(y=0;y<8;y++)
{
LCD_WrCmd(0xb0+y);
LCD_WrCmd(0x01);
LCD_WrCmd(0x10);
for(x=0;x<X_WIDTH;x++)
LCD_WrDat(bmp_dat);
}
}
/*********************LCD复位************************************/
void LCD_CLS(void)
{
unsigned char y,x;
for(y=0;y<8;y++)
{
LCD_WrCmd(0xb0+y);
LCD_WrCmd(0x01);
LCD_WrCmd(0x10);
for(x=0;x<X_WIDTH;x++)
LCD_WrDat(0);
}
}
/*********************LCD初始化************************************/
void LCD_Init(void)
{
P0SEL &= 0xFE; //让P0.0为普通IO口,
P0DIR |= 0x01; //让P0.0为为输出
P1SEL &= 0x73; //让 P1.2 P1.3 P1.7为普通IO口
P1DIR |= 0x8C; //把 P1.2 P1.3 1.7设置为输出
LCD_SCL=1;
LCD_RST=0;
LCD_DLY_ms(50);
LCD_RST=1; //从上电到下面开始初始化要有足够的时间,即等待RC复位完毕
LCD_WrCmd(0xae);//--turn off oled panel
LCD_WrCmd(0x00);//---set low column address
LCD_WrCmd(0x10);//---set high column address
LCD_WrCmd(0x40);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
LCD_WrCmd(0x81);//--set contrast control register
LCD_WrCmd(0xcf); // Set SEG Output Current Brightness
LCD_WrCmd(0xa1);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
LCD_WrCmd(0xc8);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
LCD_WrCmd(0xa6);//--set normal display
LCD_WrCmd(0xa8);//--set multiplex ratio(1 to 64)
LCD_WrCmd(0x3f);//--1/64 duty
LCD_WrCmd(0xd3);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
LCD_WrCmd(0x00);//-not offset
LCD_WrCmd(0xd5);//--set display clock divide ratio/oscillator frequency
LCD_WrCmd(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
LCD_WrCmd(0xd9);//--set pre-charge period
LCD_WrCmd(0xf1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
LCD_WrCmd(0xda);//--set com pins hardware configuration
LCD_WrCmd(0x12);
LCD_WrCmd(0xdb);//--set vcomh
LCD_WrCmd(0x40);//Set VCOM Deselect Level
LCD_WrCmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)
LCD_WrCmd(0x02);//
LCD_WrCmd(0x8d);//--set Charge Pump enable/disable
LCD_WrCmd(0x14);//--set(0x10) disable
LCD_WrCmd(0xa4);// Disable Entire Display On (0xa4/0xa5)
LCD_WrCmd(0xa6);// Disable Inverse Display On (0xa6/a7)
LCD_WrCmd(0xaf);//--turn on oled panel
LCD_Fill(0xff); //初始清屏
LCD_Set_Pos(0,0);
}
版权声明: 本文为 InfoQ 作者【DS小龙哥】的原创文章。
原文链接:【http://xie.infoq.cn/article/1b13c568742992f305b1e69ca】。文章转载请联系作者。
DS小龙哥
之所以觉得累,是因为说的比做的多。 2022.01.06 加入
熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域
评论