基于单片机的智能百叶窗设计

系统简介

随着科技的进步,社会发展进行加快,人们对人工智能的需求日益增长。。其中智能家居与人们的生活息息相关。是时代发展必不可少的一部分。而如何把智能与传统百叶窗系统相结合就是本文的研究目的。本设计以STM32F407ZGT6单片机、ATK-HC05蓝牙模块、紫外线强度传感器、语音模块、舵机模块、电阻触摸屏为硬件,以FreeRTOS系统、蓝牙串口APP作为软件,以达到用电阻触摸屏发送指令至单片机或者用Android APP通过ATK-HC05发送指令至单片机后控制舵机模块以调节百叶窗的目的,以此来构成基于单片机的智能百叶窗设计。本款设计不仅可以使窗帘进入智能化的领域,而且可以让人们的日常生活更加方便和舒适,从而有效利用科技,更加节能,并为市场开发提供了足够的发展空间。

关键词 单片机;智能百叶窗;STM32;紫外线检测

1绪论

1.1国内外研究现状
在世界范围内,能源不足已成为不可改变的现实。根据《2015年世界能源统计年鉴》的相关数据可知,欧盟的能源消耗自1985年以来已降至新低。能源短缺,特别是严重的电力短缺是一个主要问题,而且也是一个全球性的问题。全球能源投资应达到48万亿美元,国际能源署在一份报告中说,48万亿美元才能满足未来20年的全球能源需求,其中90%的新能源需求也来自中国运营的新市场,也不断被社会所接受。当前世界能源短缺形势十分严峻,中国必须在全球节能中发挥积极作用。在现代文明的背景下,科学技术的进步和人类生活需求的改善是智能家居系统快速发展的两个主要因素。智能家庭控制是嵌入式技术飞速发展的重要产品。随着数字和模拟电子技术的全面发展,以及人类在控制算法方面的不断进步提升,相继引入了遥控智能、温度控制和光控智能控制等功能算法,从而彻底改变了智能控制技术。也让人们的生活更加方便快捷。为此,窗帘智能控制器的研究与设计具有一定的商业价值。在这一阶段,窗帘智能控制器具有四种控制方法:语音控制,远程控制,灯光控制和时间控制。前两个是半自动的,后两个是全自动的。世界上第一座智能建筑于1984年在美国建成。从那时起,欧洲、日本、加拿大、澳大利亚和其他经济发达的国家和地区一直为智能家居提供各种系统的解决方案。根据美国咨询公司PARKS的统计:1995年,美国家庭平均每人在自动化生活上的花费为8000美元,但自动化家用电器在美国家庭中的普及率仅为0.33%。这些数据充分显示了当时智能家居市场的巨大潜力。自1995年后面的5年,美国智能家居市场的年均增长率为8%。根据有关机构的统计和预测:到本年年底,国际智能家居产品的销售额将达到300亿美金。最初在2012年之前,美国通用电气和日本东芝等智能家居领域的外国公司不仅在技术上超越了国内一流的家用电器公司,而且还达到了国内公司无法匹敌的质量。但是,随着《中国制造2025》的提出以及国内企业在这一领域的努力,这种情况已逐渐改变。在被海尔,美的和其他国际家电巨头等国内领先企业收购大规模国外家用电器业务之前,一些一流的国外国家对智能家居和智能控制器进行了高级研究。在海尔,美的和其他国际家电巨头等国内领先企业大规模收购国外家电业务之前,国外一些关于智能家居和智能控制器的一流研究的先进性主要体现在:重视新技术的研究,重视传感器制造工艺的研究,注重质量管理和市场分析。
1.2本设计研究的意义
随着社会的不断发展和人们生活水平的不断提高,智能家居作为一种创新技术。在已经逐渐发展成熟。人们对生活舒适性的要求也越来越高。其中之一是需要遮阴。作为一种通用的遮阳设施,百叶窗可以调节自身叶片的角度,从而进行控制室内的太阳热量,减少系统的制冷负荷并达到节能的目的。该系统主要使用紫外线强度模块来感应房间中紫外线强度的变化,并控制减速步进电机来驱动百叶窗的每个叶片的打开和关闭角度。本文主要阐述有关单片机的控制技术,该系统主要由单片机控制模块,蓝牙模块,舵机驱动模块,电阻式触摸屏显示模块,传感器模块,语音模块,电源模块,百叶窗结构。此外,在本系统中通过蓝牙进行无线数据的接收并将数据传输至STM32F407ZGT6单片机进行解码,再输出至单片机主控制模块,带动转向器转动并控制百叶窗。
1.3本设计要求
本设计基于单片机,然后利用LCD线连接电阻触摸屏,通过电阻触摸屏选择相对应的操作,指令传输到STM32单片机,由STM32单片机根据内部的具体编程对指令进行操作并传输到相应的舵机驱动模块,并实现相应的模式。利用紫外线强度传感器采集紫外线强度,并将信息传到单片机,单片机根据紫外线强度的高低对信息进行处理,再传输到舵机模块使得百叶窗状态发生改变。利用语音模块检测人说话的指令,并且将这一信息传输到单片机,在判断指令之后,单片机指令传输到舵机模块,让百叶窗根据指令变化。利用蓝牙模块使得手机APP与单片机之间进行无线连接,人通过手机APP选择相应指令,指令通过ATK-HC05蓝牙模块传输到单片机,由单片机根据内部的具体编程对指令进行操作并传输到舵机模块,并实现相应的功能。

2智能百叶窗系统的工作过程及功能模块的选择

2.1智能百叶窗系统的工作过程
本设计基于单片机,然后通过对应接口的线材连接到电阻触摸屏,通过电阻触摸屏选择相对应的功能,系统生成对应的指令,然后由单片机根据内部的具体编程对指令进行操作并传输到相应的舵机驱动模块,并实现相应的模式。布置在阳台等位置的紫外线强度传感器同时实时采集紫外线强度,并将信息传到单片机,系统程序根据紫外线强度的高低对信息进行处理并实时展示在控制面板,以及连接系统的Android APP上。也可以根据紫外线的强度自动使得百叶窗状态发生改变。还可以通过语音模块检测人说话的指令,并且将这一信息传输到单片机,在系统判断指令之后,单片机通过控制舵机模块,让百叶窗根据指令产生对应的变化。同时还可以利用蓝牙模块使得Android APP与单片机之间进行无线连接,人通过手机APP选择对应的功能,并产生对应指令通过ATK-HC05模块传输,单片机通过串口接口与蓝牙模块连接,如果APP内部设定为手动模式,则可以手动控制窗帘的开启与关闭,如果是自动模式,则系统可自动根据设定的日出日落时间,以及紫外线强度去控制舵机模块来选择开启或关闭百叶窗帘以及开启百叶窗帘的角度。本设计研究的智能百叶窗设计的工作过程如图2-1所示。

图2-1 工作过程
2.2智能百叶窗系统的模块选择
2.2.1 S12SD紫外线传感器模块
紫外线传感器是一种P-N光子二极管。它是一种光敏传感器。这是一种光电传感器,可以使用光接收元件将紫外线信号转换为电信号。操作模式通常分为两类:光导模式和光伏模式。所谓光纤模式是指需要与电池串联工作,传感器对应一个电阻,并且电阻值随光强度而变化,易于制造且成本较低。光伏模式意味着没有串联电池,电流流过串联电阻。对应于具有低输出电压的小型电池,但制造困难且价格昂贵。所以本设计中选择第一种光导模式。
S12SD紫外线传感器模块使用基于肖特基光电二极管的肖特基光电二极管,该材料基于氮化镓,具有较高的德国响应和较低的暗电流。板载LM358放大器对光电二极管输出的信号进行放大,所有元器件采用1%精度元器件制造,其模块样式如图2-2所示。

紫外线检测模块具有240 nm至370 nm的宽检测波长范围,大角度为130度,温度漂移小,约0.08%/℃。根据185〜400nm的紫外线波长宽度,可分为185nm〜270nm UVC波段,270nm〜315nm UVB波段,315nm〜400nm UVA波段。通常,UFC被阳光中的臭氧层吸收,UVB在强光作用下会产生表皮,UVA从表皮渗透到真皮,使皮肤变黑和老化。这表明该模块可以检测到大多数有害的紫外线波长,并可以有效地提醒人们采取保护措施。
2.2.2 ATK-HC05蓝牙串口模块
ATK-HC05模块是由ALIENTEK制造,主要用于串行接口的功能强大的集成蓝牙模块,而且该模块支持多种数据速率。该模块与4800至1382400的5V或3.3V兼容。单芯片系统可以轻松连接到相关产品。它不仅灵活放门面,而且易于使用。ATK-HC05模块尺寸非常紧凑(16毫米*32毫米)。模块通过六个2.54mm的台阶从外部连接。ATK-HC05蓝牙串口模块的外观参照下图2-3。

该模块具有状态显示灯:STA。该指示灯具有三种状态:1.当模块打开时(可能以前已经打开过),将KEY设置为高电平(连接到VCC),并且STA将缓慢闪烁(每1秒钟闪烁一次)。在AT状态下,波特率设置为38,400。2.开启或按住键后,将模块连接到GND。此时,STA快速闪烁(每秒两次),模块准备配对。此时,再次按下该键时,模块处于AT状态,但是STA继续立即闪烁。3.模块已成功耦合,并且STA此时闪烁两次(两次闪烁1次,每2秒闪烁1次)。在使用STA指示器时,可以判断出当前模块的状态。
2.2.3 MG995舵机
MG995舵机的死区一般设置为4微秒,其中180度是最大的旋转角,而工作电流为100mA,工作电压是从3V至7.2V。结构材料为金属铜齿,空心转向节和双球轴承,此外,工作速度为0.17秒/60度(4.8V)或0.13秒/60度(6.0V)。
舵机主要由VCC,GND和信号线三根线组成。该控制信号通常需要一个周期为20ms的PWM信号。
VCC和GND之间需要进行连接到驱动器以为转向器供电,并且必须与开发板共享。中间始终是电源的正极。接线图如图2-4所示。

图2-4 舵机接线图
控制转向器通常需要20毫秒的时基脉冲。高电平脉冲部分是角度调节脉冲的一部分,范围为0.5到2.5ms。用180度角舵机作为例子,合适的控制比:0.5ms为0度。1.0毫秒为45度。1.5毫秒为90度。2.0毫秒是135度。2.5毫秒为180度。下图2-5显示了MG995舵机的控制原理。

图2-5 MG995舵机控制原理
2.2.4 LD3320语音识别模块
LD3320是基于SI-ASR独立自动语音识别技术的一种语音识别/语音控制芯片。LD3320集成了高精度A/D和D/A接口,不需要外部支持,例如闪存或RAM。这样,就可以实现语音识别、语音控制和人机对话的功能。还可以动态编辑可识别的关键字列表。基于LD3320语音模块,不仅可以轻松地在所有电子产品中实现设备的语音识别,语音控制和机器对话功能,还可以在所有电子产品中添加语音用户界面
主要功能包括以下7种类型:1、未指定的人类语音识别技术未指定的人类语音识别技术:无需进行用户培训即可进行录音;2、可以动态编辑可识别关键字的列表,也可以被动态编辑,关键在于识别关键字列表单;3、将字符串发送到芯片,它将在下一次识别后立即生效;4、真正的芯片解决方案:无需额外的闪存和RAM即可降低系统成本;5、内置高精度A/D和D/A通道:无需外部AD芯片,只需将麦克风连接到AD芯片引脚即可播放相应的音频文件,内置550mW-配备放大器;6、高精度,实用的语音识别效果;7、帮助用户一起编辑最多50个关键字的50个关键字。最终用户可以根据场景的需要,随时编辑和更新这50个关键字的内容。
2.2.5 TFTLCD
TFT-LCD是带有薄膜的液晶显示器。英文名称是Thin Film Transistor-Liquid Crystal Display。TFT-LCD与无源TN-LCD和STN-LCD的简单矩阵不同。在TFT-LCD液晶显示器中,每个像素都有一个薄膜晶体管(TFT),利用该晶体管可以在非频闪期间有效地克服交叉结果。由于静态属性的数量与LCD屏幕的扫描线无关,因此可以提高图像质量。TFT-LCD也称为真彩色液晶显示器。该模块具有以下属性:可以通过触摸屏用作控制输入,16位真彩色显示。2.8英寸模块支持65K彩色显示屏,具有80个并行连接的16位接口,320×240的屏幕分辨率和触摸屏。
电阻式触摸屏是通过压力传感器来控制和识别。由于只需要直接进行接触,因此可以通过感受到阻力来找到接触的地方。电阻式触摸屏的核心是电阻式屏幕,这使其成为非常合适的显示区域,这是一种多层复合膜。硬塑料或玻璃层用作主层,表面覆盖有由金属氧化物制成的透明导电层(透明导电电阻器)。导电层是覆盖有硬化外表面的塑料层,并且耐刮擦且光滑。内表面也有涂层,并有许多透明的,小的(<1000cm)分离点,将两个导电层分开。当手指触摸屏幕时,两个导电层进入接触点。此时,将产生电阻变化以及X和Y方向上的信号,然后将其发送到触摸屏控制器。此时,控制器检测到触摸,计算位置(X,Y),然后根据获得的位置模拟鼠标的移动,这是电阻式触摸屏的最基本原理。电阻式触摸屏的优点:高精度,低成本,良好的干扰功能和高稳定性。

3 智能百叶窗系统的硬件设计

3.1智能百叶窗系统的电路
3.1.1 STM32F407ZGT6单片机
对于STM32F4而言,其作为智能百叶窗系统的微控制器,发挥着十分重要的作用,其内核设计功能较为全面,设计较为先进。其内核选择CortexM4进行设计,资源丰富,同时包含有FPU指令集以及DSP指令集,功能较为全面。同时,对于STM32F4而言,其包含有SRAM,速度最高可大192kb,且具有DCMI、储存器、高速On-The-Go以及处理器等,对该系统的外围功能进行进一步的增强。在对一些外围部件的使用中,该单片机能够使得系统模块具有较高的转换速度,包含有32位定时器,较低的工作电压以及较高的IO复用功能。对于该单片机而言,其具有较高的工作频率,最高可达168兆赫兹,同时,通过自身具备的实时加速器,大大缩短了系统运行的时间,在一定程度上提高了系统的运行效率,降低了能源消耗,一般而言,该单片机能源工作效率每兆赫兹损耗238微安电能,具有较好的节能效果。对于STM32F4芯片而言,其包含有DSP以及FPU指令,其16位定时器、32位定时器、DNA控制器、串行端口、CNA、SDIO接口、摄像头接口、通用IO端口、IIC、12位DAC、RTC、全双工12S以及FSMC接口数量分别为12个、2个、2个、6个、2个、1个、1个、112个、3个、2个、1个、2个以及1个,功能较为丰富全面。

图3-1 STM32F407ZGT6

3.2 通信协议的制定
对于通信协议而言,其主要代表着一种约定与规则,要求协议主体必须进行遵守,进而实现通信服务。同时,对于数据通信系统而言,其主要通过设备和通道实现多个系统的互联,进而实现资源、信息等内容的交流以及共享,因此,数据通信系统需要制定相应的通用语言,能够促使多个使用主体能够按照既定的规则进行运用,进而形成了通信协议。在该系统中,Android APP需要通过HC05 蓝牙模块向单片机发送指令,之后单片机才可以相应地控制舵机模块。我们制定通信协议的意义就在于Android APP和单片机可以基于同样的协议规定从而使得Android APP发送的相关数据能够被单片机正确读取。本设计制定的协议具体命令如表3-2所示。
表3-2 蓝牙协议具体命令
指令序号 指令 指令功能 说明
指令1 0X0A 蓝牙模式 把语音模式切换为蓝牙模式
指令2 0X0B 语音模式 把蓝牙模式切换为语音模式
指令3 0X0C 打开百叶窗 控制舵机打开百叶窗(蓝牙模式下有效)
指令4 0X0D 关闭百叶窗 控制舵机关闭百叶窗(蓝牙模式下有效)

原理图

4智能百叶窗系统的软件设计

4.1 FreeRTOS系统
对于FreeRTOS系统而言,其主要涉及两部分内容,第一部分为Free,代表着一种自由,无规则。第二部分为RTOS系统,对于RTOS而言,又可称其为实时操作系统,其系统能够免费使用,操作方便,应用较为广泛。
该操作系统允许多个任务同时运行,这称为多任务处理。实际上,对于处理器内核而言,其仅能根据一个任务进行执行,相互对应。在该实时操作系统中,对于任务计划程序而言,其主要作用在于对任务执行的具体时间顺序进行决定,并且任务计划可以在每个任务之间快速切换!这给人一种幻觉,即同时运行多个任务。操作系统的分类可以由任务计划程序的工作模式确定。对于RTOS系统而言,其任务计划程序设计具有可预测性的特点,这一特点满足了实时操作系统的相关需求。在系统运行的过程中,操作系统需根据运行过程中碰到的事件即时响应,也正因为如此,RTOS系统的任务计划程序设计需能够进行预测,满足系统所需。对于FreeRTOS系统而言,其与实时操作系统较为相似,能够实现对任务的优先分配,对任务的具体执行时间进行决定。FreeRTOS系统能够进行定制,体积小,功能丰富全面,应用较为广泛。其功能主要涉及三方面内容:一是SafeRTOS来自于freertos;二是具有协作式以及调度时间片的功能;三是其内核能够对抢占式进行支持。相比于FreeRTOS,对于SafeRTOS而言,其代码方面具有更优的完整性特点,具有较低的能源损耗,处于无跳动模式。同时,在该系统器件构建初期,能够对RAM的状态进行选择,确定其状态为动态或者静态的模式,如信号量、任务等器件组成。FreeRTOS系统简单,体积小且易于使用,一般而言,其内核空间处于4k至9k字节的范围内,具有较强的可移植性,并通过C语言实现对系统代码的编写,能够对通信协议进行支持。
对于FreeRTOS系统而言,其任务的状态可分为四种,分别是运行状态、就绪状态、暂停状态以及阻塞状态。当FreeRTOS系统任务处于运行状态时,代表着任务正在运行,对系统中的处理器进行占用,但需要注意的是,若为单核处理器时,便代表着系统的运行任务仅仅为一个。当系统任务处于就绪状态时,便代表着系统处于准备完成阶段,究其原因,主要在于系统优先任务处于运行的状态。当系统任务处于阻塞状态时,便代表着系统存在一些外部事件,如通过对系统的阻塞功能进行调用,便代表着系统任务处于阻塞状态,直至该阶段状态结束为止。当系统任务处于暂停状态时,和系统任务处于阻塞状态较为相似,此时,系统无法通过调用,使系统任务处于运行的状态,但与之不同的时,阻塞状态超过某一时间后,便会退出相应的阻塞状态,而暂停状态并不会因时间的变化而发生变化,只能通过功能挂起或任务调用来执行任务。
4.2智能百叶窗系统的开发环境
在该智能百叶窗系统开发的过程中,选择μVision5作为该系统的开发环境,其由KELL公司进行研发,兼容性较好,具有一定的代码编辑、工程管理以及仿真模拟等特点,应用较为广泛。相比与其他类开发软件,μVision5具有更优的性能以及编程能力。在该系统设计中,主要选择C语言进行系统的编程,同时通过对系统内部设计程序进行注释,提高了程序编程的理解度。
4.3 软件开发和程序运行流程
在该软件开发的过程中,主要涉及三大流程:
第一流程:在该软件开发时,主要选择μVision5为系统提供良好的开发环境,首先,需在磁盘中完成下列步骤:
(1)对工程涉及到的目录进行建造,并将其命名为“FreeRTOS”。
(2)在第一步骤中控制器目录文件内,建立新的四个文件夹,主要用于对工程文件的储存,可将这四个文件命名为stm32_fwlib、core、obj以及core。
(3)通过ST官网对固件库包进行相应的下载,版本选择为3.5.0,并在该固件库包中,选择SRC以及INC这两个文件夹并将其复制至第二流程中的stm32_fwlib文件夹内。
(4)在该固件库包中,选择core_cm4 以及 core_cm4.h这两个文件夹并将其复制至第二流程中的core文件夹内。之后,再选择ARM文件夹,将其中涉及到的S驱动相关文件复制至core文件夹内。
(5)在固件库包中,选择System_stm32f4xx、Stm32f4xx.h 以及System_stm32f4xx.h复制至user文件夹内。
(6)在 3.5.0 版本固件库包中找到 main、stm32f4xx_conf.h、stm32f4xx_it、stm32f4xx_it.h、system_stm32f4xx,将其拷贝到第二步中建立的 USER 文件夹中。
第二流程:在完成上述第一流程中的六步骤之后,选择μVision5开发软件完成下列步骤:
(1)点击μVision5软件,在其页面内选择project,在其内建立一个新的工程,并在第一流程中包含的第二步骤中的user目录下设置新的freertos文件。
(2)完成上述步骤之后,便会显现‘Target 1’界面,在该界面中对芯片进行选择。
(3)在第二步骤中,建立了‘Target 1’界面,我们在其中找到相应的文件夹,点击右鼠标,对manage components进行选择,便会显出Component,Environment and Books 界面,同时,共包含有3个小栏目,第一个栏为project targets,将其命名为freertos,第二个栏为groups,需要对其进行相应的分组,共涉及core、user以及felib这三组,对于obj而言,并未建立相应的文件夹,究其原因,主要在于其作用便是对结果以及编译等文件进行存储,因此并不需要对其建立相应的文件夹。
(4)在该系统进行设计编译时,需提前对 options for target‘template’进行选择,并对output内的select folder for objects进行点击选择,进而实现对编译以及结果等文件的存储,将其存至obj文件夹内。
(5)对options for target ‘template’进行点击,并对define进行相应的编程定义,此外,需将IncludePaths内的文件复制至工程中内。
(6)通过上述步骤,实现对.hex文件的编译,通过下载,选择储存目录为开发板,进而实现对μVision5软件工程的运用。
第三流程:在完成上述第一流程以及第二流程后,需要对系统程序进行相应的开发,其程序流程图如下所示:

图4-1 程序总体流程图
4.3电路设计程序

从上图中可以得知,在系统上位机内部,应设置有相应的程序烧录软件,便于对编译完成后的系统程序进行烧写,一般该过程通过usb串口电路完成。同时,利用mcuips能够实现对电路中含有的RTS以及DTR引脚的电平的控制,通过电平高低的设置,进而实现对启动模式的控制。
4.4 蓝牙模块程序设计
对于蓝牙模块底层而言,在该文中,并不需要对其进行相应的设计,仅需要为其提供接口即可,从下图蓝牙模块程序控制流程图我们可以得知,当借助控制器对蓝牙模块提供电源时,此时,对于蓝牙模块而言,其能够对外界的信号进行相应的接收识别,进而实现数据的传递运输。对于蓝牙模块以及控制器而言,两者之间实现数据的传递运输主要依靠于RXD以及TXD接口,借助TXD以及RXD接口便能够实现对窗帘开合的控制,具体的程序工作流程从下图可以得知。

图4-4 蓝牙模块程序控制流程
4.5紫外线强度采集模块程序设计
对于紫外线强度采集模块而言,其在该智能窗帘控制系统中,主要起着对紫外线强度信号的采集输入功能,通过该模块实现对紫外线的采集,进而将采集的紫外线信号进行转换,不同的强度代表着不同的变量,并将变量和控制量两者进行进行相应的对比,便于下一步操作。一般而言,在系统对紫外线进行采集时,主要依靠于ADC模块完成,整个过程主要涉及ADC转换函数以及初始化函数。
4.5.1 对ADC初始化函数进行设计
在该系统设计中,主要通过寄存器对ADC初始化函数进行相应的设置,究其原因,主要在于gpioa口与ADC信号采集口之间具有相应的复用联系,因此,对ADC进行初始化操作时,需提前对gpioa口进行相应的初始化操作。在该系统设计中,在对主函数进行调用时,需提前对myADC_Init()函数进行调用,进而实现对引脚、gpiob口、gpioa口等相应的定义。如下所示为myADC_Init()函数的相关代码内容。

对于GPIO而言,其配置工作主要在于对GPIO_InitTypeDef 结构体的相关配置,鉴于此,应该对该数据结构体有相应的掌握与了解。对于GPIO_InitTypeDef 结构体而言,其主要包含有三部分组成:

  1. 对于GPIO_Pin而言,其主要代表着对引脚的详细设置;
    2)对于GPIO_Speed而言,其主要由枚举体构成,对其速度进行相应的叙述,有三个选项(GPIO_Speed_10MHz = 1,GPIO_Speed_2MHz, GPIO_Speed_50MHz);
  2. GPIO_Mode 由枚举体组成,用于描述 GPIO 状态。
    对于GPIO_Mode而言,其相关配置在GPIO中占据着十分重要的角色,同时,STM32为其相关配置提供了较多可能。对于GPIO_Mode_AIN而言,其主要实现了对模数的转化,对于GPIO_Mode_IN_FLOATING而言,其主要作用在于对高阻态进行相应的输入,对于GPIO_Mode_IPD而言,主要作用在于对输入进行下拉,对于GPIO_Mode_UP而言,其主要作用在于对输入进行上拉,对于GPIO_Mode_AF_OD以及GPIO_Mode_AF_PP而言,其主要作用在于能够作为复用模式进行相应的输出,对于GPIO_Mode_Out_OD而言,其主要作用是开漏输出,对于GPIO_Mode_Out_PP而言,其主要作用在于对输出进行推挽。
    在该系统设计中,对于GPIO_Mode的相关配置,主要对GPIO_Mode_OUT_PP进行选择,作为PWM的输出引脚,对于其它引脚的相关配置,主要选择GPIO_Mode_UP进行运用。最终,调用GPIO_Init(GPIO_TypeDefGPIOx, GPIO_InitTypeDefGPIO_InitStruct)函数进行初始化,具体的使用方法如下图所示:对于第一个参数而言,其代表着GPIO引脚,对于第二个参数而言,其代表着gpio结构体涉及的指针地址,如 GPIO_Init(GPIOA,&GPIO_InitStructure)。如下图4-5所示为 ADC 初始化流程示意图。

4.5.2 ADC 转换函数设计
对于ADC转换函数而言,其入口参数ch代表着通道号通过硬件连接示意图我们可以得知,通过对ADC转换函数的相关设计,实现对紫外线强度的采集和信号转换,进而完成智能窗帘系统手动或自动闭合状态的控制。以下内容为Get_Adc(u8 ch)函数代码的相关设计。
u16 Get_Adc(u8 ch)
{
//设置转换序列
ADC1->SQR3&=0XFFFFFFE0;//规则序列 1 通道 ch
ADC1->SQR3|=ch;
ADC1->CR2|=1<<22; //启动规则装换通道
while(!(ADC1->SR&1<<1)); //等待转换结束
return ADC1->DR; //返回 adc 值
}

 **单片机程序**
#include "main.h"//一些运用到的头文件

/*======================变量========================*/
u32 run_time=0;//开机时间计算
u8 run_time_flag=0;
int lcd_discolor[14]={WHITE,BLACK,BLUE,BRED,      
											GRED,GBLUE,RED,MAGENTA,       	 
											GREEN,CYAN,YELLOW,BROWN, 			
											BRRED,GRAY};//LCD刷屏时使用的颜色
u8 manu_index = 3; 		//界面索引  会被清零
u8 read_manu=5;		 		//界面索引  不会被清零
u8 m_flag=0;					//界面计数
u8 mode=1;						//1:手动模式  2:自动模式
u8 state=1;						//1:开        2:关
u8 shine=1;						//1:Normal    2:PWM
u8 normal_val=100;		//PWM正常值
u16 shine_x=200;
u16 tarr=840,tpsc=10;
RTC_TimeTypeDef RTC_TimeStruct;	//时间
RTC_DateTypeDef RTC_DateStruct;	//日期								
u8 occupy=1;					//占用说明,为了解决颜色跳变问题
u8 m8_offset=0;				//时间设置界面三角形偏移量
SaveTime svtime;											
/*==================================================*/
					
/*定时器14中断服务函数 100us中断*/
u16 pwm = 99;u8 b1_flag = 0;
u16 blink1_count = 0;
u8 count_down=5;
void TIM8_TRG_COM_TIM14_IRQHandler(void)
{
	static u16 count = 0;
	static u16 run_time_count=0;
	if(TIM_GetITStatus(TIM14,TIM_IT_Update)==SET) //溢出中断
	{
		
		/*===============================
			功能:PWM调节LCD背光
		  参数:1.pwm:设置占空比
				    2.count:计数
		===============================*/
		count++;
		if(count < pwm)LCD_LED=1;	
		else if(count < 100)LCD_LED=0;
		else count = 0;
		
		/*===============================
			功能:实现某个字段的间隔闪烁功能
		  参数:1.blink1_count:计数
					  2.b1_flag:闪烁标志位
		===============================*/
		switch(read_manu)
		{
			case 1:
			{
				if(blink1_count++ > 5000)//界面1的字幕 500ms闪烁 
				{
					blink1_count = 0;
					//标志位取反 以达到闪烁功能
					if(b1_flag == 1)b1_flag = 0;else if(b1_flag == 0) b1_flag = 1;
					if(b1_flag == 1){POINT_COLOR = BLUE;LCD_Chinese(80,145,24,(u8 *)"4602");}
					else if(b1_flag == 0){LCD_Fill(80,145,80+24*4,145+24,BLACK);}
				}
			}break;
			case 2:
			{
				if(m_flag==1)//如果倒计时被激活
				if(blink1_count++>10000)//1S
				{
					blink1_count=0;
					if(count_down>0)count_down-=1;
					POINT_COLOR = BLUE;LCD_ShowNum(45+8*12,230,count_down,1,16);
					if(count_down==0){m_flag=0;manu_index=3;}//倒计时结束都进入界面3
				}
			}break;
		}	
		
		//STM32运行时间统计
		if(run_time_flag==1){run_time_count++;if(run_time_count>10000){run_time_count=0;run_time++;}}		
	}TIM_ClearITPendingBit(TIM14,TIM_IT_Update);  //清除中断标志位
}

/*主函数*/					
int main(void)
{ 
	MYInit();
	
	//创建开始任务
	xTaskCreate((TaskFunction_t )start_task,            //任务函数
							(const char*    )"start_task",          //任务名称
							(uint16_t       )START_STK_SIZE,        //任务堆栈大小
							(void*          )NULL,                  //传递给任务函数的参数
							(UBaseType_t    )START_TASK_PRIO,       //任务优先级
							(TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
	vTaskStartScheduler();          //开启任务调度
}

/*开始任务任务函数  开始任务创建 其余任务*/
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
    //创建TASK1任务
    xTaskCreate((TaskFunction_t )task1_task,             
                (const char*    )"task1_task",           
                (uint16_t       )TASK1_STK_SIZE,        
                (void*          )NULL,                  
                (UBaseType_t    )TASK1_TASK_PRIO,        
                (TaskHandle_t*  )&Task1Task_Handler);   
    //创建TASK2任务
    xTaskCreate((TaskFunction_t )task2_task,     
                (const char*    )"task2_task",   
                (uint16_t       )TASK2_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK2_TASK_PRIO,
                (TaskHandle_t*  )&Task2Task_Handler); 
	//创建RunTimeStats任务
	xTaskCreate((TaskFunction_t )task3_task,     
                (const char*    )"task3_task",   
                (uint16_t       )TASK3_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK3_TASK_PRIO,
                (TaskHandle_t*  )&Task3Task_Handler); 
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}


/*主要任务函数,负责屏幕刷新、触碰按键处理等任务*/
u8 time_display_flag=0;
void task1_task(void *pvParameters)
{	
	
	//界面刷新 函数
	while(1)
	{
		/*若处于界面2 则倒计时开始 5S后自动进入界面3*/if(read_manu==2){m_flag=1;}else m_flag=0;
		
		if(manu_index)
		{
				TIM_Cmd(TIM14,DISABLE); //失能定时器14 防止定时器中断打扰到界面切换
				occupy = 0;//在页面切换过程其他界面操作程序不允许打扰
				switch(manu_index)
				{
					/*界面1:提示开机界面*/										case 1:DisplayInterface_1();break;
					
					/*界面2:5S后自动跳转进入系统*/						case 2:count_down=5;DisplayInterface_2();break;
					
					/*界面3:应用界面 应用程序界面*/					case 3:DisplayInterface_3();break;
					
					/*界面4:进入设置界面*/										case 4:DisplayInterface_4();break;
					
					/*界面5:查看时间*/												case 5:DisplayInterface_5();break;
					
					/*界面6:查看项目名称以及单片机一些参数*/ case 6:DisplayInterface_6();break;
					
					/*界面7:亮度调节界面*/										case 7:DisplayInterface_7();break;
					
					/*界面8:时间调节界面*/										case 8:DisplayInterface_8();break;
					
					/*界面9:电源阈值设置*/										case 9:DisplayInterface_9();break;
				}

				/*固定显示小数点  CPU周围温度以及AD引脚采集的信息*/
				POINT_COLOR = RED;BACK_COLOR = BLACK;
				LCD_ShowString(122,249,96,16,16,(u8*)"POWER:  0.0%");
				 LCD_ShowString(138,267,200,16,16,(u8*)"CPU: 0.00C");
				 LCD_ShowString(138,285,200,16,16,(u8*)"CH5: 0.00V");
				
				//读取当前屏幕值
				switch(manu_index)
				{	
					case 1:read_manu=1;break;
					case 2:read_manu=2;break;
					case 3:read_manu=3;run_time_flag=1;break;
					case 4:read_manu=4;break;
					case 5:read_manu=5;break;
					case 6:read_manu=6;break;
					case 7:read_manu=7;break;
					case 8:read_manu=8;break;
					case 9:read_manu=9;break;
				}manu_index = 0;//清零 防止反复刷新 
				
				if(read_manu==5){time_display_flag=1;}else {time_display_flag=0;}//时间仅在界面5显示
				
				occupy=1;//在页面切换过程其他界面操作程序不允许打扰
				TIM_Cmd(TIM14,ENABLE); //使能定时器14
			}
		
			u8 return_val;//键值
			switch(read_manu)//不同的界面 不同的按键处理
			{
				case 1:if(rtp_test(1)==M1P1){manu_index=2;}break;//若 "触碰开机" 被按下,则进入界面2
				case 3:
						return_val=rtp_test(3);
						switch(return_val)
						{
							case M3P_Set :manu_index=4;break;
							case M3P_Time:manu_index=5;break;
							case M3P_Para:manu_index=6;break;
							case M3P_AutoOrHand:manu_index=3;if(mode==1)mode=2;else mode=1;break;//模式选择
							case M3P_OpenOrClose:if(mode==1){manu_index=3;if(state==1)state=2;else state=1;}break;
							case M3P_NormalOrPWM:manu_index=3;if(shine==1)shine=2;else shine=1;break;
							case M3_To_Off:manu_index=1;break;
						}break;
				case 4:return_val=rtp_test(4);
							 switch(return_val)
							 {
								 case M456Back3:manu_index=3;		break;
							   case M4P_SetShine:manu_index=7;break;
								 case M4_SetTime:/*若设置时间被按下,则保存当前时间以及 进入时间设置界面*/
								 svtime.syear=RTC_DateStruct.RTC_Year;svtime.smon=RTC_DateStruct.RTC_Month;svtime.sdate=RTC_DateStruct.RTC_Date;
								 svtime.shour=RTC_TimeStruct.RTC_Hours;svtime.smin=RTC_TimeStruct.RTC_Minutes;svtime.ssec=RTC_TimeStruct.RTC_Seconds;
								 svtime.sweek=RTC_DateStruct.RTC_WeekDay;manu_index=8;break;
								 case M4P_Set_Power:manu_index=9;break;
							 }break;
				case 5:if(rtp_test(5)== M456Back3){manu_index=3;}break;
				case 6:if(rtp_test(6)== M456Back3){manu_index=3;}break;
				case 7:
							return_val=rtp_test(7);
							switch(return_val)
							{
								case M7P_SetShine:manu_index=7;normal_val=shine_x/2;break;
								case M7P_Sure:manu_index=4;break;
							}break;
				case 8:
						return_val=rtp_test(8);//键值读取
						//选择修改的内容
						if(return_val==M8_Year || return_val==M8_Mon || return_val==M8_Data\
							|| return_val==M8_Hour || return_val==M8_Min || return_val==M8_Sec || return_val==M8_Week){manu_index=8;}
						switch(return_val)
						{
							case M8_SaveTime:manu_index=4;m8_offset=0;
									 RTC_Set_Time(svtime.shour,svtime.smin,svtime.ssec,RTC_H12_AM);
									 RTC_Set_Date(svtime.syear,svtime.smon,svtime.sdate,svtime.sweek);
								break;
							case M8_Count_plus://时间以及日期调整:数值加
								switch(m8_offset)
								{
									case 0:if(svtime.syear<99)svtime.syear++;break;
									case 1:if(svtime.smon<12)svtime.smon++;break;
									case 2:if(svtime.sdate<31)svtime.sdate++;break;
									case 3:if(svtime.shour<23)svtime.shour++;break;
									case 4:if(svtime.smin<59)svtime.smin++;break;
									case 5:if(svtime.ssec<59)svtime.ssec++;break;
									case 6:if(svtime.sweek<7)svtime.sweek++;break;
								}manu_index=8;break;
							case M8_Count_sub://时间以及日期调整:数值减
								switch(m8_offset)
								{
									case 0:if(svtime.syear>0)svtime.syear--;break;
									case 1:if(svtime.smon>1)svtime.smon--;break;
									case 2:if(svtime.sdate>1)svtime.sdate--;break;
									case 3:if(svtime.shour>0)svtime.shour--;break;
									case 4:if(svtime.smin>0)svtime.smin--;break;
									case 5:if(svtime.ssec>0)svtime.ssec--;break;
									case 6:if(svtime.sweek>1)svtime.sweek--;break;
								}manu_index=8;break;
							case M8_Year:m8_offset=0;break;case M8_Mon:m8_offset=1;break;case M8_Data:m8_offset=2;break;
							case M8_Hour:m8_offset=3;break;case M8_Min:m8_offset=4;break;case M8_Sec:m8_offset=5;break;
							case M8_Week:m8_offset=6;break;
						}
					break;
			}vTaskDelay(20);
	}
}

//task2:KEY0 按键操作函数 
void task2_task(void *pvParameters)
{
	u8 key; 
	while(1)
	{
		key = KEY_Scan(0);
		if(key == KEY0_PRES)
		{
			switch(read_manu)
			{
				case 1:manu_index=2;break;
				case 2:manu_index=3;break;
				case 3:manu_index=1;break;
			}
		}vTaskDelay(20);
	}
}

//task3:ADC值获取 以及获取时间
void task3_task(void *pvParameters)
{ 
	short temp,volt,power;//power用于获取电源的电压,处理后以百分比显示出来
	u8 tbuf[40];
	while(1)
	{
		temp=Get_Temprate();//获取CPU周围温度值
		volt=Get_Volt();		//获取PA5的电压值	 预留光照传感器接口
		power=Get_Power();	//获取PA4的电压值  预留电量查看口 
		
		//LCD 呼吸效果是否打开 
		if(shine==2){pwm=volt/3+20;}
		else {pwm=normal_val;}
			
		if(occupy==1)//解决:画笔颜色乱串bug
		{
				/*时间以及日期显示*/
				POINT_COLOR = RED;
				RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct);
				sprintf((char*)tbuf,"Data:20%02d-%02d-%02d",\
				RTC_DateStruct.RTC_Year,RTC_DateStruct.RTC_Month,RTC_DateStruct.RTC_Date);
				if(read_manu==5 && time_display_flag==1 && manu_index==0){
				BACK_COLOR = YELLOW;POINT_COLOR = BLUE;
				LCD_ShowString(20,40,210,16,24,(u8*)tbuf);}
				
				sprintf((char *)tbuf,"Week:%02d",RTC_DateStruct.RTC_WeekDay);
				if(read_manu==5 && time_display_flag==1 && manu_index==0){
				BACK_COLOR = YELLOW;POINT_COLOR = BLUE;
				LCD_ShowString(20,74,210,16,24,(u8*)tbuf);}
				
				RTC_GetTime(RTC_Format_BIN,&RTC_TimeStruct);
				sprintf((char*)tbuf,"Time:%02d:%02d:%02d",\
				RTC_TimeStruct.RTC_Hours,RTC_TimeStruct.RTC_Minutes,RTC_TimeStruct.RTC_Seconds);
				if(read_manu==5 && time_display_flag==1 && manu_index==0){
				BACK_COLOR = YELLOW;POINT_COLOR = BLUE;
				LCD_ShowString(20,108,210,16,24,(u8*)tbuf);} 

				sprintf((char*)tbuf,"Run Time:%ds",run_time);
				if(read_manu==5 && time_display_flag==1 && manu_index==0){
				BACK_COLOR = YELLOW;POINT_COLOR = BLUE;
				LCD_ShowString(20,142,210,16,24,(u8*)tbuf);} 

				/*固定显示部分*/
				if(manu_index==0)
				{
					POINT_COLOR = RED;BACK_COLOR = BLACK;	//bug:在变量被清零后才改变画笔颜色
					LCD_ShowxNum(170,267,temp/100,2,16,0);	//显示整数部分
					LCD_ShowxNum(194,267,temp/10%10,1,16,0);//显示小数部分
					LCD_ShowxNum(202,267,temp%10,1,16,0);		//显示小数部分 
					
					LCD_ShowxNum(178,285,volt/100,1,16,0);		//显示整数部分
					LCD_ShowxNum(194,285,volt/10%10,1,16,0);		//显示小数部分
					LCD_ShowxNum(202,285,volt%10,1,16,0);		//显示小数部分
				}
		}
		vTaskDelay(250);
	}
}

void MYInit(void)		//初始化函数
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	delay_init(168);		//初始化延时函数
	uart_init(115200);  //初始化串口
	LED_Init();		      //初始化LED端口
	KEY_Init();			    //初始化按键
	LCD_Init();					//初始化LCD
	tp_dev.init();			//触摸屏初始化
	myADC_Init();				//初始化ADC
	TIM14_Int_Init(tarr-1,tpsc-1);	//100us 定时器中断函数
	TIM9_PWM_Init(200-1,8400-1);
	while(My_RTC_Init());	//返回0则表示初始化成功
	TIM_SetCompare1(TIM9,100);
}

5 设计实物与调试结果

5.1 设计实物的制作
对于该系统实物设计,如下图所示。

图5-1 成品

图5-2 成品——APP界面功能选择

图5-3 成品——成功连接蓝牙界面

图5-4 成品——电阻触摸屏待机界面

5.2 实物的调试结果
调试方法如下:
(1)首先对整个系统模型进行供电
(2)然后点击Android APP图标进入APP
(3)点击蓝牙,连接蓝牙
(4)返回选择页面,点击【蓝牙模式】按键进入蓝牙控制开关
(5)分别点击开关看开窗关窗是否正常
(6)返回选择页面,点击【语音模式】按键进入语音模式
(7)说“小杰,开窗”和“小杰,关窗”看是否正常
(8)调试过程中,发现语音模块语音识别度不够高,经过试验发现是供电不足以及外界杂音干扰
(9)开启电阻触摸屏,点击【触摸开机】按键进入主菜单
(10)分别点击不同的功能来测试电阻触摸屏是否正常
(11)在测试界面时,发现界面刷新不够完善,在切换界面时,常常会有一小部分屏幕没有刷新,经过调试发现是FreeRTOS的任务切换或者是定时器中断影响到屏幕切换,所以加入了一个变量,在屏幕切换的时候,改变量可以屏蔽这些干扰
(12)电阻触摸屏调试过程中,LCD和数码管有一个迥然不同的地方,就是LCD的显示不应该不断的刷新,否则不能正常看到我们需要显示的图样和文字,于是把屏幕刷新和屏幕切换分隔开来,加入了2个变量,一个变量用于检测有没有屏幕需要更新,如果有,该变量将会被赋成某个值,界面切换成功后,变量会清零,否则该变量为0;另外一个界面则是读取当前的界面值
(13)用LED灯照射传感器,观察自动调节模式下开窗关窗是否正常
(14)用手反复遮紫外线传感器来模拟不同的紫外线强度,观察自动调节模式下百叶窗是否会根据紫外线强度的变化而变化
(15)设置手动模式,点击开或关看是否正常开窗关窗
(16)调试过程中发现舵机会有些发烫,测试后发现是电源供电过足,于是降低了电源模块的输出电压,使舵机温度保持稳定
(17)在做百叶窗的时候,最初是选用木板搭建,发现润滑度不够,导致百叶窗开启后,不能自动下落,最终改变方案,使用3D打印的百叶窗进行使用

6 总结

6.1 结论
从1980年至今,嵌入式技术随着时代的进步得到了不断的发展和创新,如何实现对智能控制系统的研发成为许多研究项目的热点问题。其中,随着人们生活水平的不断提高,对智能家居也提出了更高的要求,成为时代发展面临的新的挑战。在本文中,通过对智能百叶窗系统中的软件部分以及硬件部分的设计,在一定程度上提高了我们对智能家居系统设计的认识和了解。
在该文中,主要详细介绍了智能百叶窗系统设计的相关工作机理,选择单片机作为整个系统的控制中心,然后利用LCD线连接电阻触摸屏,通过电阻触摸屏选择相对应的操作,指令传输到单片机,由单片机根据内部的具体编程对指令进行操作并传输到相应的舵机驱动模块,并实现相应的模式。利用紫外线强度传感器采集紫外线强度,并将信息传到单片机,单片机根据紫外线强度的高低对信息进行处理,再传输到舵机模块使得百叶窗状态发生改变。利用语音模块检测人说话的指令,并且将这一信息传输到单片机,在判断指令之后,单片机指令传输到舵机模块,让百叶窗根据指令变化。利用蓝牙模块使得手机APP与单片机之间进行无线连接,人通过手机APP选择相应指令,指令通过蓝牙模块传输到单片机,由单片机根据内部的具体编程对指令进行操作并传输到舵机模块,并实现相应的功能。
6.2 展望
科技创新水平的不断发展,使得对智能家居控制器的研发不断的深入,其中,窗帘智能控制系统的设计和研发将会面临新的发展高潮,极大的提高了人们的生活水平以及舒适。当前,在我国,通过单片机实现对智能控制系统的设计应用并不普及,但随着时代的不断发展,智能控制系统将会逐步的得到推广和应用。
在此,我们相信,每一次付出和系统的设计研发,对未来的发展都起着积极的推动作用,打下坚实的基础。

作者:QQ2083558048

物联沃分享整理
物联沃-IOTWORD物联网 » 基于单片机的智能百叶窗设计

发表回复