当前位置: 首页 > article >正文

STM32拓展 低功耗案例1:睡眠模式 (register)

需求描述

让MCU进入睡眠模式,然后通过串口发送消息来唤醒MCU退出睡眠模式。观察LED在进入休眠模式后是否仍然开启。

思考

首先睡眠模式,唤醒的条件是中断,外部内部都可以,这里的串口接收中断时内部中断。

拓展:中断分为三大类:内核中断也叫异常,片上外设中断对于stem32来说也是内部所以叫内部中断,片外外设的中断stem32外部的中断,叫外部中断

图解:

软件设计:

设计到的寄存器:

ARM内核:

代码:

    /* 1. 设置普通睡眠模式 */
    SCB->SCR &= ~SCB_SCR_SLEEPDEEP;

usart.h

#ifndef __USART_H
#define __USART_H

#include "stm32f10x.h"
#include <stdio.h>

// 初始化
void USART_Init(void);

// 发送一个字符
void USART_SendChar(uint8_t ch);

// 接收一个字符
uint8_t USART_ReceiveChar(void);

// 发送字符串
void USART_SendString(uint8_t * str, uint8_t size);

// 接收字符串
void USART_ReceiveString(uint8_t buffer[], uint8_t *size);

#endif

usart.c

之前的代码新增3.4和4以及中断服务程序。

#include "usart.h"

// 初始化
void USART_Init(void)
{
    // 1. 开启时钟
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

    // 2. GPIO 工作模式
    // 2.1 PA9 - TX,复用推挽输出,CNF = 10,MODE = 11
    GPIOA->CRH |= GPIO_CRH_MODE9;
    GPIOA->CRH |= GPIO_CRH_CNF9_1;
    GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
    // 2.2 PA10 - RX,浮空输入,CNF = 01,MODE = 00
    GPIOA->CRH &= ~GPIO_CRH_MODE10;
    GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
    GPIOA->CRH |= GPIO_CRH_CNF10_0;

    // 3. 串口模块配置
    // 3.1 设置波特率 115200
    USART1->BRR = 0x271;

    // 3.2 使能串口和收发模块
    USART1->CR1 |= USART_CR1_UE;
    USART1->CR1 |= (USART_CR1_TE | USART_CR1_RE);

    // 3.3 配置数据帧的格式
    USART1->CR1 &= ~USART_CR1_M;    // 长度为 8 位
    USART1->CR1 &= ~USART_CR1_PCE;  // 不使用校验位
    USART1->CR2 &= ~USART_CR2_STOP; // 1 位停止位



    // 3.4 开启串口接收中断
    USART1->CR1 |= USART_CR1_RXNEIE;

    // 4. NVIC配置
    NVIC_SetPriorityGrouping(3);
    NVIC_SetPriority(USART1_IRQn, 3);
    NVIC_EnableIRQ(USART1_IRQn);
}

// 发送一个字符
void USART_SendChar(uint8_t ch)
{
    // 判断 TDR 是否为空,必须等待 TDR 为空才能继续发送
    while ((USART1->SR & USART_SR_TXE) == 0)
    {
    }

    // 将要发送的数据写入TDR
    USART1->DR = ch;
}

// 接收一个字符
uint8_t USART_ReceiveChar(void)
{
    // 判断 RDR 是否非空,必须等待 RDR 有数据才能读取出来
    while ((USART1->SR & USART_SR_RXNE) == 0)
    {
    }

    // 读取接收到的数据,返回
    return USART1->DR;
}

// 发送字符串
void USART_SendString(uint8_t *str, uint8_t size)
{
    for (uint8_t i = 0; i < size; i++)
    {
        USART_SendChar(str[i]);
    }
}

// 接收字符串
void USART_ReceiveString(uint8_t buffer[], uint8_t *size)
{
    // 定义变量,保存当前接收到的字符个数
    uint8_t i = 0;

    // 不停地接收字符,直到检测到空闲帧

    // 错误写法:
    // while ( (USART1->SR & USART_SR_IDLE) == 0 )
    // {
    // 	buffer[i] = USART_ReceiveChar();
    // 	i++;
    // }

    // 正确写法:
    // 外层循环:不停读取下一个字符
    while (1)
    {
        // 内层循环:判断当前数据帧是否结束
        while ((USART1->SR & USART_SR_RXNE) == 0)
        {
            // 一旦已经检测到空闲帧,就立刻退出
            if (USART1->SR & USART_SR_IDLE)
            {
                *size = i;
                USART1->DR;
                return;
            }
        }
        buffer[i] = USART1->DR;
        i++;
    }
}

// 重写fputc函数
int fputc(int ch, FILE * file)
{
    USART_SendChar(ch);
    return ch;
}

// 中断服务程序
void USART1_IRQHandler(void)
{
    if (USART1->SR & USART_SR_RXNE)
    {
        // 读取接收到的数据,清除标志位
        uint8_t c = USART1->DR & 0xff;

        USART_SendChar(c);
    }
}

main.c

前面有__的命令,证明时底层的命令,汇编指令。

#include "usart.h"
#include "delay.h"
#include "led.h"

void enter_sleep_mode(void);

int main(void)
{
	// 初始化
	USART_Init();
	LED_Init();

	printf("低功率实验:睡眠模式...\n");

	// 1. 开启LED灯,延时2s,模拟正常程序执行过程
	LED_On(LED_1);
	Delay_s(2);

	while (1)
	{
		// 2. 进入睡眠模式
		printf("正常代码执行完毕,3s后进入睡眠模式...\n");
		Delay_s(3);
		printf("进入睡眠模式");
		enter_sleep_mode();

		// 3. 以下代码只有在唤醒之后才会执行
		printf("从睡眠模式中唤醒...\n");
		Delay_s(2);
	}
}

// 定义进入睡眠模式的函数
void enter_sleep_mode(void)
{
	// 1. 设置普通睡眠模式(默认)
	SCB->SCR &= ~SCB_SCR_SLEEPDEEP;

	// 2. 使用WFI指令,进入睡眠模式
	__WFI();
}

led.h

#ifndef __LED_H
#define __LED_H

#include "stm32f10x.h"

// 宏定义LED灯
#define LED_1 GPIO_ODR_ODR0
#define LED_2 GPIO_ODR_ODR1
#define LED_3 GPIO_ODR_ODR8

// 初始化
void LED_Init(void);

// 开关LED灯
void LED_On(uint16_t led);
void LED_Off(uint16_t led);

// 翻转LED灯状态
void LED_Toggle(uint16_t led);

// 控制所有LED灯的开关
void LED_OnAll(uint16_t leds[], uint8_t size);
void LED_OffAll(uint16_t leds[], uint8_t size);

#endif

led.c

#include "led.h"

// 初始化
void LED_Init(void)
{
    // 1. 时钟配置,打开GPIOA时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

    // 2. 工作模式配置,PA0、PA1、PA8 通用推挽输出,CNF = 00,MODE = 11
    GPIOA->CRL |= GPIO_CRL_MODE0;
    GPIOA->CRL &= ~GPIO_CRL_CNF0;
    GPIOA->CRL |= GPIO_CRL_MODE1;
    GPIOA->CRL &= ~GPIO_CRL_CNF1;
    GPIOA->CRH |= GPIO_CRH_MODE8;
    GPIOA->CRH &= ~GPIO_CRH_CNF8;

    // 3. 初始状态所有引脚输出高电平,关灯
    LED_Off(LED_1);
    LED_Off(LED_2);
    LED_Off(LED_3);
}

// 开关LED灯
void LED_On(uint16_t led)
{
    GPIOA->ODR &= ~led;
}
void LED_Off(uint16_t led)
{
    GPIOA->ODR |= led;
}

// 翻转LED灯状态
void LED_Toggle(uint16_t led)
{
    // 根据IDR对应位的值,判断当前LED状态
    if ((GPIOA->IDR & led) == 0)
    {
        LED_Off(led);
    }
    else
    {
        LED_On(led);
    }
}

// 控制所有LED灯的开关
void LED_OnAll(uint16_t leds[], uint8_t size)
{
    for (uint8_t i = 0; i < size; i++)
    {
        LED_On(leds[i]);
    }
}
void LED_OffAll(uint16_t leds[], uint8_t size)
{
    for (uint8_t i = 0; i < size; i++)
    {
        LED_Off(leds[i]);
    }
}


http://www.kler.cn/a/469143.html

相关文章:

  • DeepSeek重新定义“Open“AI
  • 【读书与思考】历史是一个好东西
  • 深入理解 JavaScript 立即执行函数表达式(IIFE)
  • 每日一题:BM1 反转链表
  • vue Element Ui Upload 上传 点击一个按钮,选择多个文件后直接上传,使用防抖解决多次上传的问题。
  • Ubuntu22.04配置静态ip
  • JavaScript系列(8)-- Array高级操作
  • javaEE-网络编程-3 UDP
  • LabVIEW 实现自动对焦的开发
  • 编译与汇编
  • kubelet状态错误报错
  • linux 逻辑卷挂盘
  • F.interpolate函数
  • [Linux]redis5.0.x升级至7.x完整操作流程
  • 使用MySQL APT源在Linux上安装MySQL
  • spring mvc源码学习笔记之五
  • 【华为OD-E卷 - 九宫格按键输入 100分(python、java、c++、js、c)】
  • Linux系统常用命令详解
  • 怎么找回电脑所有连接过的WiFi密码
  • 【论文阅读笔记】LTX-Video: Realtime Video Latent Diffusion
  • 如何让编码更加高效专注?——程序员编程选明基 RD280U显示器
  • Django Swagger文档库drf-spectacular
  • 【Rust 知识点杂记】
  • 微信小程序提示 miniprogram-recycle-view 引入失败
  • leetcode hot 100 最长递增子序列
  • 智能体语言 Shire 1.2 发布:自定义多文件编辑、Sketch 视图、流式 diff、智能上下文感知...