【智能家居项目】裸机版本——项目介绍 | 输入子系统(按键) | 单元测试

news2024/11/20 7:19:12

🐱作者:一只大喵咪1201
🐱专栏:《智能家居项目》
🔥格言:你只管努力,剩下的交给时间!
请添加图片描述

目录

  • 🏀项目简介
  • 🏀输入子系统(按键)
    • ⚽应用层
    • ⚽设备层
    • ⚽ 内核层抽象层
    • ⚽芯片抽象层
    • ⚽硬件操作
  • 🏀按键单元测试
    • ⚽串口
    • ⚽测试
  • 🏀源码
  • 🏀总结

在这个专栏中,本喵要实现一个智能家居的小项目,先基于HAL库实现裸机版本,之后再实现一个RTOS版本,为了无缝实现从裸机到RTOS的移植以及维护,本喵会使用面向对象的思想,将整个项目分层来实现,构建一种编程架构。

本项目重点:

  • 设计出优秀的程序框架:容易扩展、容易维护。
  • 具体:
  • 把项目拆分为各个子系统。
  • 使用面向对象的思想,把子系统抽象为结构体。
  • 编写函数时,有一定的封装细节,看函数名就知道怎么用,不需要深入函数内部看它的实现。

🏀项目简介

图
如上图,使用百问网的STM32F103ZET6开发板,实现:

  • 开发板启动后,自动连接家里的路由器,在OLED上显示出IP。
  • 手机上启动微信小程序,输入开发板OLED上显示的IP,连接开发板。
  • 在微信小程序里,点击图标控制开发板的LED、风扇。

图
如上图所示,在程序设计过程中,分为几个层次:

  • 第1层:软件系统,就是整个系统、整个程序。
  • 第2层:分解为子系统,比如我们可以拆分为:输入子系统、显示子系统、业务系统。
  • 第3层:分解为类,在C语言里没有类,可以使用结构体来描述子系统。
  • 第4层:分解成子程序,实现那些结构体中的属性和方法(结构体中有函数指针)。

图

如上图所示,在本项目中,可以分为6个子系统:

  • 设备子系统:比如实现LED控制、风扇控制。
  • 显示子系统:在OLED上显示信息。
  • 输入子系统:可以接收按键数据、网络数据。
  • 网络子系统:负责网络连接、数据收发。
  • 字体子系统:获得字符的字库。
  • 业务子系统:起综合作用,根据输入值(网络数据),控制设备。

其中业务子系统包含其余5个子系统,可以看作是上层,并且同样也可以看作一个子系统。

🏀输入子系统(按键)

图
首先来实现输入子系统,它可以接收来自按键,网络,标准输入等设备的数据,然后供上层业务子系统去使用。整个输入子系统划分为五个层次实现,这里本喵仅实现按键一个输入设备。

⚽应用层

  • 对于传递的"数据数据",我们把它称为"输入事件"。

图

如上图,在input_system.h输入子系统头文件中定义输入事件结构体,用来描述发生的输入事件,无论是按键输入还是网络以及标准输入,都会创建一个这样的结构体对象,但是INPUT_EVENT_TYPE不同,只有根据该成员变量的值才可以确定发生了哪种输入,通过其他成员变量可以获取到需要的事件属性,比如发生事件,按键编号,以及字符串数据等等。

输入事件类型有多种,在这个项目中并不会用到触摸屏输入,本喵这样写是为了表明拓展维护的方便性,在输入子系统层面,需要增加输入事件类型,以及描述输入事件的结构体InputEvent中增加触摸屏触摸的位置。


接下来就是输入事件的来源了,从框图中看到有按键输入,网络输入,标准输入,以后甚至可以扩展更多的输入来源,这些输入来源产生输入事件。

  • "输入事件"由"输入设备"产生。

图
如上图,在input_system.h输入子系统头文件中定义输入设备结构体,用来描述输入设备,每一个设备都会创建一个这样的结构体对象,其中包含设备的名称,获取输入事件,初始化设备,去初始化设备等方法,以及下一个设备节点的指针。

每一个设备都自带获取输入事件的方法,也就是获取InputEvent对象的函数,站在输入子系统的层面,它并不关心该方法是如何实现的,只在需要获取输入数据的时候直接调取该方法即可。

包括初始化和去初始化也是设备自带的方法,上层只需要直接调用即可,至于去初始化是在不需要某个设备的时候,将其配置恢复到初始化状态,从系统中抹除该设备。

为了管理多个设备,本喵将其放在一个链表中,所以还有一个pNext指针指向下一个设备节点。

  • 在输入子系统层面,并不关心获取输入事件函数是如何实现的,而且该函数的实现涉及到了硬件底层,所以并不在子系统层面实现。

图
如上图,在input_system.c源文件中,创建一个全局链表,用来让输入子系统管理输入设备,并且实现注册输入设备,增加输入设备,初始化所有输入设备等函数。

注册输入设备的本质就是将新增加的输入设备节点插入到链表中,让输入子系统能够通过操作链表来维护使用输入设备。

增加输入设备也是输入子系统要处理的事情,在增加输入设备函数中再调用增加具体输入设备的函数,需要增加多少输入设备,就将对应设备的增加函数放进去。

初始化所有输入设备的时候,只需要变量链表中的设备节点,调用每个设备节点自带的初始化化函数即可。

  • 这三个函数要在input_system.h中声明。

无论是一个输入设备还是多个输入设备,所产生的数据并不只一个,但是使用者只有输入子系统一个,为了防止数据丢失,所以这些数据也需要维护起来,这里使用环缓冲区列来维护,主要有输入事件产生,就将相应的InputEvent对象放入环形缓冲区中,子系统只需要从环形缓冲区读取数据就可以,不用关心数据是怎么来的。
图
如上图所示,环形缓冲区本质上也是一个数组,就拿写来说,当这个数组被写满后ring_buffer[7] = data,就通过取模运算pW = (7 + 1) % 8 = 0重新从数组的起始位置开始写数据,读也是类似的道理。

  • pR是向环形缓冲区读数据时的下标。
  • pW是向环形缓冲区写数据时的下标。

通过pR是否等于pW来判断环形缓冲区中是否有数据,没有数据就相等,有数据就不相等,同样通过pW是否等于pR来判断环形缓冲区是否写满数据,相等就写满了,不相等就没写满。

图
如上图所示,定义环形缓冲区结构体,通过维护pWpR来维护环状,以及从存放输入事件的buffer中读写事件。

输入子系统还需要提供读写数据的方法:

图
如上图所示,创建一个全局的环形缓冲区对象,由于是静态全局变量,且没有初始化,所以编译器会用0去初始化,并放在未初始化数据段,读写事件都是在操作这个全局的环形缓冲区。


图
此时,输入子系统已经具有了上图所示结构以及对应的操作方法,输入子系统的层就完成了,到目前位置丝毫没有提及到和STM32F103ZE开发板有关的内容,连一句相关的代码也没有,实现了应用层和硬件的解耦。


⚽设备层

此时输入子系统中的上层部分已经完成了,还需要处理输入子系统设备层,这里本喵仅实现按键输入设备:

图
如上图所示,在gpio_key.h中定义了两个按键的编号,之后直接使用即可。

图
如上图所示,在gpio_key.c中实例化出一个按键对象,并进行初始化,赋值设备名,初始化函数等,还要提供一个增加按键设备的函数AddInputDeviceGPIOKey供应用层在初始化所有设备时候调用。

  • 对于裸机程序,事件获取方法不用注册到设备队列中,而是在后面中断函数中调用。

图
此时,已经实现了按键的设备层,包括按键设备的实例化,按键设备的初始化方法,以及增加按键设备的方法。

⚽ 内核层抽象层

本喵想让这个系统支持多个系统,包括裸机,FreeRTOS,RT-Thread,甚至是Linux,这里将裸机也看作是一种内核。

不同内核下的数据来源:

  • 裸机:数据来自中断,在中断中解析数据并放入环形缓冲区。
  • RTOS:创建任务,在任务中解析数据并放入环形缓冲区。

内核抽象层中,根据不同的内核对按键进行初始化,本喵这里仅实现裸机的按键初始化:

图

如上图,初始化按键的时候,调用KAL_GPIOKeyInit,在函数内部再调用不同内核对按键的初始化函数,对于裸机则调用芯片层的CAL_GPIOKinit函数进行初始化,如果是RTOS,则仅需要将该函数改成对应的初始化函数即可。

  • 设备抽象层调用的是该层的KAL_GPIOKeyInit,根本不关心具体的实现逻辑。

在描述输入事件的结构体InputEvent中有一个time成员变量用来记录事件发生的事件,而这个时间在不同的内核中表现方式不同,所以在内核抽象层需要实现获取时间的函数。

图
如上图,在使用的时候,直接调用内核抽象层的KAL_GetTime获取时间即可,在该函数内部,根据具体的获取方式调用对应的函数。

如本喵使用的STM32F103ZET6是通过滴答定时器来获取时间的,需要获取芯片中寄存器的值,所以要调用CAL_GetTime从芯片获取时间。

对于Linux,它在系统内部会记录着时间,此时就可以直接返回时间,不用再向下调用。

图

此时,内核抽象层也实现了,设备层会调用内核抽象层的初始化函数。

⚽芯片抽象层

项目的最终实现需要依托具体的芯片,本喵用的STM32F103ZET6是支持HAL库的,但是也有一些芯片并没有HAL库,需要用它自己的库来操作,所以在这一层要实现对不同类型芯片的支持。

图

如上图,在芯片抽象层会调用CAL_GPIOKeyInit来初始化按键,在函数内部根据不同的芯片再调用它对应的初始化函数,如ST芯片就调用KEY_GPIO_ReInit


同样,不同芯片获取时间的方式也不同,这里也要实现针对不同芯片获取时间的方式:

tu
如上图所示,从芯片寄存器中获取时间的时候,对于ST芯片,调用HAL_GetTick获取即可,对于其他芯片,放入对应的获取方式即可。

图

此时芯片抽象层也实现了,内核抽象层会调用该层的CAL_GPIOKeyInit初始化按键。

⚽硬件操作

本喵使用的是STM32F103ZET6芯片,使用CubeMXHAL库进行按键初始化,在初始化的时候,要在中断函数中进行输入数据的读取,并放入环形缓冲区中。

图
如上图,在driver_key.h中进行一些芯片的资源定义,方便后面使用。

图

如上图,使用HAL库对按键进行初始化,在按键中断函数中处理输入事件InputEvent并且放入到环形队列中

图
此时,具体芯片的硬件配置也设置好了,输入子系统中按键设备就完全写好了。


图
如上图,现在整个代码结构是这样,其中智能家居项目部分全部放在了smartdevice文件夹中,包含输入子系统的应用层,设备抽象层,内核抽象层,芯片抽象层。

其余部分是通过CubeMX进行的基本外设配置,整个输入子系统中,只有在硬件操作的时候会用到这里的配置,其余四层都是独立的,不存在耦合。

🏀按键单元测试

⚽串口

为了观察按键按下后的现象,使用串口将发生的输入事件InputEvent打印出来,此时串口配置并不属于我们实现的输入子系统,只是一个调试工具,直接使用HAL库配置就可以。

图
如上图所示是串口的头文件,只包含串口的使能和失能函数声明。

图

如上图所示是串口的具体配置函数,这里同样需要一个环形缓冲区,这里本喵就不展示它的实现了,后面本喵会放源码。

在调用EnableDebugIRQ打开串口后,在向串口发送数据的时候,直接调用printf即可,因为printf底层会调用fputc函数,所以需要在这里将fput重定向,使得printf符合我们的要求。

fputc中,先将发送完成标志清0,然后调用HAL库的中断发送函数发送一个字节,当发送完成标志位为0时就一直等待,说明没有发送完成。这个字节发送完成以后,会进入串口的发送中断回调函数,在中断函数中将发送标志位置1,让fputc退出循环等待。printf发送多个字节就调用多次fputc

在获取串口发送来的数据时,直接调用scanf即可,因为scanf底层会调用fgetc函数,所以也需要重定向fgetc函数,使得scanf符合我们的要求。

当串口上有数据到来时,会发生串口中断,通过判断SR寄存器的第五位确定是接收到了数据,并且将接收到的数据放入到环形缓冲区中。fgetc直接从环形缓冲区中读取数据。

  • 为了像在PC端一样使用标准库中的printfscanf,必须重新实现fputcfgetc函数,让终端变成串口,符合我们的要求。

⚽测试

为了看我们设计的输入子系统是否正确,需要专门写一个单元测试函数来测试一下:

图
如上图所示,将按键设备添加到输入子系统中,然后进行初始化,在while(1)循环中读取输入事件,并通过串口打印输入事件的信息。

main函数中调用该测试函数,通过串口调试助手查看打印信息:

tu
如上图,将板子的串口和电脑连在一起后,通过串口调试助手可以看到,当按键1或者按键2按下后,会打印出发生的事件信息,包括事件类型,发生事件,按键编号,以及按键值,说明设计的输入子系统是成功的。

🏀源码

这部分代码是在OLED代码的基础上写的,包含源码以及串口调试工具,需要的小伙伴自取传送门。

🏀总结

这篇文章实现了智能家居项目中输入子系统中的按键设备,最重要的是介绍的代码框架和编程思想,之后的项目部分都会按照这个思路来扩展维护。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1046368.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于谷歌Transeformer构建人工智能问答系统

目录 1 项目背景 2 关键技术 2.1 Transeformer模型 2.2 Milvus向量数据库 3 系统代码实现 3.1 运行环境构建 3.2 数据集介绍 3.3 预训练模型下载 3.4 代码实现 3.4.1 创建向量表和索引 3.4.2 构建向量编码模型 3.4.3 数据向量化与加载 3.4.4 构建检索web 3.5 运行结…

VS+Qt+C++ GDAL读取tif图像数据显示

程序示例精选 VSQtC GDAL读取tif图像数据显示 如需安装运行环境或远程调试,见文章底部个人QQ名片,由专业技术人员远程协助! 前言 这篇博客针对《VSQtC GDAL读取tif图像数据显示》编写代码,代码整洁,规则,…

基于SpringBoot的美容院管理系统设计与实现

目录 前言 一、技术栈 二、系统功能介绍 管理员功能实现 美容部位管理 销量信息统计 已支付订单 技师功能实现 统计美容用品库存 预约信息管理 前台功能实现 普通用户管理 会员管理 普通用户功能实现 美容用品 购物车 我的订单 会员功能实现 美容项目 预约信…

PyCharm中使用pyqt5的方法2-1

qt可以用来设计界面,而pyqt是将qt移植到Python上,通过python语言设计界面,目前最新的版本是qt5。 在PyCharm中使用pyqt5的步骤分为下载和配置两个部分。 1 在PyCharm中下载安装pyqt5相关模块 1.1 下载步骤 PyCharm中要下载的pyqt5相关模块…

kafka集群是如何选择leader,你知道吗?

前言 kafka集群是由多个broker节点组成,这里面包含了许多的知识点,以下的这些问题你都知道吗? 你知道topic的分区leader是怎么选举的吗?你知道zookeeper中存储了kafka的什么信息吗?起到什么做呢?你知道kafka消息文件…

【精彩回顾】 用sCrypt在Bitcoin上构建智能合约

2023年3月24日,sCrypt在英国Exeter大学举办了关于智能合约的大学讲学。sCrypt首席执行官刘晓晖做了题为“用sCrypt在Bitcoin上构建智能合约”的演讲,并与到场的老师、学生进行了深入交流、互动。这次课程着重讲解了 BSV 智能合约的基础概念,以…

一图读懂「五度易链」企业创新服务解决方案,打造卓越营商环境!

“五度易链”紧密围绕园区企业及产业发展需求,基于数据积累和应用,创新企业服务机制,提升企业服务效能,以数字化手段为企业发展纾困解难,赋能企业高质量发展。并帮助园区在运营方面打破数据壁垒,实现数据监…

调度算法+等待/周转时间计算

周转时间 作业完成时刻 - 到达时刻 等待时间 开始时刻 - 到达时刻 平均时间就是用总时间除以作业个数 先来先服务调度算法(FCFS) 非抢占 优先级调度算法 系统总是调度优先级最高的那个进程运行。 优先级可以分为静态优先级和动态优先级。静态优先…

113. 路径总和ii

力扣题目链接(opens new window) 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 说明: 叶子节点是指没有子节点的节点。 示例: 给定如下二叉树,以及目标和 sum 22, 在路径总和题目的基础上&…

新媒体运营的未来:ChatGPT的智能助手

💂 个人网站:【工具大全】【游戏大全】【神级源码资源网】🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅 寻找学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】 新媒体运营是数字时代的…

Java括号匹配

目录 一、题目描述 二、题解 一、题目描述 给定一个只包括 (,),{,},[,] 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭…

LwIP笔记01:LwIP入门

1. LwIP简介 小型开源的TCP/IP协议栈交换机、路由器、光纤收发器、云台接入、无线网关、远程模块、工业控制器、网络摄像头 TCP/IP模型 (1)应用层:HTTP、MQTT、NTP、FTP、...... (2)传输层:TCP、UDP &…

【kubernetes】【基础资源使用】kubernetes中的Deployment使用

1 Why need Deployment? K8S中Pod是用户管理工作负载的基本单位,Pod通常通过Service进行暴露,因此,通常需要管理一组Pod,RC和RS主要就实现了一组Pod的管理工作,其中,RC和RS的区别在于,RS提供更…

如何使用pycharm连接Mysql数据库!!!

1、Mysql的安装: MySQL针对不同的用户提供了2中不同的版本: MySQL Community Server:社区版。由MySQL开源社区开发者和爱好者提供技术支持,对开发者开放源代码并提供免费下载。MySQL Enterprise Server:企业版。包括最…

新手程序员怎么接单?

程序员如何在自己年富力强的时候,最大化发挥自己的能力?将超能力转化为“钞能力”? 有人还在苦哈哈当老黄牛,一身使不完的牛劲,有人已经另辟蹊径,开创了自己的一片致富小天地。 接单找兼职,就…

MyBatis-Plus多数据源——如何在一个项目中使用多个MySQL数据库

前言 MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window) 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 本系列博客结合实际应用场景,阐述MyBatis-Plus实际…

小程序中如何同步公众号的文章

小程序为了更好的服务客户,有时候需要显示公众号的文章,用于更具体介绍产品、关注公众号和会员服务等。下面就将具体介绍:小程序中如何同步显示公众号的文章。 1. 关联公众号。在管理员后台->会员管理->通知处,关联服务号。…

【大数据开发技术】实验05-HDFS目录与文件的创建删除与查询操作

文章目录 HDFS目录与文件的创建删除与查询操作一、实验目标二、实验要求三、实验内容四、实验步骤附:系列文章 HDFS目录与文件的创建删除与查询操作 一、实验目标 熟练掌握hadoop操作指令及HDFS命令行接口掌握HDFS目录与文件的创建方法和文件写入到HDFS文件的方法…

软件测试工作步骤详情

软件测试步骤按照研发阶段一般分为5个部分:单元测试、集成测试、确认测试、系统测试、验收测试,下面将不同阶段需要的一些工作内容做一下梳理希望可以帮助到大家。 一、单元测试的内容:(白盒为主,黑盒为辅)…

业务安全情报23期 | 国庆前夕,又成功狙击一个倒卖机票的不法团伙

中秋国庆临近,热门航线机票预定量暴增。顶象防御云业务安全情报中心,监测到一个不法团伙进行虚假占座攻击,倒卖热门航班机票。在顶象协助下,该航空公司有效阻截多日的攻击,保障乘客购票利益。 热门航班遭到“倒票”攻击…