【STM32H5 评测】ADC 电压表
本文介绍了 NUCLEO-H5E5ZJ 开发板驱动 OLED 显示 ADC 电压和 MCU 温度的项目设计,包括硬件连接、环境搭建、工程创建、参数配置、ADC 打印、OLED显示等流程。

项目介绍
NUCLEO-C562RE 开发板驱动 OLED 显示。
- 准备工作:硬件连接、环境搭建;
- 工程测试:工程创建、工程配置、工程代码、编译上传、效果演示。
硬件连接
根据原理图,选择 PF5 和 PG6 作为 I2C4 的通信引脚。

接线方式如下
| STM32H5E5ZJT6 |
OLED |
| PF5 (SCL) |
SCL |
| PG6 (SDA) |
SDA |
| GND |
GND |
| 3V3 |
VCC |
实物图

工程配置
-
打开 STM32CubeMX 软件,从开发板新建工程;
-
进入 Pinout & Configuration 标签页,展开 Connectivity ,使能 I2C4,标准速率 100kHz,7 位地址 0x3c ;

-
SCL 和 SDA 引脚默认配置,均设置为上拉输出模式;
-
进入 Pinout & Configuration 标签页,展开 Analog ,点击 ADC3,模式页面下勾选 Temperature Sensor Channel ,参数配置调整采样时间为最大,即 640.5 个周期,以确保读数精度;

-
进入 ADC1 模式配置,根据原理图可知板载 A0 引脚对应 PA6,因此使能 IN3,参数配置将采样时间调整至最大;

-
保存配置,生成 CMake工程;
ADC
使用 VS Code 打开 CubeMX 生成的工程文件夹;
点击左侧工具栏的运行调试按钮,生成 lanch.json 调试器配置文件;
点击下方工具栏的 生成 按钮,编译工程,确认无报错;

配置右侧 AI 助手侧边栏,使用 GitHub Copilot 便于代码生成。
代码
VS Code 加载工程,打开 ./Core/Src/main.c 文件,添加如下代码
#include "main.h"
/* USER CODE BEGIN PD */
#define ADC_VREF_MV 3300U
#define ADC_MAX_VALUE 4095U
#define ADC_POLL_TIMEOUT 100U
/* USER CODE END PD */
/* Private variables ------------*/
COM_InitTypeDef BspCOMInit;
ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc3;
I2C_HandleTypeDef hi2c4;
/* Private function prototypes -------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
static void MX_GPIO_Init(void);
static void MX_ICACHE_Init(void);
static void MX_ADC1_Init(void);
static void MX_ADC3_Init(void);
static void MX_I2C4_Init(void);
int main(void)
{
MPU_Config();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ICACHE_Init();
MX_ADC1_Init();
MX_ADC3_Init();
MX_I2C4_Init();
/* USER CODE BEGIN 2 */
if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK)
{
Error_Handler();
}
if (HAL_ADCEx_Calibration_Start(&hadc3, ADC_SINGLE_ENDED) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END 2 */
/* Initialize leds */
BSP_LED_Init(LED_GREEN);
BSP_LED_Init(LED_RED);
BSP_LED_Init(LED_BLUE);
/* Initialize USER push-button, will be used to trigger an interrupt each time it's pressed.*/
BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
/* Initialize COM1 port (115200, 8 bits (7-bit data + 1 stop bit), no parity */
BspCOMInit.BaudRate = 115200;
BspCOMInit.WordLength = COM_WORDLENGTH_8B;
BspCOMInit.StopBits = COM_STOPBITS_1;
BspCOMInit.Parity = COM_PARITY_NONE;
BspCOMInit.HwFlowCtl = COM_HWCONTROL_NONE;
if (BSP_COM_Init(COM1, &BspCOMInit) != BSP_ERROR_NONE)
{
Error_Handler();
}
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
uint32_t adcValue;
uint32_t temperatureAdcValue;
uint32_t voltageMv;
int32_t temperatureC;
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if (HAL_ADC_Start(&hadc1) != HAL_OK)
{
Error_Handler();
}
if (HAL_ADC_PollForConversion(&hadc1, ADC_POLL_TIMEOUT) != HAL_OK)
{
Error_Handler();
}
adcValue = HAL_ADC_GetValue(&hadc1);
if (HAL_ADC_Stop(&hadc1) != HAL_OK)
{
Error_Handler();
}
if (HAL_ADC_Start(&hadc3) != HAL_OK)
{
Error_Handler();
}
if (HAL_ADC_PollForConversion(&hadc3, ADC_POLL_TIMEOUT) != HAL_OK)
{
Error_Handler();
}
temperatureAdcValue = HAL_ADC_GetValue(&hadc3);
if (HAL_ADC_Stop(&hadc3) != HAL_OK)
{
Error_Handler();
}
voltageMv = (adcValue * ADC_VREF_MV) / ADC_MAX_VALUE;
temperatureC = __LL_ADC_CALC_TEMPERATURE(ADC_VREF_MV, temperatureAdcValue,
LL_ADC_RESOLUTION_12B);
printf("{\"adc_voltage_v\": %lu.%03lu, \"mcu_temperature_c\": %ld}\r\n",
(unsigned long)(voltageMv / 1000U),
(unsigned long)(voltageMv % 1000U),
(long)temperatureC);
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
保存代码。
编译调试
- 点击下方工具栏的
生成 按钮,编译工程;
- 点击左侧工具栏的
调试运行 按钮,点击上方的运行按钮,上传调试固件;
- 待进入调试状态,点击悬浮工具栏的启动按钮,开始运行程序;

效果
- 打开 网页串口数据绘图工具 ,设置串口参数并连接;
- 原始数据显示窗口打印 JSON 格式的 ADC 电压和温度数据;
- 点击
开始采集 按钮,曲线显示 ADC 电压和 MCU 温度动态演化曲线;

OLED
包括 OLED 驱动的头文件和源文件,字库和图片文件等。
ssd1306.h
新建 OLED 驱动头文件 ./Core/Inc/ssd1306.h ,添加如下代码
#ifndef __SSD1306_H
#define __SSD1306_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
void WriteCmd(void);
void SSD1306_WR_CMD(uint8_t cmd);
void SSD1306_WR_DATA(uint8_t data);
void SSD1306_Init(void);
void SSD1306_Clear(void);
void SSD1306_On(void);
void SSD1306_ShowNum(uint8_t x,uint8_t y,unsigned int num,uint8_t len,uint8_t size2, uint8_t Color_Turn);
void SSD1306_ShowString(uint8_t x,uint8_t y,const char *chr,uint8_t Char_Size, uint8_t Color_Turn);
#ifdef __cplusplus
}
#endif
#endif /* __SSD1306_H */
保存代码。
ssd1306.c
新建 OLED 驱动源文件 ./Core/Src/ssd1306.c ,添加如下代码
#include "ssd1306.h"
#include "main.h"
#include "mx_i2c2.h"
#include "oledfont.h"
void WriteCmd(void)
{
uint8_t i = 0;
for(i=0; i<27; i++)
{
HAL_I2C_Mem_Write(&hi2c4, 0x78, 0x00,
I2C_MEMADD_SIZE_8BIT, CMD_Data + i, 1, 100);
}
}
/**
* @function: void SSD1306_WR_CMD(uint8_t cmd)
* @description: 向设备写控制命令
* @param {uint8_t} cmd 芯片手册规定的命令
* @return {*}
*/
void SSD1306_WR_CMD(uint8_t cmd)
{
HAL_I2C_Mem_Write(&hi2c4, 0x78, 0x00,
I2C_MEMADD_SIZE_8BIT, &cmd, 1, 100);
}
具体函数定义详见:【功率监测与控制系统DIY活动过程贴】OLED显示 .
oledfont.h
新建 OLED 驱动的字体头文件 ./Core/Inc/oledfont.h ,添加如下代码
#ifndef __OLEDFONT_H
#define __OLEDFONT_H
#include <stdint.h>
//8*6 ASCII字符集点阵 static const uint8_t Font6x8[][6] = {};
const unsigned char F6x8[][6] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// sp
// …… ……
0x14, 0x14, 0x14, 0x14, 0x14, 0x14,// horiz lines
};
//16*8 ASCII字符集点阵 static const uint8_t Font8x16[][16]={};
const unsigned char Font8x16[]=
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//sp /0
// …… ……
0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x00,0x00,0x01,0x00,0x00,0x01,0x01,0x00,0x00 // ~ /94
};
const unsigned char Hzk[][32]=
{
{0x40,0x40,0x42,0xCC,0x00,0x04,0xF4,0x94,0x94,0xFF,0x94,0x94,0xF4,0x04,0x00,0x00},
{0x00,0x40,0x20,0x1F,0x20,0x48,0x44,0x42,0x41,0x5F,0x41,0x42,0x44,0x48,0x40,0x00},/*"速",1*/
/* (16 X 16 , 宋体 )*/
};
#endif
使用 PCtoLCD 软件取模,详见:【功率监测与控制系统DIY活动过程贴】OLED显示 .

字符在线取模:在线文字取模工具 .
bmp.h
新建 OLED 驱动的图片头文件 ./Core/Inc/bmp.h ,添加如下代码
#ifndef BMP_H_
#define BMP_H_
const unsigned char BMP1[] =
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
// …… ……
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
#endif /* BMP_H_ */
保存代码。
主函数
修改主函数 ./Core/Src/main.c 文件,关键代码如下
#include "main.h"
#include "ssd1306.h"
#include "bmp.h"
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c4;
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
static void MX_GPIO_Init(void);
static void MX_ICACHE_Init(void);
static void MX_I2C4_Init(void);
int main(void)
{
MPU_Config();
HAL_Init();
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ICACHE_Init();
MX_ADC1_Init();
MX_ADC3_Init();
MX_I2C4_Init();
SSD1306_Init();
SSD1306_Clear();
SSD1306_ShowString(25, 0, "OLED TEST", 16, 1);
SSD1306_ShowString(0, 3, "6x8 Font~", 12, 0);
SSD1306_ShowString(0, 4, "8x16 Font~", 16, 0);
SSD1306_ShowNum(0, 6, 1234567890, 10, 16, 0);
HAL_Delay(1000);
/* 图片显示 */
SSD1306_DrawBMP(0, 0, 128, 8, BMP1, 0);
HAL_Delay(1000);
/* 显示汉字 */
SSD1306_Clear();
/* 1st row */
SSD1306_ShowString(0,0," A D C ",16,1);
SSD1306_ShowCHinese(56,0,7,1); // 电
SSD1306_ShowString(71,0," ",16,1);
SSD1306_ShowCHinese(79,0,8,1); // 压
SSD1306_ShowString(94,0," ",16,1);
SSD1306_ShowCHinese(102,0,9,1); // 表
SSD1306_ShowString(117,0," ",16,1);
/* 2nd row */
SSD1306_ShowCHinese(0,2,0,0); // 电
SSD1306_ShowCHinese(15,2,1,0); // 压
SSD1306_ShowChar(30,2,':',16,0); // :
/* 3rd row */
SSD1306_ShowCHinese(0,4,3,0); // 温
SSD1306_ShowCHinese(15,4,4,0); // 度
SSD1306_ShowChar(30,4,':',16,0); // :
/* 4th row */
SSD1306_ShowCHinese(0,6,5,0); // 速
SSD1306_ShowCHinese(15,6,6,0); // 率
SSD1306_ShowChar(30,6,':',16,0); // :
HAL_Delay(1000);
while (1){}
}
保存代码。
打开项目的 CMakeLists.txt 文件,将新增的驱动文件 Core/Src/ssd1306.c 添加至编译路径;
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add user sources here
Core/Src/ssd1306.c
)
保存文件。
效果
- 编译固件,运行工程;
- OLED 显示文字、数字、图片等;

ADC 电压表
将 ADC 电压和 MCU 温度数据显示在 OLED 屏幕上。
流程图

代码
在前面代码的基础上,打开 ./Core/Src/main.c 文件,添加如下代码
#include "main.h"
#include "ssd1306.h"
#include "bmp.h"
int main(void)
{
MX_ADC1_Init();
MX_ADC3_Init();
MX_I2C4_Init();
SSD1306_Init();
while (1)
{
uint32_t adcValue; // ADC value for voltage sensor
uint32_t temperatureAdcValue; // ADC value for temperature sensor
uint32_t voltageMv; // mV
int32_t temperatureC; // Celsius
int32_t speed; // HZ
voltageMv = (adcValue * ADC_VREF_MV) / ADC_MAX_VALUE;
temperatureC = __LL_ADC_CALC_TEMPERATURE(ADC_VREF_MV, temperatureAdcValue,
LL_ADC_RESOLUTION_12B);
speed = 5;
printf("{\"adc_voltage_v\": %lu.%03lu, \"mcu_temperature_c\": %ld}\r\n",
(unsigned long)(voltageMv / 1000U),
(unsigned long)(voltageMv % 1000U),
(long)temperatureC);
SSD1306_ClearLinePart(2, 40, 99);
SSD1306_ClearLinePart(3, 40, 99);
SSD1306_ShowDecimal(40, 2, (float)voltageMv / 1000.0f, 1, 3, 16, 0);
SSD1306_ClearLinePart(4, 40, 99);
SSD1306_ClearLinePart(5, 40, 99);
SSD1306_ShowNum(40,4,temperatureC,2,16,0);
SSD1306_ClearLinePart(6, 40, 99);
SSD1306_ClearLinePart(7, 40, 99);
SSD1306_ShowNum(40,6,speed,3,16,0);
HAL_Delay(1000/speed);
}
}
保存代码。
效果
- 点击左侧工具栏的
调试运行 按钮,点击上方的运行按钮,上传调试固件;
- 待进入调试状态,点击悬浮工具栏的启动按钮,开始运行程序;
- OLED 显示 A0 引脚的 ADC 电压(0-3.3V)以及 MCU 温度;

动态演示

总结
本文介绍了 NUCLEO-H5E5ZJ 开发板驱动 OLED 显示 ADC 电压和 MCU 温度的项目设计,包括硬件连接、环境搭建、工程创建、参数配置、ADC 打印、OLED显示等流程,为相关产品的快速开发和应用设计提供了参考。