文章目录
-
- 一、方案背景与整体设计思路
-
- [1.1 硬件选型](#1.1 硬件选型)
- [1.2 整体实现流程](#1.2 整体实现流程)
- 二、开发环境配置
-
- [2.1 软件准备](#2.1 软件准备)
- [2.2 STM32CubeMX工程创建步骤](#2.2 STM32CubeMX工程创建步骤)
- 三、底层驱动编写
-
- [3.1 I2C底层驱动封装(文件名:bsp_i2c.h/bsp_i2c.c)](#3.1 I2C底层驱动封装(文件名:bsp_i2c.h/bsp_i2c.c))
- [3.2 SSD1306 OLED驱动封装(文件名:oled_ssd1306.h/oled_ssd1306.c)](#3.2 SSD1306 OLED驱动封装(文件名:oled_ssd1306.h/oled_ssd1306.c))
- 四、主函数实现(文件名:main.c)
- 五、低功耗优化细节
-
- [5.1 硬件层面优化](#5.1 硬件层面优化)
- [5.2 软件层面优化](#5.2 软件层面优化)
- [5.3 新增OLED供电控制(可选,文件名:bsp_oled_power.h/bsp_oled_power.c)](#5.3 新增OLED供电控制(可选,文件名:bsp_oled_power.h/bsp_oled_power.c))
- 六、烧录与测试
-
- [6.1 硬件接线](#6.1 硬件接线)
- [6.2 烧录步骤](#6.2 烧录步骤)
- [6.3 常见问题排查](#6.3 常见问题排查)
- 七、功能扩展建议
一、方案背景与整体设计思路
在低功耗物联网设备开发中,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 软件准备
- STM32CubeMX(版本6.9.0及以上):用于生成工程框架
- Keil MDK-ARM(版本5.36及以上):用于代码编写、编译、烧录
- ST-Link驱动:用于硬件调试和程序下载
2.2 STM32CubeMX工程创建步骤
步骤1:新建工程
- 打开STM32CubeMX,点击"New Project"
- 在搜索框输入"STM32L051C8T6",选择对应芯片后点击"Start Project"
步骤2:基础配置
- RCC配置:选择"HSE"(外部高速时钟),时钟源设为"Crystal/Ceramic Resonator"
- SYS配置:Debug选择"Serial Wire"(方便ST-Link调试)
- 时钟树配置:将系统时钟配置为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 硬件层面优化
- OLED供电优化:使用STM32L0的低功耗GPIO口为OLED供电,无需显示时关闭供电(可新增一个GPIO口控制OLED的VCC)。
- I2C通信优化:使用400kHz快速模式,减少通信时间,降低整体功耗。
5.2 软件层面优化
- 显存局部刷新:
OLED_ScrollWaveform函数仅刷新最后两列数据,而非整屏刷新,减少I2C数据传输量约98%。 - 睡眠模式:主循环中使用
HAL_PWR_EnterSLEEPMode进入睡眠模式,等待延时或中断唤醒,STM32L0睡眠电流可低至几微安。 - 减少清屏操作:滚动波形时通过左移显存实现,避免频繁清屏,降低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 烧录步骤
- 将ST-Link连接到STM32L0的SWD接口(SWDIO、SWCLK、GND、3.3V)。
- 打开Keil工程,编译无错误后,点击"Download"按钮烧录程序。
- 上电后,OLED先显示2秒正弦波,随后显示滚动的三角波,PA0指示灯同步闪烁。
6.3 常见问题排查
- OLED无显示:检查I2C地址(部分OLED地址为0x7A)、接线是否正确、供电是否为3.3V。
- 波形显示异常:检查
OLED_SetPixel函数的坐标计算是否正确,确认y坐标反转逻辑。 - 功耗过高:确认是否开启睡眠模式、是否使用局部刷新、I2C是否为400kHz模式。
七、功能扩展建议
- 多波形显示:在显存中划分区域,实现多个波形同时显示。
- 数据采集:接入ADC采集实际传感器数据(如温度、电压),替换模拟数据生成函数。
- 触摸控制:新增触摸按键,实现波形暂停、缩放、清屏等功能。
- 低功耗深度睡眠:在无数据更新时,关闭OLED并进入STM32L0的深度睡眠模式,电流可低至0.5μA以下。
总结
- 本方案基于STM32L0+SSD1306 OLED实现了低功耗动态波形显示,核心驱动分为I2C底层、SSD1306封装、波形绘制三层,代码可直接落地。
- 低功耗优化的核心是显存局部刷新 和MCU睡眠模式,相比传统整屏刷新方案,功耗可降低90%以上。
- 所有步骤面向零基础小白,从环境配置、硬件接线到代码编写、测试排查,全程详细且可复刻,适合物联网低功耗设备开发场景。