NB-IOT智慧花盆(二)

本篇主要详细介绍MCU的软件结构:

先上一张工程图
NB-IOT智慧花盆(二)_第1张图片

主程序的框架就是:

NB-IOT智慧花盆(二)_第2张图片

主要代码就在这里:

#include "stm8l15x.h"
#include 
#include  
#include "WaterPump.h"
#include "Hygrometer.h"
#include "FlowMeter.h"
#include "BC35G.h"
#include "Wakeup.h"
#include "LED.h"
/*******************************************************************************
**函数名称:void delay(unsigned int ms)     Name: void delay(unsigned int ms)
**功能描述:大概延时
**入口参数:unsigned int ms   输入大概延时数值
**输出:无
*******************************************************************************/
void delay(unsigned int ms)
{
  unsigned int x , y;
  for(x = ms; x > 0; x--)           /*  通过一定周期循环进行延时*/
    for(y = 1000 ; y > 0 ; y--);
}
void main ( void )
{
    u8 Numb = 0;
    u16 Hygrometer_Value = 0;
    u16 TotalFlow_Value = 0;
    u16 Hygrometer_Value_Last = Hygrometer_Value;
    u8 buff[60];
    CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1); //内部时钟为1分频 = 16Mhz 
    enableInterrupts();   //使能系统总中断
    WaterPump_Init();
    FlowMeter_Init();
    Hygrometer_Init();
    Wakeup_Config();
    delay(20000);//延迟为了复用下载IO口
    //LED_Init();
    BC35G_Init(9600);
    //USART1_Config(9600);
    while(1)
    {
      delay(1000);
      Hygrometer_Value = Hygrometer_GetValue();
      if(Hygrometer_Value<42)
      {
        WaterPumpSwitch(1);
        while(Hygrometer_Value = Hygrometer_GetValue(),Hygrometer_Value<50){}
        WaterPumpSwitch(0);
        TotalFlow_Value += FlowMeter_GetCounter();
      }
      Hygrometer_Value_Last = Hygrometer_Value;
      //SignalQuality = NB_Get_CSQ();
      //发送数据
      Numb++;
      sprintf(buff,"AT+QLWULDATAEX=13,%u%u%u%u20%u%u%u%u%u20%u%u,0x0101\r\n",\
              Hygrometer_Value_Last/1000%10+30,Hygrometer_Value_Last/100%10+30,Hygrometer_Value_Last/10%10+30,Hygrometer_Value_Last/1%10+30,\
              TotalFlow_Value/10000%10+30,TotalFlow_Value/1000%10+30,TotalFlow_Value/100%10+30,TotalFlow_Value/10%10+30,TotalFlow_Value/1%10+30,\
              Numb/10%10+30,Numb/1%10+30);
      do{
          while(1!=NB_SendCMD(buff,1,1000)){}
      }while(1!=NB_GetMsgKeyWord(BC35G_Buff,"+QLWULDATASTATUS:",2000,1,NULL,0,0));
      
      Sleep();//休眠一个小时
    }

}

代码不是特别多,我这边就挑一些需要注意的代码出来说明。

一、ADC

#include "ADC.h"
void ADC1_Config()
{
    GPIO_Init(GPIOC, GPIO_Pin_4, GPIO_Mode_In_FL_No_IT);
    //SYSCFG_REMAPPinConfig(REMAP_Pin_TIM2TRIGPortA,ENABLE);
    ADC_DeInit(ADC1);
    CLK_PeripheralClockConfig(CLK_Peripheral_ADC1 , ENABLE);              //使能ADC1时钟
    ADC_Init(ADC1,
           ADC_ConversionMode_Single,   //单次转换模式
           ADC_Resolution_12Bit,        //12位精度转换械
           ADC_Prescaler_1              //时钟设置为1分频
             );  
    ADC_SamplingTimeConfig(ADC1 ,
                         ADC_Group_SlowChannels , 
                         ADC_SamplingTime_384Cycles
                         );
    ADC_SchmittTriggerConfig(ADC1 , 
                          ADC_Channel_4 ,
                          ENABLE
                          );
    ADC_ChannelCmd(ADC1,
                 ADC_Channel_4,         //设置为通道18进行采样
                 ENABLE);
  
   
    ADC_Cmd(ADC1 , ENABLE);               //使能ADC 
}

解释一下为什么ADC采样时间要设置384个周期:

ADC时钟配了16MHZ,根据datasheet上面表明了采样最小时间和采样电阻的关系,因为采样电阻用了47K的,所以采样时间配置了384个周期。

NB-IOT智慧花盆(二)_第3张图片

 

三、休眠和唤醒设置

#include "Wakeup.h"
void Wakeup_Config(void)
{
   RTC_Config();
}
void WakeUp_Init(void)
{       
   RTC_ITConfig(RTC_IT_WUT, DISABLE);
   RTC_WakeUpCmd(DISABLE);
   CLK_HSICmd(ENABLE);                     //打开芯片内部的HSI
   while(CLK_GetFlagStatus(CLK_FLAG_HSIRDY) == RESET);// 等待振荡器稳定
   CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1); //内部时钟为1分频 = 16Mhz 
   enableInterrupts();   //使能系统总中断
   FlowMeter_Init();
   Hygrometer_Init();
   WaterPump_Init();
   BC35G_Init(9600);
}
void Sleep_CLK_Disable(void)
{
   CLK_PeripheralClockConfig(CLK_Peripheral_TIM2, DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_TIM3, DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_TIM4, DISABLE);

   CLK_PeripheralClockConfig(CLK_Peripheral_I2C1,   DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_SPI1,   DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_USART1, DISABLE);

   CLK_PeripheralClockConfig(CLK_Peripheral_BEEP, DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_DAC,  DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_ADC1, DISABLE);

   CLK_PeripheralClockConfig(CLK_Peripheral_TIM1, DISABLE);
   //CLK_PeripheralClockConfig(CLK_Peripheral_RTC, DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_LCD, DISABLE);

   CLK_PeripheralClockConfig(CLK_Peripheral_DMA1, DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_COMP, DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_BOOTROM, DISABLE);

   CLK_PeripheralClockConfig(CLK_Peripheral_AES, DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_TIM5, DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_SPI2, DISABLE);

   CLK_PeripheralClockConfig(CLK_Peripheral_USART2, DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_USART3, DISABLE);
   CLK_PeripheralClockConfig(CLK_Peripheral_CSSLSE, DISABLE);
}
void Sleep_Peripheral_DeInit(void)
{
  ADC_DeInit(ADC1);
  DMA_GlobalDeInit();
  DMA_DeInit(DMA1_Channel1);
  DMA_DeInit(DMA1_Channel2);
  USART_DeInit(USART1);
  TIM2_DeInit();
  TIM3_DeInit();
}
void Sleep_GPIO_SetLevel(void)
{
  GPIO_WriteBit(GPIOB , GPIO_Pin_3,RESET);//流量计引脚
  GPIO_Init(GPIOC, GPIO_Pin_4, GPIO_Mode_In_FL_No_IT);//湿度值ADC输入
  WaterPumpSwitch(0);
  Hygrometer_FlowMeter_Switch(0);
  GPIO_WriteBit(GPIOA , GPIO_Pin_2,SET);//串口发送
  GPIO_Init(GPIOA, GPIO_Pin_3, GPIO_Mode_Out_PP_High_Slow);//串口接收引脚
}
void Sleep(void)
{
  u8 i;
  Sleep_Peripheral_DeInit();//DeInit外设;
  Sleep_CLK_Disable();//关闭外设时钟;
  Sleep_GPIO_SetLevel();//逐个配置GPIO的电平
  CLK_HaltConfig(CLK_Halt_FastWakeup,ENABLE);   //快速唤醒后时钟为HSI
  PWR_FastWakeUpCmd(ENABLE);                    //开启电源管理里的快速唤醒
  PWR_UltraLowPowerCmd(ENABLE);                 //使能电源的低功耗模式(这句话的作用很大)
  RTC_ITConfig(RTC_IT_WUT, ENABLE);             //使能RTC中断
  RTC_WakeUpCmd(ENABLE);
  CLK_HSICmd(DISABLE);                          //关闭内部高速时钟(之前内核是工作在HSI下的)
  //sim();                                      //关闭所有中断,手册上说最好关闭所有中断在进入停机模式,但是好像这条指令并不会关闭RTC中断,我也没用这行代码
  for(i = 0;i<65;i++)//一个小时唤醒一次
  {
    halt();                                       //到这一步执行完后,内核时钟就停止了,进入停机模式
  }
  WakeUp_Init();                                //rtc唤醒后PC指针在这里,从这里开始执行,所以我们要在这里添加初始化各种外设和外部芯片的代码
}

在低功耗运行中尽量所有耗电的芯片外设、以及外围电路的芯片或模块都关闭。

这里特别需要注意的是先要复位外设,再关闭外设时钟,然后再把IO口的设置好。具体可以参考这一篇STM8L的低功耗模式的配置。

在低功耗休眠过程中,RTC时钟约每隔55.187S就唤醒一次MCU,这是在LSI下最长的休眠周期。所以连续休眠65次,大约是1个小时(3587.15S)后开始工作。

四、上位机软件

最后我还用php搭建了一个服务器,服务器通过华为云间接和花盆通信,但是最近比较忙,所以没什么时间来写上位机的详细说明,先给大家说一下要用到的软件:

1.花生壳

通过花生壳的内网穿透功能,就可以使自家电脑成为一个服务器与外网进行数据通信。

2.phpstudy

通过apache服务器来运行写好的php程序,华为平台有服务器的Demo可以来借鉴参考,这个放到后面详细说明。

https://www.sunweihu.com/2569.html这篇文章解释了花生壳的内网穿透设置,以及用phpstudy来配置环境运行php程序。

有需要代码的可以在评论区留下邮箱~

 

你可能感兴趣的:(NB-IOT智慧花盆(二))