STM32L151低功耗项目笔记(CO传感器TGS5042)

举报
矜辰所致 发表于 2022/09/25 07:51:22 2022/09/25
【摘要】 这是以前做过的一个项目,我们理论不多说,直接上代码,根据代码来记录 1 CO传感器型号为TGS5042,使用STM32L151 ADC采样,计算CO的数值,使用Standby模式。 本文工程源码下载地...
这是以前做过的一个项目,我们理论不多说,直接上代码,根据代码来记录

  
 
  • 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

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。