STM32L151低功耗项目笔记(CO传感器TGS5042)
这是以前做过的一个项目,我们理论不多说,直接上代码,根据代码来记录
- 1
CO传感器型号为TGS5042,使用STM32L151 ADC采样,计算CO的数值,使用Standby模式。
main.c
/**
******************************************************************************
* @file L151test.c
* @author QZH
* @version V1.1.0
* @date 16-September-2020
* @brief
******************************************************************************
* @attention
* for bluesensor : lower, DHT21
* RTC wake up
* stop 模式不会断,唤醒从睡眠的地方
* standby 模式,类似于重启
*
******************************************************************************
* @attention
* 2020/12/17
* CO传感器,使用L151的ADC直接测量电压
* 不需要温湿度
* 蓝牙模块为上次使用版本,驱动可以不用改变太大
*
* 2021/1/21
* 1、添加CO计算函数;
* 2、ADC数值处理在蓝牙模块唤醒等待的1s中内进行;
* 3、实际测试,唤醒工作时间电流约 5.6~ 5.8 mA (程序1300ms左右,实测2S,估算电量可以细算) ,休眠电流约 6uA,实际休眠电流约 4uA ~ 5uA,开始测试6uA 是因为连接了串口;
* 4、初步设置,普通周期 为 5min ,300s 一次, CO > 40ppm 变成10s 一次;
* 5、后续有需要可以 使用后备寄存器 存储检测数据,可以做到检测一定次数再发送;
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32l1xx.h"
#include "delay.h"
#include "gpio.h"
#include "sys.h"
#include "stm32l1xx_clock_config.h"
#include "usart.h"
#include "rtc.h"
#include "blue_common.h"
#include "adc.h"
#include "co.h"
/** @addtogroup STM32L1xx_StdPeriph_Examples
* @{
*/
u8 sendtime = 0;
/**
* @brief Main program
* @param None
* @retval None
*/
int main(void)
{
/*
到main()函数这里 SystemInit()已经初始化了,在学习startup_xxxx.s文件里提到过,会调用
很早版本的固件库 需要用户自己 SystemInit();
*/
u16 normal_cycle = 300;
u16 quick_cycle = 10;
/*
把系统时钟放低
时钟频率低,功耗低,但是相应的系统速度也肯定慢下来了
*/
SetHCLKTo8();
/*
根据系统时钟初始化延时函数
*/
delay_init(8);
/*
外设: 依次是串口,ADC,蓝牙模块控制IO
*/
My_USARTx_Config(USART1,9600);
My_USARTx_Config(USART3,9600);
Adc_init();
blue_control_init();
printf("main start!\r\n");
#ifdef TEST
blue_slp_on;
#endif
while(1)
{
/*芯片唤醒就开始执行自己想做的操作,读取ADC,蓝牙模块发送消息,都在这个函数里面*/
blue_setcmd();
printf("300ms time for send message...\r\n");
delay_ms(300); //这个时间得留给蓝牙发送报文
/*根据传感器的数据设置周期,就是RTC唤醒的时间 RTC_Config*/
if(sendtime == 0){
printf("into normal_cycle lowerpower mode!\r\n");
RTC_Config(normal_cycle);
}
else if(sendtime == 1){
printf("into quick_cycle lowerpower mode!\r\n");
RTC_Config(quick_cycle);
}
/*使能自动唤醒功能 */
printf("open wakeup!\r\n");
RTC_WakeUpCmd(ENABLE);
printf("gpio pin set to lowerpower!\r\n");
/*进入低功耗前,记得把所有的IO口设置为模拟输入*/
gpio_lowpower();
printf("get in standby for 5 s!\r\n");
Sys_Standby();
printf("Standby model won,t go to this place!\r\n");
RTC_WakeUpCmd(DISABLE);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
唤醒函数RTC_Config
其中有一个关键函数RTC_Config
,这个函数的实现ST官方包括网上的例子也很多,当初也是参考了不少demo:
#include "rtc.h"
#define LSE_STARTUP_TIMEOUT_Num 0xFFFFF
void RTC_Config(uint32_t wakeuptime_s)
{
__IO uint32_t lse_waittime = 0;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
/* Enable the PWR clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
/*!< Allow access to RTC 使能后备寄存器访问*/
PWR_RTCAccessCmd(ENABLE);
//RESET RTC Domain
RCC_RTCResetCmd(ENABLE);
RCC_RTCResetCmd(DISABLE);
//LSE enable 32.768k
RCC_LSEConfig(RCC_LSE_ON);
//wait till LSE is ready
while((RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)&&(lse_waittime<LSE_STARTUP_TIMEOUT_Num))
{lse_waittime++;}
/*
这里有一段代码是如果外部低速晶振出了问题采用的,用下面这段需要把上面的while语句修改一下
ex1:
if (timeout == 0) {
RCC_LSICmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
} else {
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
}
ex2: //ex2 就是按照上面while一致的方式来进行的
if(lse_waittime>=LSE_STARTUP_TIMEOUT_Num){
XTUpCounter = 0;
RCC_LSICmd(ENABLE);
while((RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)&&(lse_waittime<LSE_STARTUP_TIMEOUT_Num))
{lse_waittime++;}
if(XTUpCounter<HSE_STARTUP_TIMEOUT_Num)
{
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); //选择内部晶振作为RTC时钟源
RTCStateFlag = 2;
}
else
{
RTCStateFlag = 0;
}
}
else
{
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//选择外部晶振作为RTC时钟源
RTCStateFlag = 1;
}
*/
//rtc clock select
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
//enable rtc clock
RCC_RTCCLKCmd(ENABLE);
// wait for rtc APB registers synchronisation
RTC_WaitForSynchro();
/*
这里估计可以在后背寄存器保存一定的数据,但是目前这里没有用到
// Configure the RTC data register and RTC prescaler
从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
if (RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x32F2) {
RTC_InitStructure.RTC_AsynchPrediv = 0x7F;
RTC_InitStructure.RTC_SynchPrediv = 0xFF;
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;
RTC_Init(&RTC_InitStructure);
RTC_WriteBackupRegister(RTC_BKP_DR0, 0x32F2);
}
RTC_WaitForSynchro();
*/
/*
//配置RTC数据寄存器以及时钟分频
RTC_InitStructure.RTC_AsynchPrediv = 0X7F;//同步 设置预分频为1S一次
RTC_InitStructure.RTC_SynchPrediv = 0XFF; //异步 255
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;//24小时制
//检查RTC初始化
if(RTC_Init(&RTC_InitStructure) == ERROR)
{
while(1);
}
//Set the date: Thursday January 11th 2018,2018/01/25 星期四
RTC_DateStruct.RTC_Year = 0x18;
RTC_DateStruct.RTC_Month = RTC_Month_January;
RTC_DateStruct.RTC_Date = 0x27;
RTC_DateStruct.RTC_WeekDay = RTC_Weekday_Thursday;
RTC_SetDate(RTC_Format_BCD, &RTC_DateStruct);
//Set the time to 10h 09mn 15s AM ,早上10:09:15
RTC_TimeStructure.RTC_H12 = RTC_H12_PM;
RTC_TimeStructure.RTC_Hours = 0x21;
RTC_TimeStructure.RTC_Minutes = 0x57;
RTC_TimeStructure.RTC_Seconds = 0x33;
RTC_SetTime(RTC_Format_BCD, &RTC_TimeStructure);
*/
//EXIT Config
EXTI_ClearITPendingBit(EXTI_Line20);
EXTI_InitStructure.EXTI_Line = EXTI_Line20;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
//enable the rtc wakeup interrupt
NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//先关闭TRC中断使能
// RTC_WakeUpCmd(DISABLE);
//rtc wakeup interrupt generation:clock source:RTCDiv_16,
//wakeup time base:4s
RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits);
RTC_SetWakeUpCounter(wakeuptime_s);
//清除RTC唤醒中断标志
RTC_ClearFlag(RTC_FLAG_WUTF);
//enable the wakeup interrupt
RTC_ITConfig(RTC_IT_WUT, ENABLE);
}
void RTC_WKUP_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_WUT) != RESET)
{
RTC_ClearITPendingBit(RTC_IT_WUT);
EXTI_ClearITPendingBit(EXTI_Line20);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
除了上面的rtc.c
,当初测试的时候,也针对 系统时钟频率,IO口,ADC配置也进行了测试:
时钟频率
我为什么没有设置更高,更高虽然功耗大,但是运行时间短,相对的功耗不见得比低频率高,这个得看自己的实际需求,不能一概而论。我是直接设置成 8M:
/*
把系统时钟放低
时钟频率低,功耗低,但是相应的系统速度也肯定慢下来了
*/
SetHCLKTo8();
/*
根据系统时钟初始化延时函数
*/
delay_init(8);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
IO口
IO口用到的不多,最后休眠前有一个gpio_lowpower()
函数如下:
void gpio_lowpower(void){
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6|GPIO_Pin_9|GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
ADC
ADC配置其实和F103一样,固件库的配置都是一样的,这个也是从别的地方复制一下,这个配置其实网上很多,我记得我最初看到还是在正点原子的F103系列教程demo里面看到的,基本就是一样:
#include "adc.h"
void Adc_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
/*----------------- ADC1 configuration with DMA enabled --------------------*/
/* Enable the HSI oscillator */
RCC_HSICmd(ENABLE);
/* Enable GPIOA clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
/* Configure PA.1 (ADC Channe1) in analog mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Check that HSI oscillator is ready */
while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div1;
ADC_CommonInit(&ADC_CommonInitStructure);
/* Enable ADC1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* ADC1 configuration */
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //需要连续读10次,所以开启
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_TempSensorVrefintCmd(ENABLE);
/* ADC1 regular channel18 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_48Cycles);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Wait until the ADC1 is ready */
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS) == RESET)
{
}
ADC_SoftwareStartConv(ADC1);
}
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_48Cycles ); //ADC1,ADC通道,采样时间为239.5周期
ADC_SoftwareStartConv(ADC1); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
TGS5042传感器
TGS5042解析代码在这里就不贴出来,因为如果做其他产品这个部分也没有必要知道,我自己也只是为了做个记录,国外的传感器,价格比较贵,而且这个传感器有些年头了,传感器的更新还是比较快的,估计以后也用得少。
蓝牙模块
蓝牙模块使用的是 深圳云里物里 的 MS50SFB 模块,是nRF52832的内核,因为是别人的固件,所以产品在蓝牙模块通讯处理部分还是有改进空间的,可以自己使用 nRF52832 模块进行设计,然后程序执行的时间,功耗,可以得到进一步控制。
产品最终
唤醒工作时间电流约 5.6~ 5.8 mA ,2s左右。
产品实际运行时间2S,主要是因为和蓝牙模块的通讯 和 预留发送蓝牙报文的时间,一般至少预留蓝牙模块发送 2个周期以上的报文,以确保产品周期播报的稳定性,这也是根据自己的实际需求,如果休眠时间过长,可以多放点周期,如果本来周期就短,唤醒工作时间可以缩短 。
休眠电流约 4uA ~ 5uA。(以上电流是在3.6V电池供电环境下)
文章来源: blog.csdn.net,作者:矜辰所致,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_42328389/article/details/120703262
- 点赞
- 收藏
- 关注作者
评论(0)