本篇主要详细介绍MCU的软件结构:
主程序的框架就是:
主要代码就在这里:
#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个周期。
三、休眠和唤醒设置
#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程序。
有需要代码的可以在评论区留下邮箱~