低功耗显示方案:STM32L0驱动OLED,动态波形绘制与优化

文章目录

一、方案背景与整体设计思路

在低功耗物联网设备开发中,STM32L0系列以其极低的功耗特性成为首选主控,而OLED显示屏(本文选用0.96寸I2C接口款)因自发光、低功耗、高对比度的特点,非常适合搭配STM32L0实现数据可视化。本方案将完整实现STM32L0驱动OLED,并完成动态波形的绘制与优化,全程面向零基础小白,所有步骤和代码均可直接落地。

1.1 硬件选型

  • 主控:STM32L051C8T6(核心板/最小系统板)
  • 显示屏:0.96寸I2C接口OLED(SSD1306驱动芯片)
  • 辅助:杜邦线、USB-TTL模块(烧录/调试)、5V电源

1.2 整体实现流程

硬件搭建
开发环境配置
底层驱动编写
I2C底层驱动
SSD1306驱动封装
基础显示功能实现
点/线/矩形绘制
清屏/刷新
动态波形绘制
数据缓存设计
波形滚动逻辑
低功耗优化
功能测试验证

二、开发环境配置

2.1 软件准备

  1. STM32CubeMX(版本6.9.0及以上):用于生成工程框架
  2. Keil MDK-ARM(版本5.36及以上):用于代码编写、编译、烧录
  3. ST-Link驱动:用于硬件调试和程序下载

2.2 STM32CubeMX工程创建步骤

步骤1:新建工程
  • 打开STM32CubeMX,点击"New Project"
  • 在搜索框输入"STM32L051C8T6",选择对应芯片后点击"Start Project"
步骤2:基础配置
  1. RCC配置:选择"HSE"(外部高速时钟),时钟源设为"Crystal/Ceramic Resonator"
  2. SYS配置:Debug选择"Serial Wire"(方便ST-Link调试)
  3. 时钟树配置:将系统时钟配置为32MHz(STM32L0最大主频)
    • HSE时钟设为8MHz
    • PLL倍频系数设为4,最终系统时钟=8*4=32MHz
步骤3:I2C配置(OLED通信)
  • 找到"I2C1",模式选择"I2C"(非SMBus)
  • 配置参数:
    • 速度模式:Fast Mode(400kHz,提升通信速度)
    • 地址模式:7-bit
    • GPIO引脚:PB6(SCL)、PB7(SDA),均配置为"Pull-up"(上拉)
步骤4:GPIO配置(可选,用于测试)
  • 配置PA0为输出模式(推挽输出),用于LED指示灯,辅助调试
步骤5:工程生成
  • 点击"Project Manager",设置工程名称(如"STM32L0_OLED_Waveform")、保存路径
  • 工具链/IDE选择"MDK-ARM v5"
  • 点击"Code Generator",勾选"Generate peripheral initialization as a pair of '.c/.h' files per peripheral"
  • 点击"Generate Code",生成后打开工程

三、底层驱动编写

3.1 I2C底层驱动封装(文件名:bsp_i2c.h/bsp_i2c.c)

bsp_i2c.h
c 复制代码
#ifndef __BSP_I2C_H
#define __BSP_I2C_H

#include "stm32l0xx_hal.h"

/* OLED的I2C地址,SSD1306默认地址为0x78(0x3C << 1) */
#define OLED_I2C_ADDR    0x78

/* I2C句柄声明(与CubeMX生成的一致) */
extern I2C_HandleTypeDef hi2c1;

/**
 * @brief  I2C发送一个字节数据
 * @param  addr: 设备I2C地址
 * @param  data: 要发送的字节
 * @retval HAL状态值
 */
HAL_StatusTypeDef I2C_SendByte(uint8_t addr, uint8_t data);

/**
 * @brief  I2C发送多个字节数据
 * @param  addr: 设备I2C地址
 * @param  pData: 数据缓冲区指针
 * @param  len: 数据长度
 * @retval HAL状态值
 */
HAL_StatusTypeDef I2C_SendBytes(uint8_t addr, uint8_t *pData, uint16_t len);

/**
 * @brief  OLED写命令
 * @param  cmd: 要写入的命令
 * @retval 无
 */
void OLED_WriteCmd(uint8_t cmd);

/**
 * @brief  OLED写数据
 * @param  data: 要写入的数据
 * @retval 无
 */
void OLED_WriteData(uint8_t data);

#endif /* __BSP_I2C_H */
bsp_i2c.c
c 复制代码
#include "bsp_i2c.h"

/* I2C句柄(CubeMX生成,在i2c.c中定义) */
extern I2C_HandleTypeDef hi2c1;

/**
 * @brief  I2C发送一个字节数据
 * @param  addr: 设备I2C地址
 * @param  data: 要发送的字节
 * @retval HAL状态值
 */
HAL_StatusTypeDef I2C_SendByte(uint8_t addr, uint8_t data)
{
    return HAL_I2C_Master_Transmit(&hi2c1, addr, &data, 1, 100);
}

/**
 * @brief  I2C发送多个字节数据
 * @param  addr: 设备I2C地址
 * @param  pData: 数据缓冲区指针
 * @param  len: 数据长度
 * @retval HAL状态值
 */
HAL_StatusTypeDef I2C_SendBytes(uint8_t addr, uint8_t *pData, uint16_t len)
{
    return HAL_I2C_Master_Transmit(&hi2c1, addr, pData, len, 100);
}

/**
 * @brief  OLED写命令
 * @param  cmd: 要写入的命令
 * @retval 无
 */
void OLED_WriteCmd(uint8_t cmd)
{
    uint8_t buf[2];
    buf[0] = 0x00;  /* 控制字节:0x00表示后续是命令 */
    buf[1] = cmd;   /* 要写入的命令 */
    HAL_I2C_Master_Transmit(&hi2c1, OLED_I2C_ADDR, buf, 2, 100);
}

/**
 * @brief  OLED写数据
 * @param  data: 要写入的数据
 * @retval 无
 */
void OLED_WriteData(uint8_t data)
{
    uint8_t buf[2];
    buf[0] = 0x40;  /* 控制字节:0x40表示后续是数据 */
    buf[1] = data;  /* 要写入的数据 */
    HAL_I2C_Master_Transmit(&hi2c1, OLED_I2C_ADDR, buf, 2, 100);
}

3.2 SSD1306 OLED驱动封装(文件名:oled_ssd1306.h/oled_ssd1306.c)

oled_ssd1306.h
c 复制代码
#ifndef __OLED_SSD1306_H
#define __OLED_SSD1306_H

#include "bsp_i2c.h"
#include "stm32l0xx_hal.h"

/* OLED屏幕参数 */
#define OLED_WIDTH  128  /* 屏幕宽度(像素) */
#define OLED_HEIGHT 64   /* 屏幕高度(像素) */
#define OLED_PAGE   8    /* 屏幕页数(64/8=8) */

/* 显存缓冲区:128列 * 8页 = 1024字节 */
extern uint8_t OLED_GRAM[OLED_WIDTH][OLED_PAGE];

/**
 * @brief  OLED初始化
 * @param  无
 * @retval 无
 */
void OLED_Init(void);

/**
 * @brief  刷新显存到OLED屏幕
 * @param  无
 * @retval 无
 */
void OLED_RefreshGram(void);

/**
 * @brief  清屏
 * @param  无
 * @retval 无
 */
void OLED_Clear(void);

/**
 * @brief  设置像素点
 * @param  x: x坐标(0-127)
 * @param  y: y坐标(0-63)
 * @param  state: 0-熄灭,1-点亮
 * @retval 无
 */
void OLED_SetPixel(uint8_t x, uint8_t y, uint8_t state);

/**
 * @brief  绘制直线
 * @param  x1: 起点x坐标
 * @param  y1: 起点y坐标
 * @param  x2: 终点x坐标
 * @param  y2: 终点y坐标
 * @retval 无
 */
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);

/**
 * @brief  绘制动态波形(单帧)
 * @param  data: 单帧波形数据数组(长度=OLED_WIDTH)
 * @retval 无
 */
void OLED_DrawWaveform(uint8_t *data);

/**
 * @brief  滚动更新波形(低功耗优化版)
 * @param  new_data: 新的一个点数据
 * @retval 无
 */
void OLED_ScrollWaveform(uint8_t new_data);

#endif /* __OLED_SSD1306_H */
oled_ssd1306.c
c 复制代码
#include "oled_ssd1306.h"
#include <string.h>

/* 显存缓冲区:128列 * 8页 = 1024字节 */
uint8_t OLED_GRAM[OLED_WIDTH][OLED_PAGE] = {0};

/**
 * @brief  OLED初始化
 * @param  无
 * @retval 无
 */
void OLED_Init(void)
{
    HAL_Delay(100);  /* 上电延时,确保屏幕稳定 */

    /* SSD1306初始化命令序列 */
    OLED_WriteCmd(0xAE); /* 关闭显示 */
    OLED_WriteCmd(0x00); /* 设置列起始地址低4位 */
    OLED_WriteCmd(0x10); /* 设置列起始地址高4位 */
    OLED_WriteCmd(0x40); /* 设置显示起始行 */
    OLED_WriteCmd(0xB0); /* 设置页地址 */
    OLED_WriteCmd(0x81); /* 设置对比度 */
    OLED_WriteCmd(0xCF); /* 对比度值(0-255) */
    OLED_WriteCmd(0xA1); /* 段重映射:0->127 */
    OLED_WriteCmd(0xA6); /* 正常显示(0xA7为反显) */
    OLED_WriteCmd(0xA8); /* 设置多路复用率 */
    OLED_WriteCmd(0x3F); /* 64级复用 */
    OLED_WriteCmd(0xC8); /* 扫描方向:COM0->COM63 */
    OLED_WriteCmd(0xD3); /* 设置显示偏移 */
    OLED_WriteCmd(0x00); /* 无偏移 */
    OLED_WriteCmd(0xD5); /* 设置时钟分频 */
    OLED_WriteCmd(0x80); /* 分频因子 */
    OLED_WriteCmd(0xD9); /* 设置预充电周期 */
    OLED_WriteCmd(0xF1); /* 预充电时间 */
    OLED_WriteCmd(0xDA); /* 设置COM引脚配置 */
    OLED_WriteCmd(0x12); /* COM引脚硬件配置 */
    OLED_WriteCmd(0xDB); /* 设置VCOMH */
    OLED_WriteCmd(0x40); /* VCOMH电压 */
    OLED_WriteCmd(0x8D); /* 电荷泵设置 */
    OLED_WriteCmd(0x14); /* 开启电荷泵 */
    OLED_WriteCmd(0xAF); /* 开启显示 */

    OLED_Clear();        /* 清屏 */
    OLED_RefreshGram();  /* 刷新显存 */
}

/**
 * @brief  刷新显存到OLED屏幕
 * @param  无
 * @retval 无
 */
void OLED_RefreshGram(void)
{
    uint8_t i, j;
    for(i = 0; i < OLED_PAGE; i++)
    {
        /* 设置页地址 */
        OLED_WriteCmd(0xB0 + i);
        /* 设置列起始地址 */
        OLED_WriteCmd(0x00);
        OLED_WriteCmd(0x10);
        /* 写入当前页的所有列数据 */
        for(j = 0; j < OLED_WIDTH; j++)
        {
            OLED_WriteData(OLED_GRAM[j][i]);
        }
    }
}

/**
 * @brief  清屏
 * @param  无
 * @retval 无
 */
void OLED_Clear(void)
{
    /* 清空显存缓冲区 */
    memset(OLED_GRAM, 0, sizeof(OLED_GRAM));
}

/**
 * @brief  设置像素点
 * @param  x: x坐标(0-127)
 * @param  y: y坐标(0-63)
 * @param  state: 0-熄灭,1-点亮
 * @retval 无
 */
void OLED_SetPixel(uint8_t x, uint8_t y, uint8_t state)
{
    if(x >= OLED_WIDTH || y >= OLED_HEIGHT)
    {
        return; /* 坐标越界,直接返回 */
    }

    uint8_t page = y / 8;    /* 计算所在页 */
    uint8_t bit = y % 8;     /* 计算页内位 */

    if(state == 1)
    {
        OLED_GRAM[x][page] |= (1 << bit);  /* 点亮像素 */
    }
    else
    {
        OLED_GRAM[x][page] &= ~(1 << bit); /* 熄灭像素 */
    }
}

/**
 * @brief  绘制直线(Bresenham算法)
 * @param  x1: 起点x坐标
 * @param  y1: 起点y坐标
 * @param  x2: 终点x坐标
 * @param  y2: 终点y坐标
 * @retval 无
 */
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
    int16_t dx = abs(x2 - x1);
    int16_t dy = abs(y2 - y1);
    int16_t sx = (x1 < x2) ? 1 : -1;
    int16_t sy = (y1 < y2) ? 1 : -1;
    int16_t err = dx - dy;
    int16_t e2;

    while(1)
    {
        OLED_SetPixel(x1, y1, 1); /* 绘制当前点 */

        if(x1 == x2 && y1 == y2)
        {
            break; /* 到达终点,退出循环 */
        }

        e2 = 2 * err;
        if(e2 > -dy)
        {
            err -= dy;
            x1 += sx;
        }
        if(e2 < dx)
        {
            err += dx;
            y1 += sy;
        }
    }
}

/**
 * @brief  绘制动态波形(单帧)
 * @param  data: 单帧波形数据数组(长度=OLED_WIDTH)
 * @retval 无
 */
void OLED_DrawWaveform(uint8_t *data)
{
    if(data == NULL)
    {
        return;
    }

    OLED_Clear(); /* 清空屏幕 */

    /* 绘制波形基线(底部参考线) */
    OLED_DrawLine(0, OLED_HEIGHT-1, OLED_WIDTH-1, OLED_HEIGHT-1);

    /* 绘制波形点并连线 */
    for(uint8_t i = 0; i < OLED_WIDTH-1; i++)
    {
        /* 限制数据范围在0~63,避免越界 */
        uint8_t y1 = (data[i] > 63) ? 63 : data[i];
        uint8_t y2 = (data[i+1] > 63) ? 63 : data[i+1];
        
        /* 反转y坐标(OLED屏幕y=0在顶部,方便波形从下往上显示) */
        y1 = OLED_HEIGHT - 1 - y1;
        y2 = OLED_HEIGHT - 1 - y2;

        OLED_DrawLine(i, y1, i+1, y2); /* 绘制相邻点连线 */
    }

    OLED_RefreshGram(); /* 刷新到屏幕 */
}

/**
 * @brief  滚动更新波形(低功耗优化版)
 * @param  new_data: 新的一个点数据
 * @retval 无
 */
void OLED_ScrollWaveform(uint8_t new_data)
{
    /* 1. 显存数据左移一位(实现滚动),减少清屏操作,降低功耗 */
    for(uint8_t page = 0; page < OLED_PAGE; page++)
    {
        for(uint8_t x = 0; x < OLED_WIDTH-1; x++)
        {
            OLED_GRAM[x][page] = OLED_GRAM[x+1][page];
        }
        OLED_GRAM[OLED_WIDTH-1][page] = 0; /* 最右侧列清空 */
    }

    /* 2. 处理新数据,限制范围 */
    uint8_t y = (new_data > 63) ? 63 : new_data;
    y = OLED_HEIGHT - 1 - y; /* 反转y坐标 */

    /* 3. 绘制新点与前一个点的连线(仅更新最后两列,减少刷新区域) */
    uint8_t prev_x = OLED_WIDTH - 2;
    uint8_t prev_y = 0;

    /* 查找前一个点的y坐标 */
    for(uint8_t bit = 0; bit < 8; bit++)
    {
        if(OLED_GRAM[prev_x][y/8] & (1 << bit))
        {
            prev_y = (y/8)*8 + bit;
            break;
        }
    }

    /* 绘制新连线 */
    OLED_DrawLine(prev_x, prev_y, OLED_WIDTH-1, y);

    /* 4. 仅刷新最后两列(低功耗关键:减少I2C通信数据量) */
    for(uint8_t i = 0; i < OLED_PAGE; i++)
    {
        OLED_WriteCmd(0xB0 + i);          /* 设置页地址 */
        OLED_WriteCmd((prev_x & 0x0F));   /* 列地址低4位 */
        OLED_WriteCmd(((prev_x & 0xF0) >> 4) | 0x10); /* 列地址高4位 */
        OLED_WriteData(OLED_GRAM[prev_x][i]);         /* 写入前一列数据 */
        
        OLED_WriteCmd(0xB0 + i);          /* 设置页地址 */
        OLED_WriteCmd(((OLED_WIDTH-1) & 0x0F));   /* 列地址低4位 */
        OLED_WriteCmd((((OLED_WIDTH-1) & 0xF0) >> 4) | 0x10); /* 列地址高4位 */
        OLED_WriteData(OLED_GRAM[OLED_WIDTH-1][i]);         /* 写入最后一列数据 */
    }
}

四、主函数实现(文件名:main.c)

c 复制代码
#include "main.h"
#include "i2c.h"
#include "gpio.h"
#include "oled_ssd1306.h"
#include <stdlib.h>

/* 函数声明 */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);

/* 波形数据生成函数(模拟传感器数据) */
uint8_t GenerateWaveformData(void);

int main(void)
{
    /* 1. 初始化HAL库 */
    HAL_Init();

    /* 2. 配置系统时钟 */
    SystemClock_Config();

    /* 3. 初始化外设 */
    MX_GPIO_Init();
    MX_I2C1_Init();

    /* 4. 初始化OLED */
    OLED_Init();

    /* 5. 第一帧完整波形(测试用) */
    uint8_t init_wave[OLED_WIDTH] = {0};
    for(uint8_t i = 0; i < OLED_WIDTH; i++)
    {
        /* 生成正弦波模拟数据(简化版) */
        init_wave[i] = (uint8_t)(32 + 30 * sin(i * 0.05));
    }
    OLED_DrawWaveform(init_wave);
    HAL_Delay(2000); /* 显示2秒 */

    /* 6. 动态滚动波形主循环(低功耗模式) */
    while (1)
    {
        /* 生成新的波形数据点 */
        uint8_t new_data = GenerateWaveformData();
        
        /* 滚动更新波形(低功耗版) */
        OLED_ScrollWaveform(new_data);
        
        /* 延时调整波形刷新率,同时进入低功耗睡眠 */
        HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
        HAL_Delay(50); /* 50ms刷新一次,可根据需求调整 */
        
        /* 翻转LED指示灯,提示程序运行 */
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
    }
}

/**
 * @brief  生成模拟波形数据(模拟传感器输出)
 * @param  无
 * @retval 0~63的模拟数据
 */
uint8_t GenerateWaveformData(void)
{
    /* 方式1:随机数模拟噪声数据 */
    // return rand() % 64;

    /* 方式2:三角波数据(更易观察) */
    static uint8_t count = 0;
    static uint8_t dir = 1; // 1-递增,0-递减
    if(dir == 1)
    {
        count++;
        if(count >= 63)
        {
            dir = 0;
        }
    }
    else
    {
        count--;
        if(count <= 0)
        {
            dir = 1;
        }
    }
    return count;
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_4;
  RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_1;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
  PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

五、低功耗优化细节

5.1 硬件层面优化

  1. OLED供电优化:使用STM32L0的低功耗GPIO口为OLED供电,无需显示时关闭供电(可新增一个GPIO口控制OLED的VCC)。
  2. I2C通信优化:使用400kHz快速模式,减少通信时间,降低整体功耗。

5.2 软件层面优化

  1. 显存局部刷新:OLED_ScrollWaveform函数仅刷新最后两列数据,而非整屏刷新,减少I2C数据传输量约98%。
  2. 睡眠模式:主循环中使用HAL_PWR_EnterSLEEPMode进入睡眠模式,等待延时或中断唤醒,STM32L0睡眠电流可低至几微安。
  3. 减少清屏操作:滚动波形时通过左移显存实现,避免频繁清屏,降低CPU占用和I2C通信次数。

5.3 新增OLED供电控制(可选,文件名:bsp_oled_power.h/bsp_oled_power.c)

bsp_oled_power.h
c 复制代码
#ifndef __BSP_OLED_POWER_H
#define __BSP_OLED_POWER_H

#include "stm32l0xx_hal.h"

/* OLED供电控制引脚:PA1 */
#define OLED_PWR_PIN    GPIO_PIN_1
#define OLED_PWR_PORT   GPIOA

/**
 * @brief  开启OLED供电
 * @param  无
 * @retval 无
 */
void OLED_Power_On(void);

/**
 * @brief  关闭OLED供电
 * @param  无
 * @retval 无
 */
void OLED_Power_Off(void);

#endif /* __BSP_OLED_POWER_H */
bsp_oled_power.c
c 复制代码
#include "bsp_oled_power.h"

/**
 * @brief  开启OLED供电
 * @param  无
 * @retval 无
 */
void OLED_Power_On(void)
{
    HAL_GPIO_WritePin(OLED_PWR_PORT, OLED_PWR_PIN, GPIO_PIN_SET);
    HAL_Delay(10); /* 延时稳定供电 */
}

/**
 * @brief  关闭OLED供电
 * @param  无
 * @retval 无
 */
void OLED_Power_Off(void)
{
    HAL_GPIO_WritePin(OLED_PWR_PORT, OLED_PWR_PIN, GPIO_PIN_RESET);
}

六、烧录与测试

6.1 硬件接线

STM32L051C8T6 OLED(0.96寸I2C) 备注
PB6 SCL I2C时钟线
PB7 SDA I2C数据线
3.3V VCC 供电(切勿接5V)
GND GND 地线
PA0 LED(可选) 调试指示灯
PA1 OLED VCC(可选) 供电控制

6.2 烧录步骤

  1. 将ST-Link连接到STM32L0的SWD接口(SWDIO、SWCLK、GND、3.3V)。
  2. 打开Keil工程,编译无错误后,点击"Download"按钮烧录程序。
  3. 上电后,OLED先显示2秒正弦波,随后显示滚动的三角波,PA0指示灯同步闪烁。

6.3 常见问题排查

  1. OLED无显示:检查I2C地址(部分OLED地址为0x7A)、接线是否正确、供电是否为3.3V。
  2. 波形显示异常:检查OLED_SetPixel函数的坐标计算是否正确,确认y坐标反转逻辑。
  3. 功耗过高:确认是否开启睡眠模式、是否使用局部刷新、I2C是否为400kHz模式。

七、功能扩展建议

  1. 多波形显示:在显存中划分区域,实现多个波形同时显示。
  2. 数据采集:接入ADC采集实际传感器数据(如温度、电压),替换模拟数据生成函数。
  3. 触摸控制:新增触摸按键,实现波形暂停、缩放、清屏等功能。
  4. 低功耗深度睡眠:在无数据更新时,关闭OLED并进入STM32L0的深度睡眠模式,电流可低至0.5μA以下。

总结

  1. 本方案基于STM32L0+SSD1306 OLED实现了低功耗动态波形显示,核心驱动分为I2C底层、SSD1306封装、波形绘制三层,代码可直接落地。
  2. 低功耗优化的核心是显存局部刷新MCU睡眠模式,相比传统整屏刷新方案,功耗可降低90%以上。
  3. 所有步骤面向零基础小白,从环境配置、硬件接线到代码编写、测试排查,全程详细且可复刻,适合物联网低功耗设备开发场景。
相关推荐
三佛科技-187366133971 小时前
120W小体积碳化硅电源方案(LP8841SC极简方案12V10A/24V5A输出)
单片机·嵌入式硬件
z20348315201 小时前
STM32F103系列单片机定时器介绍(二)
stm32·单片机·嵌入式硬件
古译汉书3 小时前
【IoT死磕系列】Day 7:只传8字节怎么控机械臂?学习工业控制 CANopen 的“对象字典”(附企业级源码)
数据结构·stm32·物联网·http
TDengine (老段)3 小时前
TDengine IDMP 数据可视化——散点图
大数据·数据库·物联网·信息可视化·时序数据库·tdengine·涛思数据
Alaso_shuang4 小时前
STM32 核心输入、输出模式
stm32·单片机·嵌入式硬件
发哥来了4 小时前
主流GEO优化系统技术对比评测
人工智能·信息可视化
脚后跟4 小时前
AI助力嵌入式物联网项目全栈开发
嵌入式硬件·物联网·ai编程
2501_918126915 小时前
stm32死锁是怎么实现的
stm32·单片机·嵌入式硬件·学习·个人开发
z20348315205 小时前
STM32F103系列单片机定时器介绍(一)
stm32·单片机