怎么设计一款智能直流变频风扇并连接到涂鸦 IoT 云

更新时间Invalid date

概况

风扇由于便捷、易安装、能耗低的特性,在消费品市场非但没有受空调的影响,反而新增了远程控制、自然风、暖风、清凉风、睡眠风、负离子功能等新型产品,甚至还有驱蚊、低噪风扇,具有非常高的实用价值。

随着消费者对健康的日益关注,产品设计还可以在提高空气质量、便携移动、低噪声等方向进行新品研发。

本教程主要介绍目前市场上流行 直流变频风扇 的设计思路,通过方案设计、硬件设计、涂鸦 IoT 平台 DP 设置、嵌入式开发,来讲解普通风扇智能化的研发流程。如果你也感兴趣,可以一起动手操作,制作出一款原型产品,了解物联网硬件产品的设备端开发。

硬件设计思路如下:

智能电风扇硬件设计思路.png

物料清单

硬件 (8)软件 (3)
  • 无刷直流电机(BLDC)

    数量:1

    相比传统交流电机风扇,无刷直流电机(BLDC,Brushless DC electric motor)具有中低速转矩性能好等多种优势,且使用寿命更长。因此直流电机选用的是 MINEBEA 12V 直流三相无刷电机,作为风量控制电机。

  • 电机驱动器(FU6832 芯片)

    数量:1

    驱动器主要控制直流无刷电动机(Brushless DC electric motor,BLDC)。采用峰岹的 BLDC 解决方案,采用 FU6832 作为 BLDC 的控制 MCU。该芯片有双核 8051 内核 + ME,16KB Flash,256 bytes IRAM, 768 bytes XRAM SSOP24 封装,集成 BLDC 控制算法。

  • 涂鸦 CBU Wi-Fi 模组

    数量:1

    模组的选择影响到智能硬件的无线通信方式。其中,Wi-Fi 作为常见的通信方式,也是居家或者商业运用较广的方案,因此采用涂鸦 SoC 方案的 CBU Wi-Fi 模组作为通信模组。查看详情

  • 旋钮编码器

    数量:1

    作为风量调节旋钮,顺时针调大,逆时针调小,该编码器可根据要求,设置需要的档位数。该器件手柄支持360°选择,结构上无限位点。

  • 指示灯

    数量:8

    至少 8 个指示灯,由 Wi-Fi 模组的 I/O 口控制。

  • 按键

    数量:4

    用于电源、定时、按键、Wi-Fi 配网重置、风扇工作模式的按键。

  • 电源插头

    数量:1

    输出 12V DC/2A,用于满足风扇整体供电要求。

  • 转换电路(矽力杰 SY8121B 芯片)

    数量:1

    转换市电然后用于 Wi-Fi 模组及外围供电、FU6832S 电机驱动控制芯片及外围供电。

步骤

一:方案选型

有关风量控制电机、电机驱动器等器件的选型,请参考上文物料清单。本小节继续介绍相关器件的其他信息,方便您了解细节逻辑:

  • 电机驱动器 芯片 I/O 口:

  • 旋钮编码器(风量调节)

    • 编码器的脉冲波形:

    • 编码器的外围电路:

  • 按键设计:方案设计 4 个按键,按键均设置为低电平有效,按键功能:

    • 电源按键:开启关闭风扇。

    • 定时按键:设置风扇定时开关的时间段。

    • Wi-Fi 配网重置按键:清除已联网的 Wi-Fi 的网络信息,恢复到出厂设置状态。

    • 模式按键:设置风扇工作模式。模式按键设置在编码器上。

  • 指示灯设计:方案设计 8 个指示灯,指示灯由 CBU Wi-Fi 模组的 I/O 口控制,设置均为低电平点亮,指示灯功能:

    • 4 个档位指示灯

    • 3 个模式指示灯

    • 1 个网络状态指示灯

    • 增加一个指示灯亮度调节控制 I/O 口,可设置调节 LED 指示灯亮度

  • 交互信号:采用 PWM 信号作为 Wi-Fi 模组和 FU6832S 电机驱动控制芯片的交互信号。

  • 电源方案:电源选用绿源电源插头,输出 12V DC/2A,用于满足风扇整体供电要求。

  • 转换电路:此次教程需要一个 12V DC 转 3.3V DC 的转换电路,用于 Wi-Fi 模组及外围供电。一个 12V DC 转 5V DC 的转换电路,用于 FU6832S 电机驱动控制芯片及外围供电。

    转换电路采用基于矽力杰的 SY8121B 芯片的降压(Buck)电路。SY8121B 是一款高效,响应速度快的同步整流方案的 DC-DC 转换芯片。

    • 输入电压范围:4.5V~18V
    • 输出电流:2A(max)

二:电路接线参考

  • Wi-Fi 模组及外围电路接线:

  • DC-DC 转换电路接线:

  • FU6832S 电机控制芯片及外围电路:

三:设备功能设计

功能设计
详细说明
工作模式 正常风
自然风:忽大忽小间隔 15 秒
睡眠风:每隔一小时自动降档,最后降到最低档
风速控制 编码器旋转控制风扇,顺时针旋转风速 +,逆时针旋转风速 -
模式切换 编码器按钮短按切换模式,长按设备复位
本地定时 定时结束后自动关机,定时按键:无定时 -> 1 小时 -> 2 小时 -> 3 小时 -> 4 小时 -> 无定时。
指示灯显示 1 4 颗指示灯显示风速,4 颗指示灯指示 8 档风速,闪烁代表 1 档,常亮代表 2 档。4 颗指示灯还复用本地定时指示。
指示灯显示 2 LED 有 8 颗,除指示风速 4 颗 LED 外,还有 4 颗指示灯。一颗 Wi-Fi 指示灯,指示 Wi-Fi 状态;其他三颗指示当前风扇模式。
电源 电源按键,控制风扇的启动和断电状态
设备配网 长按 Wi-Fi 按键,设备进入配网模式
指示灯调节 LED 指示灯亮度可通过 App 调整,正常亮度,较暗亮度
断电记忆 设备通电自动恢复

四:实物组装

  • 电机驱动板和电压转换板:

  • Wi-Fi 模组控制板:

五:在 IoT 平台创建产品

  1. 进入 涂鸦 IoT 平台的产品创建页面

  2. 创建一款风扇产品,创建产品时,选择 小家电 > 风扇,开发方案确认为 自定义方案,通讯方式选择为 Wi-Fi

    image.png

  3. 设置 DP 时,需要考虑您想要添加的风扇功能,与嵌入式开发的功能设置做一一映射。

    image.png

  4. 选择面板后,就进入硬件开发阶段,此时选择 涂鸦标准模组SDK开发 作为云端连接方式,并选择 CBU 模组为通信模组。

    image.png

更多详情和步骤说明,请参考 Wi-Fi 模组 SDK 开发参考

六:嵌入式功能开发

相关代码仓库:

根据本节进行代码开发和代码编译后,您需要将生成的固件上传,填入具体信息。详情请参考 固件升级

1:配网及配网指示灯显示

调用该函数 tuya_iot_wf_gw_unactive() 以进入配网模式,涂鸦模组 SDK 对于网络状态的定义有以下几种:

typedef BYTE_T GW_WIFI_NW_STAT_E;
#define STAT_LOW_POWER          0   // idle status,use to external config network
#define STAT_UNPROVISION        1   // smart config status
#define STAT_AP_STA_UNCFG       2   // ap Wi-Fi config status
#define STAT_AP_STA_DISC        3   // ap Wi-Fi already config,station disconnect
#define STAT_AP_STA_CONN        4   // ap station mode,station connect
#define STAT_STA_DISC           5   // only station mode,disconnect
#define STAT_STA_CONN           6   // station mode connect
#define STAT_CLOUD_CONN         7   // cloud connect
#define STAT_AP_CLOUD_CONN      8   // cloud connect and ap start
#define STAT_REG_FAIL           9   // register fail
#define STAT_OFFLINE            10   // offline
#define STAT_MQTT_ONLINE        11
#define STAT_MQTT_OFFLINE       12
#define STAT_UNPROVISION_AP_STA_UNCFG		13 //smart-cfg and ap-cfg concurrent config status
  • 长按进入配网模式功能实现:

    STATIC VOID wifi_key_process(TY_GPIO_PORT_E port,PUSH_KEY_TYPE_E type,INT_T cnt)
    {
    	PR_DEBUG("port:%d,type:%d,cnt:%d",port,type,cnt);
    	OPERATE_RET op_ret = OPRT_OK;
    	UCHAR_T ucConnectMode = 0;
    
    	if (port = WIFI_KEY_PIN) {
    		if (LONG_KEY == type) { //press long enter linking network
    			PR_NOTICE("key long press");
    			/* 手动移除设备 */
    			tuya_iot_wf_gw_unactive();
    		} else if (NORMAL_KEY == type) {
    			PR_NOTICE("key normal press");
    		} else {
    			PR_NOTICE("key type is no deal");
    		}
    	}
    
    	return;
    }
    
    STATIC VOID wifi_config_init(VOID)
    {
    	OPERATE_RET op_ret = OPRT_OK;
    
    	/* LED 相关初始化 */
    	tuya_gpio_inout_set(WIFI_LED_PIN, FALSE);
    	tuya_set_led_light_type(wifi_led_handle, OL_HIGH, 0, 0); //关闭 LED
    
    	/* LED 相关初始化 */
    	op_ret = tuya_create_led_handle(WIFI_LED_PIN, TRUE, &wifi_led_handle);
    	if (op_ret != OPRT_OK) {
    		PR_ERR("key_init err:%d", op_ret);
    		return;
    	}
    	tuya_set_led_light_type(wifi_led_handle, OL_HIGH, 0, 0);
    
    	/* 按键相关初始化 */
    	KEY_USER_DEF_S key_def;
    
    	op_ret = key_init(NULL, 0, WIFI_KEY_TIMER_MS);
    	if (op_ret != OPRT_OK) {
    		PR_ERR("key_init err:%d", op_ret);
    		return;
    	}
    
    	/* 初始化 key 相关参数 */
    	memset(&key_def, 0, SIZEOF(key_def));
    	key_def.port = WIFI_KEY_PIN;                            //按键引脚
    	key_def.long_key_time = WIFI_KEY_LONG_PRESS_MS;         //长按时间配置
    	key_def.low_level_detect = WIFI_KEY_LOW_LEVEL_ENABLE;   //TRUE:低电平算按下,FALSE:高电平算按下
    	key_def.lp_tp = LP_ONCE_TRIG;   //
    	key_def.call_back = wifi_key_process;                   //按键按下后回调函数
    	key_def.seq_key_detect_time = WIFI_KEY_SEQ_PRESS_MS;    //连按间隔时间配置
    
    	/* 注册按键 */
    	op_ret = reg_proc_key(&key_def);
    	if (op_ret != OPRT_OK) {
    		PR_ERR("reg_proc_key err:%d", op_ret);
    	}
    
    	return;
    }
    
  • Wi-Fi 状态提示:

    STATIC VOID wifi_state_led_reminder(IN CONST GW_WIFI_NW_STAT_E cur_stat)
    {
    	switch (cur_stat)
    	{
    		case STAT_LOW_POWER:    //wifi 连接超时,进入低功耗模式
    			tuya_set_led_light_type(wifi_led_handle, OL_HIGH, 0, 0); //关闭提示灯
    		break;
    
    		case STAT_UNPROVISION: //SamrtConfig 配网模式,等待连接
    			tuya_set_led_light_type(wifi_led_handle, OL_FLASH_HIGH, WIFI_LED_FAST_FLASH_MS, 0xffff); //LED 快闪
    		break;
    
    		case STAT_AP_STA_UNCFG: //ap 配网模式,等待连接
    			tuya_set_led_light_type(wifi_led_handle, OL_FLASH_HIGH, WIFI_LED_LOW_FLASH_MS, 0xffff); //LED 慢闪
    		break;
    
    		case STAT_AP_STA_DISC:
    		case STAT_STA_DISC:     //SamrtConfig/ap 正在连接中
    			tuya_set_led_light_type(wifi_led_handle, OL_HIGH, 0, 0); //关闭 LED
    		break;
    
    		case STAT_CLOUD_CONN:
    		case STAT_AP_CLOUD_CONN: //连接到涂鸦 IoT
    			tuya_set_led_light_type(wifi_led_handle, OL_LOW, 0, 0); //LED 常亮
    		break;
    
    		default:
    		break;
    	}
    }
    

2:风扇工作模式

常见的风扇都会有相关的工作模式,最简单的就是控制风量,从弱到强。除此之外,我们还可以根据生活场景来设计设备的模式,例如普通、自然风、睡眠风三种工作模式,然后通过 PWM 对 BLDC 进行控制。

  • 风扇的控制函数:

    VOID_T fan_speed_set(UINT_T speed)
    {
    	UINT_T  fan_speed_pwm_duty_cycle = 0;
    
    	if (speed <= 0) {
    		vSocPwmSetDuty(BLDC_PWM_ID, (BLDC_PWM_FAN_OFF));
    		return;
    	}
    
    	//由于电机在30%以下工作时间过长会出现异常,这里对 PWM 输出进行一些处理,使输出的 PWM 在 30%-99% 之间
    	fan_speed_pwm_duty_cycle = (UINT_T)(BLDC_PWM_FAN_MIN + ((BLDC_PWM_FAN_MAX - BLDC_PWM_FAN_MIN) * (speed / 100.0)));
    
    	vSocPwmSetDuty(BLDC_PWM_ID, (fan_speed_pwm_duty_cycle));
    
    	return;
    }
    
  • 普通工作模式:

    static VOID_T fan_mode_normal(VOID_T)
    {
    	INT_T opRet = LIGHT_OK;
    
    	//关闭睡眠模式的定时器,防止干扰普通模式的运行
    	opRet = opSocSWTimerStop(SLEEP_MODE_TIMER);
    	if (opRet != LIGHT_OK) {
    		PR_ERR("stop sleep timer error");
    	}
    
    	//关闭自然模式的定时器,防止干扰普通模式的运行
    	opRet = opSocSWTimerStop(NATURAL_MODE_TIMER);
    	if (opRet != LIGHT_OK) {
    		PR_ERR("stop natural timer error");
    	}
    
    	fan_speed_set(fan_state.speed);
    	PR_NOTICE("+++ normal mode fan_state.speed : %d", fan_state.speed);
    }
    
  • 自然风工作模式:

    static VOID_T fan_mode_natural_timer_cb(VOID_T)
    {
    	//如果关机,不执行任何操作
    	if (fan_state.on_off == FALSE) {
    		opSocSWTimerStop(NATURAL_MODE_TIMER);
    		return;
    	}
    
    	if (natural_speed_low_flag) {
    		PR_NOTICE("natural mode low speed");
    		fan_speed_set(1);
    	} else {
    		PR_NOTICE("natural mode high speed");
    		fan_speed_set(fan_state.speed);
    	}
    	natural_speed_low_flag = ~(natural_speed_low_flag);
    	opSocSWTimerStart(NATURAL_MODE_TIMER, NATURAL_SPEED_CHANGE_TIME * 1000, fan_mode_natural_timer_cb);
    }
    
    static VOID_T fan_mode_natural(VOID_T)
    {
    	INT_T opRet = LIGHT_OK;
    
    	//关闭睡眠模式的定时器,防止干扰自然模式的运行
    	opRet = opSocSWTimerStop(SLEEP_MODE_TIMER);
    	if (opRet != LIGHT_OK) {
    		PR_ERR("stop sleep timer error");
    	}
    
    	natural_speed_low_flag = ~(0x00);
    	fan_speed_set(fan_state.speed);
    
    	opSocSWTimerStart(NATURAL_MODE_TIMER, NATURAL_SPEED_CHANGE_TIME * 1000, fan_mode_natural_timer_cb);
    }
    
  • 睡眠风工作模式:

    static VOID_T fan_sleep_mode_task(VOID_T)
    {
    	UINT8_T cur_gear;
    
    	PR_NOTICE("enter fan_sleep_mode_task!");
    	//判断当前是不是最低档。若为最低档,不再降速
    	if (fan_state.speed <= g_fan_speed_gear[0]) {
    		fan_speed_set(g_fan_speed_gear[0]);
    		change_fan_state();
    		opSocSWTimerStop(SLEEP_MODE_TIMER);
    		return;
    	}
    
    	cur_gear = get_cur_gear();
    	PR_NOTICE("current gear is %d.", cur_gear);
    	fan_state.speed = g_fan_speed_gear[--cur_gear];
    
    	//改变档位转速
    	fan_speed_set(fan_state.speed);
    	fan_speed_led_set(get_cur_gear()+1);
    	PR_NOTICE("speed change to %d.", fan_state.speed);
    	//写入风扇状态到Flash中
    	write_flash_fan_state();
    
    	//启动睡眠模式,1h 减一档
    	opSocSWTimerStart(SLEEP_MODE_TIMER, SLEEP_SPEED_CHANGE_TIME * 1000, fan_sleep_mode_task);
    }
    
    static VOID_T fan_mode_sleep(VOID_T)
    {
    	UINT8_T cur_gear;
    	INT_T opRet = LIGHT_OK;
    	SHORT_T i;
    
    	//关闭自然模式的定时器,防止干扰睡眠模式的运行
    	opRet = opSocSWTimerStop(NATURAL_MODE_TIMER);
    	if (opRet != LIGHT_OK) {
    		PR_ERR("stop sleep timer error");
    	}
    	opRet = opSocSWTimerStop(SLEEP_MODE_TIMER);
    	if (opRet != LIGHT_OK) {
    		PR_ERR("stop sleep timer error");
    	}
    
    	//判断当前档位
    	cur_gear = get_cur_gear();
    	fan_state.speed = g_fan_speed_gear[cur_gear];
    	//改变档位转速
    	fan_speed_set(fan_state.speed);
    	PR_NOTICE("speed change to %d.", fan_state.speed);
    	//写入风扇状态到Flash中
    	write_flash_fan_state();
    
    	opSocSWTimerStart(SLEEP_MODE_TIMER, SLEEP_SPEED_CHANGE_TIME * 1000, fan_sleep_mode_task);
    }
    

3:编码器及按键

  • 按键初始化:

    VOID_T fan_key_init(VOID_T)
    {
    	OPERATE_RET opRet;
    
    	tuya_gpio_inout_set(KEY_ROTARY_A, TRUE);
    	tuya_gpio_inout_set(KEY_ROTARY_B, TRUE);
    
    	/* 旋钮正反转检测初始化 */
    	BkGpioEnableIRQ(KEY_ROTARY_A, IRQ_TRIGGER_FALLING_EDGE, knod_key_cb, NULL);
    
    	opRet = key_init(NULL, 0, 0);
    	if (opRet != OPRT_OK) {
    		PR_ERR("key_init err:%d", opRet);
    		return;
    	}
    
    	memset(&KEY_DEF_T, 0, SIZEOF(KEY_DEF_T));
    	KEY_DEF_T.port = KEY_ROTARY_N;
    	KEY_DEF_T.long_key_time = 3000;
    	KEY_DEF_T.low_level_detect = TRUE;
    	KEY_DEF_T.lp_tp = LP_ONCE_TRIG;
    	KEY_DEF_T.call_back = key_press_cb;
    	KEY_DEF_T.seq_key_detect_time = 400;
    	opRet = reg_proc_key(&KEY_DEF_T);
    	if (opRet != OPRT_OK) {
    		PR_ERR("reg_proc_key err:%d", opRet);
    		return;
    	}
    
    	KEY_DEF_T.port = KEY_TIMER;
    	opRet = reg_proc_key(&KEY_DEF_T);
    	if (opRet != OPRT_OK) {
    		PR_ERR("reg_proc_key err:%d", opRet);
    		return;
    	}
    
    	KEY_DEF_T.port = KEY_POWER;
    	KEY_DEF_T.long_key_time = 10000;
    	opRet = reg_proc_key(&KEY_DEF_T);
    	if (opRet != OPRT_OK) {
    		PR_ERR("reg_proc_key err:%d", opRet);
    		return;
    	}
    }
    
  • 按键功能回调函数:

    编码器回调函数,编码器功能的功能实现,简单的使用的外部中断触发后,开始判断 A 和 B 引脚电平是否相同来确认是顺时针旋转还是逆时针旋转。

    STATIC VOID_T knod_key_cb(VOID_T)
    {
    	INT8_T current_gear;
    	//如果关机,不执行任何操作
    	if (fan_state.on_off == FALSE) {
    		return;
    	}
    
    	BkGpioFinalize(KEY_ROTARY_A);
    
    	//得到当前档位
    	current_gear = get_cur_gear();
    
    	if(tuya_gpio_read(KEY_ROTARY_A) != tuya_gpio_read(KEY_ROTARY_B)) {
    		PR_DEBUG("A != B"); //顺时针方向
    		current_gear++;
    		if (current_gear > (MAX_GEAR_NUMBER-1)) {
    			current_gear = (MAX_GEAR_NUMBER-1);
    		}
    		fan_state.speed = g_fan_speed_gear[current_gear];
    
    	} else {
    		PR_DEBUG("A == B"); //逆时针方向
    		current_gear--;
    		if (current_gear < 0) {
    			current_gear = 0;
    		}
    		fan_state.speed = g_fan_speed_gear[current_gear];
    	}
    
    	/* 改变风扇状态:风速,模式,LED */
    	change_fan_state();
    	write_flash_fan_state();
    
    	PR_DEBUG("fan current_gear is : %d", current_gear);
    
    	/* 旋钮正反转检测初始化 */
    	BkGpioEnableIRQ(KEY_ROTARY_A, IRQ_TRIGGER_FALLING_EDGE, knod_key_cb, NULL);
    }
    
  • 其他普通按键回调函数:

    STATIC VOID_T key_press_cb(TY_GPIO_PORT_E port,PUSH_KEY_TYPE_E type,INT_T cnt)
    {
    	PR_DEBUG("port: %d, type: %d, cnt: %d", port, type, cnt);
    
    	/* 旋钮按键 */
    	if (port == KEY_ROTARY_N) {
    		if (fan_state.on_off == FALSE) {
    			return;
    		}
    		switch (type) {
    			case NORMAL_KEY:
    				PR_DEBUG("knod press.");
    
    				if (fan_state.mode == NORMAL_MODE) {
    					fan_state.mode = NATURAL_MODE;
    				} else if (fan_state.mode == NATURAL_MODE) {
    					fan_state.mode =SLEEP_MODE;
    				} else {
    					fan_state.mode = NORMAL_MODE;
    				}
    				change_fan_state();
    				break;
    			case LONG_KEY:
    				PR_DEBUG("knod long press.");
    				/* 复位,删除所有用户信息,恢复到默认模式 */
    				fan_state = fan_default_state;
    				change_fan_state();
    				write_flash_fan_state();
    				break;
    
    			case SEQ_KEY:
    				PR_DEBUG("knod SEQ press, the count is %d.", cnt);
    				break;
    
    			default:
    				break;
    		}
    	}
    
    	/* 定时按键 */
    	if (port == KEY_TIMER) {
    		if (fan_state.on_off == FALSE) {
    			return;
    		}
    		switch (type) {
    			case NORMAL_KEY:
    				PR_DEBUG("timer press.");
    				if (fan_state.local_timing == 0xFF) {
    					fan_state.local_timing = 1;
    				} else if (fan_state.local_timing >= 4) {
    					fan_state.local_timing = 0xFF; //取消定时
    				} else {
    					fan_state.local_timing++;
    				}
    				fan_local_timing_shutdown();
    				break;
    			case LONG_KEY:
    				PR_DEBUG("timer long press.");
    
    				break;
    			case SEQ_KEY:
    				PR_DEBUG("timer SEQ press, the count is %d.", cnt);
    
    				break;
    			default:
    				break;
    		}
    	}
    
    	/* 开关按键 */
    	if (port == KEY_POWER) {
    		switch (type) {
    			case NORMAL_KEY:
    				if (fan_state.on_off == FALSE) {
    					fan_state.on_off = TRUE;
    					PR_DEBUG("Turn on");
    				} else {
    					fan_state.on_off = FALSE;
    					PR_DEBUG("Turn off");
    				}
    				change_fan_state();
    				break;
    			case LONG_KEY:
    				PR_DEBUG("power long press.");
    
    				break;
    			case SEQ_KEY:
    				PR_DEBUG("power SEQ press, the count is %d.", cnt);
    
    				break;
    			default:
    				break;
    		}
    	}
    	write_flash_fan_state();
    }
    

4:设备本地定时

定时功能是基础的控制功能,对持续工作的设备有重要作用,尤其是风扇、空调、香薰机等设备。

本地定时功能简单的调用了一个软件定时器来实现:

VOID_T fan_timing_cd(VOID_T)
{
	fan_state.local_timing--;
	opSocSWTimerStop(SHUTDOWN_TIMER);
	if (fan_state.local_timing == 0 || fan_state.on_off == FALSE) {
		fan_turn_off();
	} else {
		PR_NOTICE("fan_state.local_timing ======== %d", fan_state.local_timing);
		write_flash_fan_state();
		opSocSWTimerStart(SHUTDOWN_TIMER, (SINGLE_TIMING*1000), fan_timing_cd);
		fan_local_timing_led_set(fan_state.local_timing);
	}
}

VOID_T fan_local_timing_shutdown(VOID_T)
{
	fan_local_timing_led_set(fan_state.local_timing);
	if (fan_state.local_timing > 4) { //无定时
		opSocSWTimerStop(SHUTDOWN_TIMER);
		return;
	}
	PR_NOTICE("run shutdown timer");
	opSocSWTimerStop(SHUTDOWN_TIMER);
	opSocSWTimerStart(SHUTDOWN_TIMER, (SINGLE_TIMING*1000), fan_timing_cd);
}

5:通电自动恢复

自动恢复又称断电记忆,是指在设备断电并重新通电后,可以重新以之前的状态继续工作,无需人为干扰。

该功能依赖于 Flash,每次改变状态后,都将当前设备信息存储到 Flash 中。在设备通电后,首先读取 Flash 中的数据对设备状态进行设定。

VOID_T read_flash_fan_state(VOID_T)
{
	INT_T opRet, i;
	UCHAR_T fan_state_data_crc;
	UCHAR_T before_fan_power_off_state[FAN_STATE_STORAGE_LEN]; //断电前风扇状态

	opRet = uiSocFlashRead(SAVE_TYP1, FAN_STATE_OFFSET, FAN_STATE_STORAGE_LEN*SIZEOF(UCHAR_T), before_fan_power_off_state);
	if (opRet != FAN_STATE_STORAGE_LEN) {
		PR_ERR("read data error for Flash");
		return;
	}

	//判断头部数据是否正确
	if (before_fan_power_off_state[0] != FAN_DATA_HEAD) {
		PR_ERR("data head error");
		return;
	}

	fan_state_data_crc = get_crc_8(before_fan_power_off_state, (FAN_STATE_STORAGE_LEN - 1)*SIZEOF(UCHAR_T));
	//校验数据是否正确
	if (fan_state_data_crc != before_fan_power_off_state[FAN_STATE_STORAGE_LEN - 1]) {
		PR_ERR("crc error, before_fan_power_off_state[%d] = %02x, crc data = %02x.", FAN_STATE_STORAGE_LEN - 1, before_fan_power_off_state[FAN_STATE_STORAGE_LEN - 1], fan_state_data_crc);
		return;
	}

	//将从 Flash 读取到的数据,存放到结构体中
	fan_state.on_off    = before_fan_power_off_state[FLASH_FAN_STATE_ON_OFF];
	fan_state.mode      = before_fan_power_off_state[FLASH_FAN_STATE_MODE];
	fan_state.speed     = before_fan_power_off_state[FLASH_FAN_STATE_SPEED];
	fan_state.local_timing = before_fan_power_off_state[FLASH_FAN_STATE_TIMING];

	return;
}

VOID_T write_flash_fan_state(VOID_T)
{
	INT_T opRet, i;
	UCHAR_T fan_state_buffer[FAN_STATE_STORAGE_LEN];

	fan_state_buffer[0] = FAN_DATA_HEAD;
	fan_state_buffer[1] = fan_state.on_off;
	fan_state_buffer[2] = fan_state.mode;
	fan_state_buffer[3] = fan_state.speed;
	fan_state_buffer[4] = fan_state.local_timing;
	fan_state_buffer[5] = get_crc_8(fan_state_buffer, (FAN_STATE_STORAGE_LEN - 1)*SIZEOF(UCHAR_T));

	for (i=0; i<FAN_STATE_STORAGE_LEN; i++) {
		PR_NOTICE(" +++ fan_state_buffer is [%d] : %02x", i, fan_state_buffer[i]);
	}

	opRet = opSocFlashWrite(SAVE_TYP1, FAN_STATE_OFFSET, fan_state_buffer, FAN_STATE_STORAGE_LEN * SIZEOF(UCHAR_T));
	if (opRet != LIGHT_OK) {
		PR_ERR("write Flash error");
	}

	return;
}

6:风扇恢复初始设置

设备恢复初始设置又称复位功能。该功能可以通过将 Flash 中存储的设备状态更改为初始化值,或者直接擦除 Flash 中的信息来实现。

VOID_T erase_flash_fan_state(VOID_T)
{
	INT_T opRet, i;
	UCHAR_T fan_state_buffer[FAN_STATE_STORAGE_LEN];

	fan_state.on_off = FALSE;
	fan_state.mode   = NORMAL_MODE;
	fan_state.speed  = 1;

	fan_state_buffer[0] = FAN_DATA_HEAD;
	fan_state_buffer[1] = FALSE;    //fan_state.on_off
	fan_state_buffer[2] = NORMAL_MODE; //fan_state.mode
	fan_state_buffer[3] = 1;        //fan_state.speed
	fan_state_buffer[4] = 0xFF;     // fan_state.local_timing
	fan_state_buffer[5] = get_crc_8(fan_state_buffer, (FAN_STATE_STORAGE_LEN - 1)*SIZEOF(UCHAR_T));

	for (i=0; i<FAN_STATE_STORAGE_LEN; i++) {
		PR_NOTICE(" +++ fan_state_buffer is [%d] : %02x", i, fan_state_buffer[i]);
	}

	opRet = opSocFlashWrite(SAVE_TYP1, FAN_STATE_OFFSET, fan_state_buffer, FAN_STATE_STORAGE_LEN * SIZEOF(UCHAR_T));
	if (opRet != LIGHT_OK) {
		PR_ERR("write Flash error");
	}

	return;
}

7:改变风扇工作状态

当用户通过 App 改变风扇状态,例如从自然风模式切换成睡眠风模式,可以调用该函数实现,当然使用按键也可以控制设备。

VOID_T change_fan_state(VOID_T)
{
	if (fan_state.on_off == FALSE) {
		fan_turn_off();
		hw_report_all_dp_status();
		PR_NOTICE("stop sleep & natural timer");
		return;
	}

	if (fan_state.bright == 1) {
		fan_led_dimmer(100);
	} else {
		fan_led_dimmer(900);
	}

	if (fan_state.mode == SLEEP_MODE) {
		PR_NOTICE("enter sleep mode");
		fan_mode_sleep();
	} else if (fan_state.mode == NATURAL_MODE) {
		PR_NOTICE("enter natural mode");
		fan_mode_natural();
	} else {
		PR_NOTICE("enter normal mode");
		fan_mode_normal();
	}

	hw_report_all_dp_status();

	/* speed LED set */
	fan_speed_led_set(get_cur_gear()+1);
	fan_mode_led_set();

	return;
}

七:手机远程控制

经过以上开发过程的风扇可以连接到 Wi-Fi 网络,实现远程控制。控制的方式可以是语音、遥控器、智能手机、微信小程序、平板电脑,或者您设计的物理按键。

您可以在 AppStore 等应用商店下载 涂鸦智能,通过扫码配网来实现设备远程控制,连接到涂鸦 IoT 云。

有关其他控制方式,请参考 App 开发

八:(可选)了解更多

在硬件设计阶段,为了方便您了解整体的操作流程,我们省略了大量的背景信息。您可以在本章节了解到更多细节,供您参考。

三相无刷直流电机的电流流向

  • 红色流向:电流从 AH 高位 MOS 流入电机的 MOT A,再从 MOT B 流出,经过 BL 低位 MOS 回到电源负端。

  • 蓝色流向:电流从 BH 高位 MOS 流入电机的 MOT B,再从 MOT C 流出,经过 CL 低位 MOS 回到电源负端。

  • 绿色流向:电流从 CH 高位 MOS 流入电机的 MOT C,再从 MOT A 流出,经过 AL 低位 MOS 回到电源负端。

FU6832S MCU 芯片与 BLDC

FU6832S 芯片有 6 个指定引脚H_PU、H_PV、H_PW、L_U、L_V、L_W 用于控制三相直流无刷电机,内置上拉电阻和下拉电阻,高电平最大值是 VCC 电压。

只要将芯片供电的输入电压和电机工作电压保持相同,就不需要再在这些引脚的外围增加电平转换电路,可直接用三颗驱动芯片 AO4606 驱动电机的 U、V、W 三个引脚,该芯片由一个 Nmos 和 Pmos 组成,通过引脚走线可设计成半桥驱动电路。

FU6832S 芯片内置一个可配置增益的独立运算放大器,外围增加取样电阻,便可用于实现电机过流检测保护。

电机正反转监测电路,通过电阻分压后,进入 FU6832S 芯片 14、15、16 脚。

因为FU6832S芯片数字电路引脚的电平是5V,Wi-Fi模块的引脚电平是3.3V,因此两个通信间需要增加电平转换电路。

通信模组 CBU

CBU 由一个高集成度的无线射频芯片 BK7231N 和少量外围器件构成,可以支持 AP 和 STA 双角色连接,并同时支持 BLE 连接,运行速度最高可到 120 MHz ,还内置2Mbyte 闪存和 256 KB RAM。它拥有的外设:PWM、UART、SPI;其中可配置6路的 32 位硬件 PWM ,非常适合高品质的 LED 控制。模组可用普通IO口有16个,也满足方案对IO口的需求。更多详情,请参考 CBU 模组规格书