STM32
直播中

jsqueh

8年用户 1183经验值
私信 关注
[问答]

如何去实现一个基于STM32F103C8T6+NRF24L01的低功耗无线人体传感器设计呢

如何去实现一个基于STM32F103C8T6+NRF24L01的低功耗无线人体传感器设计呢?

回帖(1)

林明

2021-12-16 15:55:59
简述

使用STM32F103C8T6 、NRF24L01 、红外热释电传感器组成一个低功耗无线人体传感器,系统采用低功耗设计,在STM32停机模式,整机工作电流37uA,可用电池供电。
硬件接口

SPI接口
[tr]STM32引脚NRF24L01[/tr]
PB8NRFIRQ
PB9NRFCS
PB10NRFCE
PB13NRF_SCK
PB14NRF_MISO
PB15NRF_MOSI
红外热释电接口
红外热释电为集成感应探头,使用3.3V供电,输出高电平代表有人
[tr]STM32引脚红外热释电[/tr]
PB11热释电输出IO口
外设使用

[tr]外设作用[/tr]
系统滴答定时器用于软件延时
SPI接口用于驱动NRF24L01
RTC停机模式下的定时唤醒
外部中断停机模式下的中断唤醒
NRF24L01低功耗设计

关于NRF24L01的使用,网上有很多的教程和例程,在这里NRF24L01的SPI驱动使用了原子哥的精英版例程。
针对低功耗设计,只需要修改NRF24L01的配置寄存器的PWR_UP位,1代表上电,0代表掉电。
NRF24L01进入掉电之后,STM32的SPI相关脚位必须都输出高电平,否则会在引脚之间存在漏电。实际测试过用模拟输入方式也会有30左右uA的漏电。
NRF24L01进入掉电模式后,输入电流约为1uA。
STM32F103低功耗设计

STM32F103支持三种低功耗模式,可以查看STM32F103的参考手册





睡眠模式,功耗较高,没有实际测试。
停机模式,在1.8V供电区域的的所有时钟都被停止, PLL、 HSI和
HSE RC振荡器的功能被禁止, SRAM和寄存器内容被保留下来(见参考手册)。"SRAM和寄存器保留"这点个人感觉是和待机模式区别最大的一点,停机模式唤醒之后,从进入停机的指令后继续运行;而待机是程序重新运行,相当于重新上电。
在进入停机模式时,可以选择电压调节器开启或处于低功耗模式。低功耗模式会进一步降低功耗,但会增加下次唤醒的启动时间。可以根据实际情况进行选择。
还有注意的一点,从停机模式唤醒后,系统的时钟为内部时钟,如需切换到外部时钟,需要重新初始化时钟。
待机模式,待机模式是功耗最低的,该模式是在Cortex-M3深睡眠模式时关闭电压调节器。1.8V供电区域被断电。 PLL、 HSI和HSE振荡器也被断电。 SRAM和寄存器内容丢失。换言之,就是端口全部不会保持待机前的状态(除WKUP引脚)
代码

NRF24L01驱动

SPI接口初始化,直接使用原子哥的代码,改了端口和引脚

//初始化24L01的IO口
void NRF24L01_Init(void)
{        
        GPIO_InitTypeDef GPIO_InitStructure;
          SPI_InitTypeDef  SPI_InitStructure;


        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);         //使能PB端口时钟
           
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_9;        //PB9 10推挽                  
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化指定IO
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_8;   
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //PB6 输入  
        GPIO_Init(GPIOB, &GPIO_InitStructure);
        GPIO_ResetBits(GPIOB,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10);//                                         
                 
          SPI2_Init();                    //初始化SPI         
        SPI_Cmd(SPI2, DISABLE); // SPI外设不使能
        SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //SPI设置为双线双向全双工
        SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //SPI主机
          SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //发送接收8位帧结构
        SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                //时钟悬空低
        SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;        //数据捕获于第1个时钟沿
        SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由软件控制
        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;                //定义波特率预分频的值:波特率预分频值为16
        SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //数据传输从MSB位开始
        SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式
        SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器

        SPI_Cmd(SPI2, ENABLE); //使能SPI外设
                         
        NRF24L01_CE=0;                         //使能24L01
        NRF24L01_CSN=1;                        //SPI片选取消                            
}


NRF24L01进入发送模式


//该函数初始化NRF24L01到TX模式
//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和LNA HCURR
//PWR_UP,CRC使能
//当CE变高后,即进入RX模式,并可以接收数据了                  
//CE为高大于10us,则启动发送.         
void NRF24L01_TX_Mode(void)
{                                                                                                                 
        NRF24L01_CE=0;            
        NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,发送模式,开启所有中断
          NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址
          NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK          


          NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答   
          NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
          NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
          NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40);       //设置RF通道为40
        NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   
         
        NRF24L01_CE=1;//CE为高,10us后启动发送
}


外部中断和RTC中断配置
//PB11外部中断  RTC闹钟中断
void WKUP_Init(void)
{       
          GPIO_InitTypeDef  GPIO_InitStructure;                    
        NVIC_InitTypeDef NVIC_InitStructure;
        EXTI_InitTypeDef EXTI_InitStructure;


        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
       
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);//使能GPIOB和复用功能时钟


        GPIO_InitStructure.GPIO_Pin =GPIO_Pin_11;         //
        GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPD;//
        GPIO_Init(GPIOB, &GPIO_InitStructure);        //初始化IO
  //使用外部中断方式
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11);        //


          EXTI_InitStructure.EXTI_Line = EXTI_Line11;        //设置按键所有的外部线路
        EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;                        //设外外部中断模式:EXTI线路为中断请求
        EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;  //上升沿触发
        EXTI_InitStructure.EXTI_LineCmd = ENABLE;
        EXTI_Init(&EXTI_InitStructure);        // 初始化外部中断


        NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //使能按键所在的外部中断通道
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级2级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级2级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
        NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器


        //RTC中断配置
  /* 2 bits for Preemption Priority and 2 bits for Sub Priority */
        /* Configure EXTI Line17(RTC Alarm) to generate an interrupt on rising edge */
        EXTI_ClearITPendingBit(EXTI_Line17);
        EXTI_InitStructure.EXTI_Line = EXTI_Line17;
        EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
        EXTI_InitStructure.EXTI_LineCmd = ENABLE;
        EXTI_Init(&EXTI_InitStructure);
  
        NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
}


停机模式唤醒后系统时钟为内部时钟,需要切换到外部时钟可以直接调用这个函数。这函数直接拷贝STM32库函数里PWR的例程。


/**
  * @brief  Configures system clock after wake-up from STOP: enable HSE, PLL
  *         and select PLL as system clock source.
  * @param  None
  * @retval None
  */
void SYSCLKConfig_STOP(void)
{
  /* Enable HSE */
  RCC_HSEConfig(RCC_HSE_ON);


  /* Wait till HSE is ready */
  HSEStartUpStatus = RCC_WaitForHSEStartUp();


  if(HSEStartUpStatus == SUCCESS)
  {


#ifdef STM32F10X_CL
    /* Enable PLL2 */
    RCC_PLL2Cmd(ENABLE);


    /* Wait till PLL2 is ready */
    while(RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET)
    {
    }


#endif


    /* Enable PLL */
    RCC_PLLCmd(ENABLE);


    /* Wait till PLL is ready */
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }


    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);


    /* Wait till PLL is used as system clock source */
    while(RCC_GetSYSCLKSource() != 0x08)
    {
    }
  }
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分