
基于梁山派的智能窗帘项目
简介
本项目旨在设计并实现一种基于梁山派的智能窗帘系统,使窗帘的开合能够根据用户的需求自动完成,提高生活质量和使用体验。
简介:本项目旨在设计并实现一种基于梁山派的智能窗帘系统,使窗帘的开合能够根据用户的需求自动完成,提高生活质量和使用体验。开源协议
:CERN Open Hardware License
描述
一、设计概述
随着科技的发展,智能家居成为趋势,智能窗帘作为其中的重要组成部分,提供了自动化和便捷性,本项目旨在设计并实现一种基于梁山派的智能窗帘系统,使窗帘的开合能够根据用户的需求自动完成,提高生活质量和使用体验。
二、各模块介绍
(1)语音模块
HLK-V20语音模块用于提供给用户友好的交互体验,HLK-V20支持150条本地指令离线识别,HLK-V20S支持50条本地指令离线识别,可自由定制唤醒词、命令词与应答播报词,具有丰富的外围接口。该语音识别模块是可以由我们自由设计命令词的,可以通过在线配置平台进行配置。配置完成之后会生成一个语音识别固件,我们要将固件下载到模块里,通过模块的B6,B7引脚进行下载。所以下方通过排针的方式引出了B6,B7引脚方便我们下载。还需要注意的是,在下载固件时,要先将模块断电。待下载工具识别到模块之后,再给模块通电,才可以正常下载。这个通断电操作,由原理图中的SW1开关进行控制。
HLK-V20语音模块固件创建和下载:http://voice.hlktech.com/yunSound/public/toWebLogout
http://www.smartpi.cn/#/
端口连接如下:
VDD --> 5V
B2 --> PA3
B3 --> PA2
GND --> GND

(2)光敏传感器模块
光敏电阻工作原理是基于内光电效应。光照愈强,阻值就愈低,随着光照强度的升高,电阻值迅速降低,亮电阻值可小至1KΩ以下。光敏电阻对光线十分敏感,其在无光照时,呈高阻状态。光敏电阻的特殊性能,随着科技的发展将得到极其广泛应用。我们通过读取光敏电阻的阻值就可以判断当前的光线强度了。

(3)雨滴传感器模块
雨滴传感器是一种传感装置,主要用于检测是否下雨及雨量的大小,当雨滴传感器检测到雨滴时,其电阻值就会发生变化,我们可通过获取它的电阻值来判断是否有雨。我们需要给雨滴传感器的一极接正,一极接负,才可以产生回路测量电压变化。但是当传感器的雨滴过多时会导致两极彻底短路。为了防止雨滴传感器彻底短路,需要增加一个电阻R1作为负载,防止3.3V直接接入GND,形成电源短路。
端口连接如下:
RAIN_ADC --> PC3

(4)红外接收模块
红外光是以特定的频率脉冲形式发射,接收端收到到信号后,按照约定的协议进行解码,完成数据传输。接收端的原理: 接收端的芯片对这个红外光比较敏感,可以根据有没有光输出高低电平,如果发送端的闪烁频率是有规律的,接收端收到后输出的高电平和低电平也是有规律对应的,这样发送端和接收端只要约定好,那就可以做数据传输了。NEC协议采用脉冲间隔的方式编码每一位数据,用不同数据位的时间间隔来表示不同的逻辑位。与众多单总线IC的通信方式很相似。通过接收遥控器发送的红外光的时间来接收的数据,当接受到红外光就会拉低OUT引脚,GD32通过中断的方式接收数据即可。本工程GD32通过PF8与红外接收的OUT引脚相连。

(5)步进电机驱动模块
L9110S是一块直流电机驱动电路,该产品为电池供电的玩具、低压或电池供电的控制应用提供了一种集成直流马达驱动电路内部集成了采用MOS管设计的H桥驱动电路,主要应用于驱动通用直流电机。用于驱动窗帘电机的运转,两个驱动芯片驱动一个电机。本工程GD32通过PG12与A_FOR、PG10与A_BAK、PB9与B_FOR、PB6与B_BAK和电机芯片

三、主要代码
(1)主函数
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 优先级分组
//滴答定时器初始化 1us
systick_config();
//串口0初始化 调试
usart_gpio_config(9600U);
//语音识别模块引脚初始化
hlk_usart_init(9600U);
//雨滴与光照识别引脚初始化
raindrop_and_light_config();
//红外接收引脚初始化
infrared_goio_config();
//步进电机初始化
stepper_motor_config();
stepper_motor_timer_config();
//步进电机复位
curtain_reset();
while(1)
{
//语音命令识别操作:窗帘控制与模式切换
voice_anakysis_data();
//红外命令识别操作:窗帘控制与模式切换
infrared_command_judgment();
//模式选择与对应模式的操作
mode_select();
}
}
(2)雨滴与光照函数
#include "bsp_adc.h"
void raindrop_and_light_config(void)
{
//使能引脚时钟
rcu_periph_clock_enable(BSP_RAINDROP_GPIO_RCU);
rcu_periph_clock_enable(BSP_LIGHT_GPIO_RCU);
//使能ADC时钟
rcu_periph_clock_enable(BSP_ADC_RCU);
//配置ADC时钟
adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
//配置引脚为模拟输入模式
gpio_mode_set(BSP_RAINDROP_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, BSP_RAINDROP_GPIO_PIN);
gpio_mode_set(BSP_LIGHT_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, BSP_LIGHT_GPIO_PIN);
//配置ADC为独立模式
adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
//使能扫描模式
adc_special_function_config(BSP_ADC, ADC_SCAN_MODE, ENABLE);
//数据右对齐
adc_data_alignment_config(BSP_ADC, ADC_DATAALIGN_RIGHT);
//ADC0设置为12位分辨率
adc_resolution_config(BSP_ADC, ADC_RESOLUTION_12B);
//ADC0设置为规则组 一共使用 1 个通道
adc_channel_length_config(BSP_ADC,ADC_REGULAR_CHANNEL, 1);
//ADC外部触发禁用, 即只能使用软件触发
adc_external_trigger_config(BSP_ADC, ADC_REGULAR_CHANNEL, EXTERNAL_TRIGGER_DISABLE);
//ADC0使能
adc_enable(BSP_ADC);
//开启ADC自校准
adc_calibration_enable(BSP_ADC);
}
unsigned int get_adc_value(uint8_t adc_channel_x)
{
unsigned int adc_value = 0;
//设置采集通道
adc_regular_channel_config(BSP_ADC, 0, adc_channel_x, ADC_SAMPLETIME_15);
//开始软件转换
adc_software_trigger_enable(BSP_ADC, ADC_REGULAR_CHANNEL);
//等待 ADC 采样完成
while ( adc_flag_get(BSP_ADC, ADC_FLAG_EOC) == RESET )
{
;
}
//读取采样值
adc_value = adc_regular_data_read(BSP_ADC);
//返回采样值
return adc_value;
}
(3)步进电机驱动函数
#include "bsp_stepper_motor.h"
#include "systick.h"
#include "stdio.h"
#include "math.h"
uint8_t phasecw[8] = {0x08, 0x0c, 0x04, 0x06, 0x02, 0x03, 0x01, 0x09};
uint8_t phaseccw[8] = {0x09, 0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08};
uint8_t motor_cw_flag = 0;
uint8_t motor_ccw_flag = 0;
//当前步数
int step_count = 0;
//最大步数,每走一拍表示一步
//实测从左边尽头到右边尽头的步数是600步
//请根据自己设备的最大步数调整此参数
#define MAX_STEPS 600
void stepper_motor_timer_config(void)
{
/* 一个周期的时间T = 1/f, 定时时间time = T * 周期
设预分频值位pre,周期位per
time = (pre + 1) * (per + 1) / psc_clk
*/
timer_parameter_struct timere_initpara; // 定义定时器结构体
/* 开启时钟 */
rcu_periph_clock_enable(RCU_TIMER5); // 开启定时器时钟
/* CK_TIMERx = 4 x CK_APB1 = 4x50M = 200MHZ */
rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4); // 配置定时器时钟
timer_deinit(TIMER5); // 复位定时器
/* 配置定时器参数 */
timere_initpara.prescaler = 2000-1; // 时钟预分频值 0-65535
timere_initpara.alignedmode = TIMER_COUNTER_EDGE; // 边缘对齐
timere_initpara.counterdirection = TIMER_COUNTER_UP; // 向上计数
timere_initpara.period = 200-1; // 周期
timere_initpara.clockdivision = TIMER_CKDIV_DIV1; // 分频因子
timere_initpara.repetitioncounter = 0; // 重复计数器 0-255
timer_init(TIMER5,&timere_initpara); // 初始化定时器
/* 配置中断优先级 */
nvic_irq_enable(TIMER5_DAC_IRQn,1,2); // 设置中断优先级为 3,2
/* 使能中断 */
timer_interrupt_enable(TIMER5,TIMER_INT_UP); // 使能更新事件中断
/* 使能定时器 */
timer_enable(TIMER5);
}
void stepper_motor_config(void)
{
rcu_periph_clock_enable(AP_RCU); // 开启时钟
rcu_periph_clock_enable(AM_RCU); // 开启时钟
rcu_periph_clock_enable(BP_RCU); // 开启时钟
rcu_periph_clock_enable(BM_RCU); // 开启时钟
/* 配置A+推挽输出模式 上拉模式 */
gpio_mode_set(AP_PORT,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,AP_PIN);
gpio_output_options_set(AP_PORT,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,AP_PIN);
/* 配置A-推挽输出模式 上拉模式 */
gpio_mode_set(AM_PORT,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,AM_PIN);
gpio_output_options_set(AM_PORT,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,AM_PIN);
/* 配置B+推挽输出模式 上拉模式 */
gpio_mode_set(BP_PORT,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,BP_PIN);
gpio_output_options_set(BP_PORT,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,BP_PIN);
/* 配置B-推挽输出模式 上拉模式 */
gpio_mode_set(BM_PORT,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,BM_PIN);
gpio_output_options_set(BM_PORT,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,BM_PIN);
AP(0);
BP(0);
AM(0);
BM(0);
}
void motor_cw(void)
{
static uint8_t i=0;
//开启了顺时针动作
if( motor_cw_flag == 1 )
{
AP ( ( phasecw[i] >> 3 ) & 0x01 );
BP ( ( phasecw[i] >> 2 ) & 0x01 );
AM ( ( phasecw[i] >> 1 ) & 0x01 );
BM ( ( phasecw[i] >> 0 ) & 0x01 );
//拍数增加
i = ( i + 1 ) % 8;
//记录当前步数
step_count++;
}
}
void motor_ccw( void )
{
static uint8_t i=0;
//如果开启了逆时针动作
if( motor_ccw_flag == 1 )
{
AP ( ( phaseccw[i] >> 3 ) & 0x01 );
BP ( ( phaseccw[i] >> 2 ) & 0x01 );
AM ( ( phaseccw[i] >> 1 ) & 0x01 );
BM ( ( phaseccw[i] >> 0 ) & 0x01 );
i = ( i + 1 ) % 8;
//记录当前步数
if( step_count <= 1 ) step_count = 1;
step_count--;
}
}
void motor_stop( void )
{
AP ( 0 );
BP ( 0 );
AM ( 0 );
BM ( 0 );
}
void curtain_reset(void)
{
//如果步数没有到达最大步数
while( get_step_count() < MAX_STEPS )
{
//步进电机顺时针旋转,步数累加
motor_cw_flag = 1;
motor_ccw_flag = 0;
}
//停止步进电机动作
motor_cw_flag = 0;
motor_stop();
}
void open_curtain(void)
{
//如果步数没有到达0步
if( get_step_count() > 0 )
{
//步进电机逆时针旋转,步数累减
motor_ccw_flag = 1;
motor_cw_flag = 0;
}
else
{
//停止步进电机动作
motor_ccw_flag = 0;
motor_cw_flag = 0;
motor_stop();
}
}
void close_curtain(void)
{
//如果步数没有到达最大步数
if( get_step_count() < MAX_STEPS )
{
//步进电机顺时针旋转,步数累加
motor_cw_flag = 1;
motor_ccw_flag = 0;
}
else
{
//停止步进电机动作
motor_cw_flag = 0;
motor_ccw_flag = 0;
motor_stop();
}
}
void limit_judgment(int num)
{
//如果当前是顺时针旋转
if( motor_cw_flag == 1 )
{
//如果顺时针旋转的步数已经累加超过最大步数
if( num >= MAX_STEPS )
{
//停止旋转
motor_cw_flag = 0;
motor_stop();
}
}
//如果当前是逆时针旋转
if( motor_ccw_flag == 1 )
{
//如果逆时针旋转的步数已经累减到0步
if( num <= 0 )
{
//停止旋转
motor_ccw_flag = 0;
motor_stop();
}
}
}
int get_step_count(void)
{
return step_count;
}
void set_step_count(int num)
{
step_count = num;
}
void TIMER5_DAC_IRQHandler(void)
{
/* 这里是定时器中断 */
if(timer_interrupt_flag_get(TIMER5,TIMER_INT_FLAG_UP) == SET)
{
timer_interrupt_flag_clear(TIMER5,TIMER_INT_FLAG_UP); // 清除中断标志位
//顺时针旋转
motor_cw();
//逆时针旋转
motor_ccw();
}
}
(4)红外接收函数
#include "bsp_ir_receiver.h"
#include "bsp_usart.h"
#include "stdio.h"
#include "systick.h"
#include "bsp_mode_control.h"
#include "bsp_stepper_motor.h"
#include "bsp_voice.h"
typedef struct INFRARED_DATA{
uint8_t AddressCode; //地址码
uint8_t AddressInverseCode; //地址反码
uint8_t CommandCode; //命令码
uint8_t CommandInverseCode; //命令反码
}_INFRARED_DATA_STRUCT_;
//红外数据结构体
_INFRARED_DATA_STRUCT_ InfraredData;
void infrared_goio_config(void)
{
//开启引脚时钟
rcu_periph_clock_enable(IR_RCU);
//开启系统配置时钟
rcu_periph_clock_enable(RCU_SYSCFG);
//配置引脚为上拉输入模式
gpio_mode_set(IR_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, IR_PIN);
/* 使能NVIC中断 中断分组为2位抢占优先级,2位子优先级 */
nvic_irq_enable(EXTI_IRQ,2U,2U); // 抢占优先级2,子优先级2
/* 连接中断线到GPIO */
syscfg_exti_line_config(EXTI_SOURCE_PORT,EXTI_SOURCE_PIN);
/* 初始化中断线下降沿触发 */
exti_init(EXTI_X,EXTI_INTERRUPT,EXTI_TRIG_FALLING);
/* 使能中断 */
exti_interrupt_enable(EXTI_X);
/* 清除中断标志位 */
exti_interrupt_flag_clear(EXTI_X);
}
{
uint32_t time_val = 0;
while( gpio_input_bit_get(IR_PORT, IR_PIN) == 0 )
{
if( time_val>= 500 )
{
*low_time = time_val;
return;
}
delay_1us(20);
time_val++;
}
*low_time = time_val;
}
void get_infrared_high_time(uint32_t *high_time)
{
uint32_t time_val = 0;
while( gpio_input_bit_get(IR_PORT, IR_PIN) == 1 )
{
if( time_val >= 250 )
{
*high_time = time_val;
return;
}
delay_1us(20);
time_val++;
}
*high_time = time_val;
}
uint8_t guide_and_repeat_code_judgment(void)
{
uint32_t out_time=0;
get_infrared_low_time(&out_time);
//time>10ms time <8ms
if((out_time > 500) || (out_time < 400))
{
return 1;
}
get_infrared_high_time(&out_time);
// x>5ms 或者 x<2ms
if((out_time > 250) || (out_time < 100))
{
return 1;
}
//如果是重复码 2ms < time < 3ms
if((out_time > 100) && (out_time < 150))
{
return 2;
}
return 0;
}
uint8_t infrared_data_true_judgment(uint8_t *value)
{
//判断地址码是否正确
if( value[0] != (uint8_t)(~value[1]) ) return 0;
//判断命令码是否正确
if( value[2] != (uint8_t)(~value[3]) ) return 1;
printf("%x %x %x %x\r\n",value[0],value[1],value[2],value[3]);
//保存正确数据
InfraredData.AddressCode = value[0];
InfraredData.AddressInverseCode = value[1];
InfraredData.CommandCode = value[2];
InfraredData.CommandInverseCode = value[3];
}
void receiving_infrared_data(void)
{
uint16_t group_num = 0,data_num = 0;
uint32_t time=0;
uint8_t bit_data = 0;
uint8_t ir_value[5] = {0};
uint8_t guide_and_repeat_code = 0;
//等待引导码
guide_and_repeat_code = guide_and_repeat_code_judgment();
//如果不是引导码则结束解析
if( guide_and_repeat_code == 1 )
{
printf("err\r\n");
return;
}
//共有4组数据
//地址码+地址反码+命令码+命令反码
for(group_num = 0; group_num < 4; group_num++ )
{
//接收一组8位的数据
for( data_num = 0; data_num < 8; data_num++ )
{
//接收低电平
get_infrared_low_time(&time);
//如果不在0.56ms内的低电平,数据错误
if((time > 60) || (time < 20))
{
return ;
}
time = 0;
//接收高电平
get_infrared_high_time(&time);
//如果是在1200us<t<2000us范围内则判断为1
if((time >=60) && (time < 100))
{
bit_data = 1;
}
//如果是在200us<t<1000us范围内则判断为0
else if((time >=10) && (time < 50))
{
bit_data = 0;
}
//groupNum表示第几组数据
ir_value[ group_num ] <<= 1;
//接收的第1个数为高电平;在第二个for循环中,数据会向右移8次
ir_value[ group_num ] |= bit_data;
//用完时间要重新赋值
time=0;
}
}
//判断数据是否正确,正确则保存数据
infrared_data_true_judgment(ir_value);
}
uint8_t get_infrared_command(void)
{
return InfraredData.CommandCode;
}
void clear_infrared_command(void)
{
InfraredData.CommandCode = 0x00;
}
void infrared_command_judgment(void)
{
//播报【当前为自动模式】语音
unsigned char auto_mode_report[5] = {0XAA, 0X55, 0X01, 0X55, 0XAA};
//播报【当前为手动模式】语音
unsigned char manual_mode_report[5] = {0XAA, 0X55, 0X02, 0X55, 0XAA};
//播报【窗帘已打开】语音
unsigned char curtain_open_report[5] = {0XAA, 0X55, 0X03, 0X55, 0XAA};
//播报【窗帘已关闭】语音
unsigned char curtain_clear_report[5] = {0XAA, 0X55, 0X04, 0X55, 0XAA};
//如果按下【*】键,进入自动模式
if( get_infrared_command() == 0X68 )
{
clear_infrared_command();
set_mode_switch_flag(AUTO_MODE);
//播报【当前为自动模式】语音
hlk_usart_send_string(auto_mode_report, 5);
}
//如果按下【#】键,进入非自动模式
if( get_infrared_command() == 0Xb0 )
{
clear_infrared_command();
set_mode_switch_flag(NO_AUTO_MODE);
//播报【当前为手动模式】语音
hlk_usart_send_string(manual_mode_report, 5);
}
//如果按下【<】键,关闭窗帘
if( get_infrared_command() == 0X10 )
{
clear_infrared_command();
close_curtain();
set_mode_switch_flag(NO_AUTO_MODE);
//播报【窗帘已关闭】语音语音
hlk_usart_send_string(curtain_clear_report, 5);
}
//如果按下【>】键,展开窗帘
if( get_infrared_command() == 0X5a )
{
clear_infrared_command();
open_curtain();
set_mode_switch_flag(NO_AUTO_MODE);
//播报【窗帘已打开】语音语音
hlk_usart_send_string(curtain_open_report, 5);
}
}
void EXTI5_9_IRQHandler(void)
{
if(exti_interrupt_flag_get(EXTI_X) == SET) // 中断标志位为1
{
if(gpio_input_bit_get(IR_PORT,IR_PIN) == RESET) // 如果是低电平
{
//接收一次红外数据
receiving_infrared_data();
}
exti_interrupt_flag_clear(EXTI_X); // 清中断标志位
}
}
(5)语音识别函数
#include "bsp_voice.h"
#include "stdio.h"
#include "string.h"
#include "bsp_stepper_motor.h"
#include "bsp_mode_control.h"
unsigned char hlk_rx_buff[HLK_RX_LEN_MAX];
unsigned char hlk_rx_flag = 0;
unsigned char hlk_rx_len = 0;
unsigned char rx_data = 0;
unsigned char rx_flag = 0;
void hlk_usart_init(unsigned int bund)
{
/* 使能 HLK_USART 的时钟 */
rcu_periph_clock_enable(RCU_HLK_USART);
/* 使能时钟 */
rcu_periph_clock_enable(RCU_HLK_TX);
rcu_periph_clock_enable(RCU_HLK_RX);
/* 配置引脚为复用功能 */
gpio_af_set(PORT_HLK_TX, BSP_HLK_AF, GPIO_HLK_TX);
/* 配置引脚为复用功能 */
gpio_af_set(PORT_HLK_RX, BSP_HLK_AF, GPIO_HLK_RX);
/* 配置TX引脚为复用上拉模式 */
gpio_mode_set(PORT_HLK_TX, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_HLK_TX);
/* 配置RX引脚为复用上拉模式 */
gpio_mode_set(PORT_HLK_RX, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_HLK_RX);
/* 配置PA2引脚为为输出模式 */
gpio_output_options_set(PORT_HLK_TX, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_HLK_TX);
/* 配置PA3引脚为为输出模式 */
gpio_output_options_set(PORT_HLK_RX, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_HLK_RX);
/* 设置HLK_USART的波特率为115200 */
usart_baudrate_set(HLK_USART, bund);
/* 设置HLK_USART的校验位为无 */
usart_parity_config(HLK_USART, USART_PM_NONE);
/* 设置HLK_USART的数据位为8位 */
usart_word_length_set(HLK_USART, USART_WL_8BIT);
/* 设置HLK_USART的停止位为1位 */
usart_stop_bit_set(HLK_USART, USART_STB_1BIT);
/* 使能串口1 */
usart_enable(HLK_USART);
/* 使能HLK_USART传输 */
usart_transmit_config(HLK_USART, USART_TRANSMIT_ENABLE);
/* 使能HLK_USART接收 */
usart_receive_config(HLK_USART, USART_RECEIVE_ENABLE);
/* 使能HLK_USART接收中断标志位 */
usart_interrupt_enable(HLK_USART, USART_INT_RBNE);
/* 使能HLK_USART空闲中断标志位 */
usart_interrupt_enable(HLK_USART, USART_INT_IDLE);
/* 配置中断优先级 */
nvic_irq_enable(HLK_USART_IRQ, 2, 2);
}
void hlk_usart_send_bit(unsigned char ch)
{
//发送字符
usart_data_transmit(HLK_USART, ch);
// 等待发送数据缓冲区标志自动置位
while(RESET == usart_flag_get(HLK_USART, USART_FLAG_TBE) );
}
void hlk_usart_send_string(unsigned char *str, unsigned int str_len)
{
while( str_len-- ) // 地址为空或者值为空跳出
{
hlk_usart_send_bit(*str++);
}
}
void clear_hlk_rx_buff(void)
{
unsigned char i = HLK_RX_LEN_MAX-1;
while(i)
{
hlk_rx_buff[i--] = 0;
}
hlk_rx_len = 0;
hlk_rx_flag = 0;
}
unsigned char voice_anakysis_data(void)
{
unsigned char ret = 0;
if( rx_flag == 1 )//接收到语音命令
{
rx_flag = 0;
switch( rx_data )//根据语音命令确定对应的动作
{
case 0x01://开窗帘命令
open_curtain();
//设置非自动模式
set_mode_switch_flag(NO_AUTO_MODE);
break;
case 0x02://关窗帘命令
close_curtain();
//设置非自动模式
set_mode_switch_flag(NO_AUTO_MODE);
break;
case 0x03://自动模式命令
set_mode_switch_flag(AUTO_MODE);
break;
case 0x04://手动模式命令
set_mode_switch_flag(NO_AUTO_MODE);
break;
}
ret = 1;
//清除当前数据
clear_hlk_rx_buff();
}
//步进电机限位判断
limit_judgment(get_step_count());
return ret;
}
void HLK_USART_IRQHandler(void)
{
if(usart_interrupt_flag_get(HLK_USART,USART_INT_FLAG_RBNE) != RESET) // 接收缓冲区不为空
{
//接收数据
hlk_rx_buff[ hlk_rx_len ] = usart_data_receive(HLK_USART);
#if DEBUG
//测试,查看接收到了什么数据
printf("%c", hlk_rx_buff[ hlk_rx_len ]);
#endif
//0XAA X 0X55
if( hlk_rx_buff[hlk_rx_len] == 0X55 )//接收到帧尾
{
if( hlk_rx_buff[hlk_rx_len-2] == 0XAA)//接收到帧头,确定数据格式正确
{
rx_data = hlk_rx_buff[hlk_rx_len-1];//接收数据
rx_flag = 1;
}
}
//接收长度限制
hlk_rx_len = ( hlk_rx_len + 1 ) % HLK_RX_LEN_MAX;
}
if(usart_interrupt_flag_get(HLK_USART,USART_INT_FLAG_IDLE) == SET) // 检测到空闲中断
{
usart_data_receive(HLK_USART); // 必须要读,读出来的值不能要
hlk_rx_buff[hlk_rx_len] = '\0'; //字符串结尾补 '\0'
hlk_rx_flag = 1; // 接收完成
}
}
四、总结
(1)创新点
1.提高生活便捷性:通过语音控制可直接去操控窗帘的开关以及窗帘的自动模式与手动模式。自动模式下用户无需手动操作窗帘,经检测大量雨滴后自动关闭窗帘,以及有强光照射后自动打开窗帘,为人们省下了大量的精力去操心窗帘的开关。手动模式下可通过红外遥控器实现远程开关窗帘。
2.光线和雨滴感应:传统的智能窗帘多以光线感应为主,而本项目通过光线传感器和雨滴传感器,实现对户外光线和雨滴的感知。这样智能窗帘能够根据环境的光线强度和天气情况自动调整开合程度,提供更舒适的室内环境。
(2)心得体会
在本次项目中由于知识方面的欠缺,进度较慢,通过查看资料和咨询老师和学长,及时调整方案,花了一段时间学习相关知识,在此过程中我们理解到项目最重要的是抓住研究的主要问题,我体会最深的是要勤于思考,要善于从不同角度分析问题,查找参考文献和教程,要找到创新方向不断地努力思考,一步步通过实践来论证实验的可行性。在项目中我们不断锻炼自己的技术能力和提高我们知识储量。
设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM
克隆工程知识产权声明&复刻说明
本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。
请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。










