物联网实战--入门篇之(八)嵌入式-空气净化器

目录

一、风扇调速

二、通讯协议

三、净化器运行逻辑


一、风扇调速

        单片机是不能直接驱动电机的,因为主芯片的驱动电流比较小(50mA左右),他们之间正常还要有个电机驱动器,常用的有TB6612、L298和L9110等,目前项目用的这个电机它是内置了驱动器的,什么型号不清楚,作为应用层只要给个PWM信号就行了。

        PWM基本原理就是通过控制开关的时长进行调速,STM32通过定时器发生器,可以把开关周期控制在毫秒以内,这样我们宏观上就不会有卡顿的感觉了。

        下面是具体的代码,这些代码都比较常规,要注意的是PWM输出要手动开启TIM_CtrlPWMOutputs(TIM1, ENABLE),可能是PA8引脚与串口1的初始化有冲突了。

/*		
================================================================================
描述 : 风扇电机初始化
输入 : 
输出 : 
================================================================================
*/
void app_motor_init(void)
{
// 使能TIM1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);// 使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 设置GPIOA_8为复用功能推挽输出GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化TIM1 PWM模式TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_Period = PWMPeriodValue-1; // 周期为10kTIM_TimeBaseStructure.TIM_Prescaler = 720 - 1; // 预分频器设置为7199,确保计数器的频率为1MHzTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//选择PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较极性选择TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能TIM_OC1Init(TIM1, &TIM_OCInitStructure);//初始化 TIM1 OC1TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);//使能CCR1自动重装TIM_ARRPreloadConfig(TIM1,ENABLE);//开启预装载  TIM_CtrlPWMOutputs(TIM1, ENABLE);//手动开启,防止与串口1冲突后不启动// 使能TIM1的输出比较预装载寄存器TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);// 使能TIM1TIM_Cmd(TIM1, ENABLE);  printf("app_motor_init ok!\n");
}

        在使用上就很简单了,就是不断改变PWM值就行了,如下所示。这里还有个系数0.6主要是为了防止电流过大,影响主控设备稳定。

/*		
================================================================================
描述 : 设置速度
输入 : speed 0~255,速度分为256级
输出 : 
================================================================================
*/
void app_motor_set_speed(u8 speed)
{g_sAirWork.fan_speed=speed;u16 value=speed*PWMPeriodValue/255*0.6;//系数主要是防止电机电流过大影响设备稳定printf("pwm val=%d\n", value);TIM_SetCompare1(TIM1, value);
}

二、通讯协议

        这里的通讯协议是指在MQTT之上的应用层通讯协议,属于我们自定义的内容,由此我们也可以知道,协议也是一层一层堆起来的,每个场景有各自适合的协议。通讯协议在物联网系统里是最重要的,如果交流语言都不通了,还谈什么联网。

        自定义协议满足自己的要求即可,但是我们的要求也不能太低,至少要满足以下几个要求:

1、二进制传输,比较高效、省流量,这在使用4G流量卡的时候就很关键了;

2、易检索,可以在一堆数据包里找到明显特征并解析,可以解决粘包问题;

3、正确性,要有校验码,确保数据正确;

4、方便代码书写,这样可以在各类系统中使用;

5、没有网络大小端问题,这是早期很多人用结构体传输时候经常碰到的问题。

        上图是基本的协议框架,包含了帧头、数据长度、设备码、命令和校验码等关键通用的信息,需要注意的是,这里整形数据都采用高字节先传输的原则,比如数据总长Len,先传输Len的高8位,再传输低8位,具体的整合代码如下:

        这里面有个入口参数cmd_type,根据这个项目的定义如下。

        在实际使用的时候举例如下,把自己应用层的数据按你自己的顺序要求打包好发送就行了。

        细心的同学会发现,上图中在传输温度数据时候比较特别,先把数值乘以10再加1000,为什么要这么操作?主要是为了保证传输的数据都是正整数,避免了网络大小端的问题,这里的乘以10是保留小数点后一位,传感器返回的数据最小是-45.0,乘以10就是-450,再加上1000就可以保证这个数值是正整数,最后再把这个正整数高8位、低8位依次存进数组内。对于接收端,解析就是一个逆向过程了,具体如下图所示,获取到原始数据后先减1000再除以10.0,这里要记得是10.0而不是10,这样才能保持小数。

        对于接收端,就是按照协议一步步解析即可,代码很简洁、模式很固定的,具体的可以工程项目打开来看看。


/*		
================================================================================
描述 : 设备解析服务器下发的数据
输入 : 
输出 : 
================================================================================
*/
void app_air_recv_parse(u8 *buff, u16 len)
{u8 head[2]={0xAA, 0x55};u8 *pData=memstr(buff, len, head, 2);if(pData!=NULL){u16 total_len=pData[2]<<8 | pData[3];u16 crcValue=pData[total_len]<<8 | pData[total_len+1];if(crcValue==drv_crc16(pData, total_len)){pData+=4;u32 device_sn=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];pData+=4;if(device_sn!=g_sAirWork.device_sn)//识别码确认return;u8 cmd_type=pData[0];pData++;switch(cmd_type){case AIR_CMD_HEART://心跳包{break;}case AIR_CMD_DATA://数据包{break;}case AIR_CMD_SET_SPEED://设置风速{u8 speed=pData[0];pData+=1;app_motor_set_speed(speed);break;}        case AIR_CMD_SET_SWITCH://设置开关{u8 state=pData[0];pData+=1;g_sAirWork.switch_state=state;if(state>0){app_motor_set_speed(100);//启动风扇}else{app_motor_set_speed(0);//停止风扇}app_air_send_status();break;}}}}
}

三、净化器运行逻辑

        到目前为止,嵌入式端的各个模块基本上讲解完成了,剩下的就是如何把他们整合起来的问题了。从任务上来讲就三个部分:一是通讯连接,二是传感数据采集和发送,三是风速和开关控制。这里面任务最繁忙的要数通讯连接了,要驱动WiFi连接以及MQTT的运行,比较庞大,理论上要用RT-Thread单独为它创建个任务,但是这里由于传感数据采集内容较为简单,这里就不那么麻烦了,直接放一起就好了。对于风扇和开关控制属于被动的,其实已经在数据解析里完成了,即跟MQTT主程序同一个任务。总的来讲,就是下图这个任务了,对照着注释看,很简单。

        这样,整个项目的嵌入式部分就讲解完成了,接下来继续讲解的是手机端开发的内容了。

        

   本项目的交流QQ群:701889554

   写于2024-4-1

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

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

相关文章

【软件测试】测试常见知识点汇总

测试常见知识点汇总 一、什么是测试1.1 测试和调试的区别1.2 什么是需求1.2.1 用户需求1.2.2 软件需求 1.3 测试用例要素1.4 软件的生命周期及各阶段概述1.5 开发模型和测试模型&#xff08;记住特点和适用场景&#xff09;1.5.1 开发模型1.5.1.1 瀑布模型&#xff08;自上而下…

SWM341系列应用(上位机应用)

SWM341系列之上位机应用 1、分级图像和PNG、JPG的应用 现象&#xff1a;客户使用SWM34SVET6HMI_0.4.1版本上位机进行UI界面布局&#xff0c;反馈在模拟运行时&#xff08;PC端&#xff09;流畅&#xff0c;在Demo平台&#xff08;设备端&#xff09;运行卡顿。 分析及解决&…

ids工业相机与电控位移台同步控制及数据采集

通过VS2017和OpenCV&#xff0c;实现ids工业相机与电控位移台同步控制及数据采集 目录项目环境配置代码流程及思路项目架构项目开发运行效果开发关键ids相机配置位移台环境配置相机头文件相机参数设置保存图像函数设置电控位移台头文件电控位移台设置参数最后就是通过main函数进…

Collection与数据结构 链表与LinkedList(三):链表精选OJ例题(下)

1. 分割链表 OJ链接 class Solution {public ListNode partition(ListNode head, int x) {if(head null){return null;//空链表的情况}ListNode cur head;ListNode formerhead null;ListNode formerend null;ListNode latterhead null;ListNode latterend null;//定义…

计算机网络-HTTP相关知识-RSA和ECDHE及优化

HTTPS建立基本流程 客户端向服务器索要并验证服务器的公钥。通过密钥交换算法&#xff08;如RSA或ECDHE&#xff09;协商会话秘钥&#xff0c;这个过程被称为“握手”。双方采用会话秘钥进行加密通信。 RSA流程 RSA流程包括四次握手&#xff1a; 第一次握手&#xff1a;客户…

Python可视化之Matplotlib

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言1、解决坐标轴刻度负号乱码2、解决中文乱码问题3、图形展现形式 一、图形绘制1.折线图plot2.散点图plot&scatter3.柱状图plt.bar&条形图plt.barh4.直方…

探索设计模式的魅力:简单工厂模式

个人主页: danci_ &#x1f525;系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#xff1a;探索设计模式的魅力&#xff1a;简单工厂模式 简单工厂模式&#x…

Prometheus+grafana环境搭建MongoDB(docker+二进制两种方式安装)(五)

由于所有组件写一篇幅过长&#xff0c;所以每个组件分一篇方便查看&#xff0c;前四篇mongodb的exporter坑也挺多总结一下各种安装方式&#xff0c;方便后续考古。 Prometheusgrafana环境搭建方法及流程两种方式(docker和源码包)(一)-CSDN博客 Prometheusgrafana环境搭建rabb…

Redis面试题28道

1、什么是类加载器&#xff0c;类加载器有哪些&#xff1f; 1、什么是类加载器&#xff1f; 类加载器负责加载所有的类&#xff0c;其为所有被载入内存的类生成一个 java.lang.Class 实例对象。 2、类加载器有哪些&#xff1f; JVM 有三种类加载器&#xff1a; &#xff08…

软考--软件设计师(软件工程总结2)

目录 1.测试方法 2.软件项目管理 3.软件容错技术 4.软件复杂性度量 5.结构化分析方法&#xff08;一种面向数据流的开发方法&#xff09; 6.数据流图 1.测试方法 软件测试&#xff1a;静态测试&#xff08;被测程序采用人工检测&#xff0c;计算机辅助静态分析的手段&…

GraphSage

背景 大型图中节点的低维嵌入在各种预测任务中非常有用。GraphSage是一种通用的归纳框架&#xff0c;它利用节点特征信息&#xff08;例如&#xff0c;文本属性&#xff09;有效地为以前看不见的数据生成节点嵌入。相比于对每个节点训练单独的嵌入&#xff0c;GraphSage学习了一…

Hyper-v平台搭建pve系统之网络配置(双网卡、内外网分离)

现在我需要在我本地配置的PVE系统上配置双网卡&#xff0c;然后一个连接外部网络&#xff08;访问互联网&#xff09;&#xff0c;一个连接内部网络&#xff08;只能和宿主机之间互相访问&#xff09; 最终效果&#xff1a; 登录PVE平台&#xff0c;我可以正常访问外网&#…

用html写一个爱心

<!DOCTYPE html> <html lang"zh-CN"><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8" /><title>爱您</title><style>* {padding: 0;margin: 0;}body {background-color: pin…

铁山靠之数学建模-基础篇

小黑子的数模基础篇 一、什么是数学建模1.1 数学模型分类1.2 备战准备什么1.3 组队学习路线1.4 赛前准备1.5 赛题选择1.5.1 赛题类型1.5.2 ABC赛题建议 1.6 学会查询1.6.1 百度搜索技巧1.6.2 查文献1.6.3 数据预处理 1.7 建模全过程 二、数模论文2.1 论文排版2.2 标题怎么写2.3…

前端学习之BOM编程-window对象、history对象、navigator对象、location对象

window对象 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>widnow对象</title><style>div{width: 2000px;height: 4000px;background-image: linear-gradient(to bottom right,red,blue…

JVM调优参数介绍

堆配置 -Xms:初始堆大小 -Xms&#xff1a;最大堆大小 -XX:NewSizen:设置年轻代大小 -XX:NewRation:设置年轻代和年老代的比值。如&#xff1a;为3表示年轻代和年老代比值为1&#xff1a;3&#xff0c;年轻代占整个年轻代年老代和的1/4 -XX:SurvivorRation:年轻代中Eden区与…

计组第三版书例题

基础知识过一下 存储器与CPU的连接主要通过数据总线、地址总线和控制总线实现。CPU首先向存储器发送地址信号&#xff0c;然后发出读写控制信号&#xff0c;最后在数据总线上进行数据的读写操作 。这种连接方式确保了CPU能够正确地访问和控制存储器中的数据。 https://blog.cs…

ES6学习(五)-- Module 语法

文章目录 Module 语法1.1 痛点介绍(1) 异步加载(2) 私密(3) 重名(4) 依赖 1.2 解决方法(1) 解决异步加载问题(2) 解决私密问题(3) 重名解决方法(4) 解决依赖问题 1.3 模块化使用案例 Module 语法 之前js 出现的某些痛点&#xff1a; 在script 中引入的变量名不可以重复&#…

Day:003 | Python爬虫:高效数据抓取的编程技术(爬虫基础)

urllib发送get请求 在目前网络获取数据的方式有多种方式&#xff1a;GET方式大部分被传输到浏览器的html&#xff0c;images, js,css, … 都是通过GET 方法发出请求的。它是获取数据的主要方法 例如&#xff1a;www.baidu.com 搜索 Get请求的参数都是在Url中体现的,如果有中…

SQLAlchemy 建立数据库模型之间的关系

常见关系&#xff1a; 一对多关系多对一关系多对多关系一对一关系 一对多关系&#xff08;一个作者&#xff0c;多篇文章&#xff09; ## 一对多关系&#xff0c;单作者-多文章&#xff0c;外键不可少 ## 外键(ForeignKey)总在多的那边定义,关系(relationship)总在单的那边定…