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

🐱作者:一只大喵咪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.mzph.cn/news/91681.shtml

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

相关文章

简单三步 用GPT-4和Gamma自动生成PPT PDF

1. 用GPT-4 生产PPT内容 我想把下面的文章做成PPT,请你给出详细的大纲和内容 用于谋生的知识,学生主要工作是学习,成年人的工作是养家糊口,这是基本的要求,在这之上,才能有更高的追求。 不要短期期望过高…

问题: 视频颜色问题,偏绿

参考 什么是杜比视界? - https://www.youtube.com/watch?vldXDQ6VlC7g 【哈士亓说】07:HDR、杜比视界究竟是个啥?为什么这个视频还不是HDR视频? - https://www.youtube.com/watch?vrgb9Xg3cJns 正文 视频应该是 杜比视界 电…

Java 设计模式——抽象工厂模式

目录 1.概念2.结构3.实现4.优缺点5.使用场景6.模式扩展7.JDK 源码解析——Collection.iterator 方法 1.概念 (1)Java 设计模式——工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机等。这些工厂只生产同种类产…

653. 两数之和 IV - 输入二叉搜索树

给定一个二叉搜索树 root 和一个目标结果 k,如果二叉搜索树中存在两个元素且它们的和等于给定的目标结果,则返回 true。 输入: root [5,3,6,2,4,null,7], k 9 输出: true 1.第一种思路 中序遍历双指针 即先中序递归遍历并用动态数组ArrayList存储&am…

网络-跨域解决

文章目录 前言一、跨域是什么?二、跨域的解决1.JSONP2.前端代理dev环境3.后端设置请求头CORS4.运维nginx代理 总结 前言 本文主要介绍跨域问题介绍并提供了四种解决办法。 一、跨域是什么? 准确的来说是浏览器存在跨域问题,浏览器为了安全考…

Ubuntu基于Docker快速配置GDAL的Python、C++环境

本文介绍在Linux的Ubuntu操作系统中,基于Docker快速配置Python、C等不同编程语言均可用的地理数据处理库GDAL的方法。 首先,我们访问GDAL库的Docker镜像官方网站(https://github.com/OSGeo/gdal/tree/master/docker)。其中&#x…

unity 限制 相机移动 区域(无需碰撞检测)

限制功能原著地址:unity限制相机可移动区域(box collider)_unity限制相机移动区域_manson-liao的博客-CSDN博客 一、创建限制区域 创建一个Cube,Scale大小1,添加组件:BoxCollder,调整BoxColld…

Arcgis克里金插值报错:ERROR 010079: 无法估算半变异函数。 执行(Kriging)失败。

Arcgis克里金插值报错:ERROR 010079: 无法估算半变异函数。 执行(Kriging)失败。 问题描述: 原因: shape文件的问题,此图可以看出,待插值的点有好几个都超出了地理范围之外,这个不知道是坐标系配准的问…

如果在 Mac 上的 Safari 浏览器中无法打开网站

使用网络管理员提供的信息更改代理设置。个人建议DNS解析,设置多个例如114.114.114.114 8.8.8.8 8.8.4.4 如果打不开网站,请尝试这些建议。 在 Mac 上的 Safari 浏览器 App 中,检查页面无法打开时出现的信息。 这可能会建议解决问题的…

2023年中国工业脱水机行业供需分析:随着自动化和智能化技术的快速发展,销量同比增长4.9%[图]

工业脱水机行业是指专门从湿润的固体物料中去除水分的设备制造和相关服务。它广泛应用于食品加工、化工、制药、纺织、环保等行业,用于去除物料中的水分,提高产品质量和降低能耗。 工业脱水机行业分类 资料来源:共研产业咨询(共研…

C. MEX Repetition

题目:样例: 输入 5 1 2 1 3 1 0 1 3 2 2 0 2 5 5 1 2 3 4 5 10 100 5 3 0 4 2 1 6 9 10 8输出 1 2 0 1 2 1 2 3 4 5 0 7 5 3 0 4 2 1 6 9 10 思路: 从题目和样例中,我们可以知道,从一个数组中,按照包括0的自…

让大脑自由

前言 作者写这本书的目的是什么? 教会我们如何让大脑更好地为自己工作。 1 大脑的运行机制是怎样的? 大脑的基本运行机制是神经元之间通过突触传递信息,神经元的兴奋和抑制状态决定了神经网络的运行和信息处理,神经网络可以通过…

idea Springboot 教师标识管理系统开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot 教师标识管理系统是一套完善的信息系统,结合springboot框架和bootstrap完成本系统,对理解JSP java编程开发语言有帮助系统采用springboot框架(MVC模式开发),系统 具有完整的源代码和数据库&…

蓝桥等考Python组别十级003

第一部分:选择题 1、Python L10 (15分) 已知s Pencil,下列说法正确的是( )。 s[0]对应的字符是Ps[1]对应的字符是ns[-1]对应的字符是is[3]对应的字符是e 正确答案:A 2、Python L10 &am…

MySQL MMM高可用架构

MySQL MMM高可用架构一、MMM概述1、MMM简介2、MMM高可用架构3、MMM故障切换流程 二、MMM高可用双主双从架构部署1、配置主主复制(master),主从复制(slave)1)修改 Master1的MySQL配置文件2)把配置…

【AI视野·今日Robot 机器人论文速览 第四十一期】Tue, 26 Sep 2023

AI视野今日CS.Robotics 机器人学论文速览 Tue, 26 Sep 2023 Totally 73 papers 👉上期速览✈更多精彩请移步主页 Daily Robotics Papers Extreme Parkour with Legged Robots Authors Xuxin Cheng, Kexin Shi, Ananye Agarwal, Deepak Pathak人类可以通过以高度动态…

vuejs中缓存组件状态-keepAlive

前言 在 vuejs中,我们经常需要缓存一些组件的状态,比如用户登录后,切换到其他页面,再切换回来,需要保留之前的登录状态,而不是重新登录。 或者在切换不同组件的时候,需要保留之前的组件状态&…

2023年10月腾讯云优惠活动汇总:腾讯云最新优惠、代金券整理

腾讯云作为国内领先的云服务提供商,致力于为用户提供优质、稳定的云服务。为了更好地满足用户需求,腾讯云推出了各种优惠活动。本文将给大家分享腾讯云最新优惠活动,帮助用户充分利用腾讯云提供的优惠。 一、腾讯云优惠券领取【点此领取】 腾…

FFmpeg 命令:从入门到精通 | ffplay 播放控制选项

FFmpeg 命令:从入门到精通 | ffplay 播放控制选项 FFmpeg 命令:从入门到精通 | ffplay 播放控制选项选项表格图片 FFmpeg 命令:从入门到精通 | ffplay 播放控制选项 选项表格 项目说明Q,Esc退出播放F,鼠标左键双击全…

LongLoRA:不需要大量计算资源的情况下增强了预训练语言模型的上下文能力

麻省理工学院和香港中文大学推出了LongLoRA,这是一种革命性的微调方法,可以在不需要大量计算资源的情况下提高大量预训练语言模型的上下文能力。 LongLoRA是一种新方法,它使改进大型语言计算机程序变得更容易,成本更低。训练LLM往…