当前位置:   article > 正文

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

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

目录

一、风扇调速

二、通讯协议

三、净化器运行逻辑


一、风扇调速

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

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

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

  1. /*
  2. ================================================================================
  3. 描述 : 风扇电机初始化
  4. 输入 :
  5. 输出 :
  6. ================================================================================
  7. */
  8. void app_motor_init(void)
  9. {
  10. // 使能TIM1时钟
  11. RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
  12. // 使能GPIOA时钟
  13. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  14. // 设置GPIOA_8为复用功能推挽输出
  15. GPIO_InitTypeDef GPIO_InitStructure;
  16. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  17. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  18. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  19. GPIO_Init(GPIOA, &GPIO_InitStructure);
  20. // 初始化TIM1 PWM模式
  21. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  22. TIM_OCInitTypeDef TIM_OCInitStructure;
  23. TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
  24. TIM_TimeBaseStructure.TIM_Period = PWMPeriodValue-1; // 周期为10k
  25. TIM_TimeBaseStructure.TIM_Prescaler = 720 - 1; // 预分频器设置为7199,确保计数器的频率为1MHz
  26. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  27. TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
  28. TIM_OCStructInit(&TIM_OCInitStructure);
  29. TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
  30. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//选择PWM模式1
  31. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较极性选择
  32. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能
  33. TIM_OC1Init(TIM1, &TIM_OCInitStructure);//初始化 TIM1 OC1
  34. TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);//使能CCR1自动重装
  35. TIM_ARRPreloadConfig(TIM1,ENABLE);//开启预装载
  36. TIM_CtrlPWMOutputs(TIM1, ENABLE);//手动开启,防止与串口1冲突后不启动
  37. // 使能TIM1的输出比较预装载寄存器
  38. TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
  39. // 使能TIM1
  40. TIM_Cmd(TIM1, ENABLE);
  41. printf("app_motor_init ok!\n");
  42. }

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

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

二、通讯协议

        这里的通讯协议是指在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,这样才能保持小数。

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

  1. /*
  2. ================================================================================
  3. 描述 : 设备解析服务器下发的数据
  4. 输入 :
  5. 输出 :
  6. ================================================================================
  7. */
  8. void app_air_recv_parse(u8 *buff, u16 len)
  9. {
  10. u8 head[2]={0xAA, 0x55};
  11. u8 *pData=memstr(buff, len, head, 2);
  12. if(pData!=NULL)
  13. {
  14. u16 total_len=pData[2]<<8 | pData[3];
  15. u16 crcValue=pData[total_len]<<8 | pData[total_len+1];
  16. if(crcValue==drv_crc16(pData, total_len))
  17. {
  18. pData+=4;
  19. u32 device_sn=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];
  20. pData+=4;
  21. if(device_sn!=g_sAirWork.device_sn)//识别码确认
  22. return;
  23. u8 cmd_type=pData[0];
  24. pData++;
  25. switch(cmd_type)
  26. {
  27. case AIR_CMD_HEART://心跳包
  28. {
  29. break;
  30. }
  31. case AIR_CMD_DATA://数据包
  32. {
  33. break;
  34. }
  35. case AIR_CMD_SET_SPEED://设置风速
  36. {
  37. u8 speed=pData[0];
  38. pData+=1;
  39. app_motor_set_speed(speed);
  40. break;
  41. }
  42. case AIR_CMD_SET_SWITCH://设置开关
  43. {
  44. u8 state=pData[0];
  45. pData+=1;
  46. g_sAirWork.switch_state=state;
  47. if(state>0)
  48. {
  49. app_motor_set_speed(100);//启动风扇
  50. }
  51. else
  52. {
  53. app_motor_set_speed(0);//停止风扇
  54. }
  55. app_air_send_status();
  56. break;
  57. }
  58. }
  59. }
  60. }
  61. }

三、净化器运行逻辑

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

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

        

   本项目的交流QQ群:701889554

   写于2024-4-1

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/387280
推荐阅读
相关标签
  

闽ICP备14008679号