前言
最近做嵌入式实训项目:STM32F103C8T6 搭配 BMP180 数字气压传感器,通过软件 I2C 读取温压原始数据,利用芯片出厂校准系数做补偿运算,再通过标准大气公式估算海拔,最终 12864 OLED 实时显示温度、气压、海拔。 本文完整梳理硬件接线、BMP180 工作原理、软件 I2C 通信、校准补偿逻辑、海拔换算、排错方案,适合单片机入门、课程设计、毕设直接复用。
视频讲解与演示:STM32F103+BMP180 气压温度传感器 + OLED 完整教程|软件 I2C 驱动、补偿算法、海拔计算全解析
http:// https://www.bilibili.com/video/BV1HdKE6uEKm/?share_source=copy_web&vd_source=91b967b41a17a5686913521545a8c3fb
一、整体实现流程
外界温压变化 → BMP180 MEMS 压阻元件产生信号 → 内部 ADC 输出原始 UT/UP → STM32 软件 I2C 读取原始数据 + 片内校准参数 → 补偿计算真实温压 → 公式换算海拔 → OLED 屏幕刷新展示
核心逻辑一句话:BMP180 只输出未补偿原始 ADC 值,有效温压、海拔全部由 STM32 软件运算得出。

二、硬件接线方案(双组独立软件 I2C 互不干扰)
1. BMP180 接线
| SCL | PB6 | I2C 时钟 |
| SDA | PB7 | I2C 数据 |
| VCC | 3.3V | 供电(芯片额定 1.8~3.6V,禁止直连 5V 裸芯片) |
| GND | GND | 共地,通信稳定必备 |
2. OLED 接线(独立 I2C,不占用 BMP 总线)
| SCL | PB8 | I2C 时钟 |
| SDA | PB9 | I2C 数据 |
| VCC | 3.3V | 供电 |
| GND | GND | 共地 |
3. I2C 上拉电阻关键说明
I2C 为开漏输出,总线必须上拉;手册推荐 2.2k~10kΩ,典型 4.7kΩ。 市面上成品 BMP180 模块自带上拉,裸芯片 / 无板载电阻模块需外接:3.3V 分别串 4.7k 电阻接 SCL、SDA,否则无应答、通信报错。
三、BMP180 内部工作原理
BMP180 是博世 MEMS 压阻式数字气压传感器,内部结构:微型压力膜片 + 压阻电桥 + 温度检测单元 + ADC+E2PROM 校准存储 + I2C 接口。

四、软件 I2C 通信细节(无硬件 I2C 外设,纯 GPIO 模拟)
1. I2C 设备地址
7 位基础地址:0x77
- 写操作 8 位地址:0xEE(0x77<<1,最低位 0)
- 读操作 8 位地址:0xEF(0x77<<1 | 1,最低位 1)
2. 标准读写时序
1)写寄存器:起始信号→发送写地址等待 ACK→寄存器地址→写入数据→停止信号 2)读寄存器:起始→写地址 + 寄存器地址→重复起始→读地址→读取字节→最后 NACK→停止信号 初始化阶段会发送 9 个恢复时钟,释放卡死的 I2C 总线,解决异常通信死机问题。
五、初始化流程与芯片校验
BMP180_Init()执行步骤:
重点:不同 BMP180 校准参数不能通用,必须上电实时读取自身 E2PROM。
六、温压测量完整流程(OSS=0 超低功耗模式)
规则:必须先测温度,再测气压
寄存器速查表: 0xD0:芯片 ID;0xF4:测量控制寄存器;0xF6/F7/F8:转换结果寄存器
七、补偿计算与单位转换
海拔误差说明
海拔不是传感器直接测量值,纯气压推算结果:
八、OLED 显示格式
BMP180
Temp: 23.5 C
Press:1013.25 hPa
Alt: 0042 m
异常提示:
- Sensor error:芯片失联、ID 错误、校准参数失效;
- Check PB6 / PB7:I2C 引脚接反、供电 / 接地故障; 主循环 500ms 刷新一次,通信失败自动重新初始化,恢复后自动正常采集。
九、工程文件函数分工
#include "BMP180.h"
#include "delay.h"
#include <math.h>
/*
* 文件名称:BMP180.c
* 功能说明:
* 1. 使用 PB6、PB7 模拟 I2C 时序,与 BMP180 进行通信;
* 2. 读取芯片内部 E2PROM 中的出厂校准系数;
* 3. 读取原始温度和原始气压,并按照数据手册完成补偿计算;
* 4. 使用标准大气公式估算海拔。
*
* 硬件连接:BMP180 SCL -> PB6,BMP180 SDA -> PB7。
*/
/* BMP180 软件 I2C 使用的 GPIO 引脚。 */
#define BMP180_SCL_PIN GPIO_Pin_6
#define BMP180_SDA_PIN GPIO_Pin_7
/* BMP180 的 7 位地址为 0x77,加入读写位后分别为 0xEE 和 0xEF。 */
#define BMP180_ADDRESS_WRITE 0xEE
#define BMP180_ADDRESS_READ 0xEF
/* BMP180 主要寄存器地址。 */
#define BMP180_REG_CHIP_ID 0xD0
#define BMP180_REG_CONTROL 0xF4
#define BMP180_REG_RESULT 0xF6
/* 写入测量控制寄存器的转换命令。当前气压测量采用 OSS=0 模式。 */
#define BMP180_CMD_TEMPERATURE 0x2E
#define BMP180_CMD_PRESSURE_OSS0 0x34
/*
* BMP180 出厂校准参数。
* 每颗芯片的参数都不相同,必须从 0xAA~0xBF 寄存器读取,
* 不能直接使用其他芯片的固定参数。
*/
typedef struct
{
s16 ac1;
s16 ac2;
s16 ac3;
u16 ac4;
u16 ac5;
u16 ac6;
s16 b1;
s16 b2;
s16 mb;
s16 mc;
s16 md;
} BMP180_CalibrationTypeDef;
/* 保存当前芯片的校准参数,以及驱动是否已成功初始化。 */
static BMP180_CalibrationTypeDef bmp180_cal;
static u8 bmp180_ready = 0;
/* 软件 I2C 时序延时,用于控制 SCL 和 SDA 的变化速度。 */
static void BMP180_I2C_Delay(void)
{
delay_us(3);
}
/* 释放 SCL。PB6 为开漏输出,写 1 后由上拉电阻产生高电平。 */
static void BMP180_SCL_High(void)
{
GPIO_SetBits(GPIOB, BMP180_SCL_PIN);
}
/* 将 SCL 主动拉低。 */
static void BMP180_SCL_Low(void)
{
GPIO_ResetBits(GPIOB, BMP180_SCL_PIN);
}
/* 释放 SDA,使 BMP180 可以在数据线或应答周期驱动该引脚。 */
static void BMP180_SDA_High(void)
{
GPIO_SetBits(GPIOB, BMP180_SDA_PIN);
}
/* 将 SDA 主动拉低。 */
static void BMP180_SDA_Low(void)
{
GPIO_ResetBits(GPIOB, BMP180_SDA_PIN);
}
/* 读取 SDA 的实际电平,用于接收数据和检测从机 ACK。 */
static u8 BMP180_SDA_Read(void)
{
return (u8)GPIO_ReadInputDataBit(GPIOB, BMP180_SDA_PIN);
}
/*
* 产生 I2C 停止信号:
* SCL 为高电平期间,SDA 从低电平跳变为高电平。
*/
static void BMP180_I2C_Stop(void)
{
BMP180_SCL_Low();
BMP180_SDA_Low();
BMP180_I2C_Delay();
BMP180_SCL_High();
BMP180_I2C_Delay();
BMP180_SDA_High();
BMP180_I2C_Delay();
}
/*
* 产生 I2C 起始或重复起始信号:
* SCL 为高电平期间,SDA 从高电平跳变为低电平。
*/
static void BMP180_I2C_Start(void)
{
BMP180_SDA_High();
BMP180_SCL_High();
BMP180_I2C_Delay();
BMP180_SDA_Low();
BMP180_I2C_Delay();
BMP180_SCL_Low();
}
/*
* 通过软件 I2C 发送一个字节,数据从最高位开始发送。
* 发送完成后释放 SDA,并在第 9 个时钟周期读取 BMP180 的应答。
* 返回值:1 表示收到低电平 ACK;0 表示无应答。
*/
static u8 BMP180_I2C_WriteByte(u8 value)
{
u8 i;
u8 ack;
for(i = 0; i < 8; i++)
{
BMP180_SCL_Low();
if((value & 0x80U) != 0U)
{
BMP180_SDA_High();
}
else
{
BMP180_SDA_Low();
}
BMP180_I2C_Delay();
BMP180_SCL_High();
BMP180_I2C_Delay();
value <<= 1;
}
/* 第 9 个时钟周期用于读取从机应答。 */
BMP180_SCL_Low();
BMP180_SDA_High();
BMP180_I2C_Delay();
BMP180_SCL_High();
BMP180_I2C_Delay();
ack = (BMP180_SDA_Read() == 0U) ? 1U : 0U;
BMP180_SCL_Low();
return ack;
}
/*
* 通过软件 I2C 接收一个字节,数据从最高位开始接收。
* send_ack 非 0 时发送 ACK,表示主机还要继续读取;
* send_ack 为 0 时发送 NACK,表示当前字节是最后一个字节。
*/
static u8 BMP180_I2C_ReadByte(u8 send_ack)
{
u8 i;
u8 value = 0;
BMP180_SDA_High();
for(i = 0; i < 8; i++)
{
BMP180_SCL_Low();
BMP180_I2C_Delay();
BMP180_SCL_High();
value <<= 1;
if(BMP180_SDA_Read() != 0U)
{
value |= 1U;
}
BMP180_I2C_Delay();
}
/* 第 9 个时钟周期由主机向 BMP180 返回 ACK 或 NACK。 */
BMP180_SCL_Low();
if(send_ack != 0U)
{
BMP180_SDA_Low();
}
else
{
BMP180_SDA_High();
}
BMP180_I2C_Delay();
BMP180_SCL_High();
BMP180_I2C_Delay();
BMP180_SCL_Low();
BMP180_SDA_High();
return value;
}
/*
* I2C 总线恢复:释放 SDA 后输出 9 个 SCL 时钟,再发送停止信号。
* 当上一次通信异常中断、从机仍占用 SDA 时,该操作有助于释放总线。
*/
static void BMP180_I2C_BusRecovery(void)
{
u8 i;
BMP180_SDA_High();
for(i = 0; i < 9; i++)
{
BMP180_SCL_Low();
BMP180_I2C_Delay();
BMP180_SCL_High();
BMP180_I2C_Delay();
}
BMP180_I2C_Stop();
}
/* 向 BMP180 的指定寄存器写入一个字节,任一阶段无 ACK 都返回失败。 */
static u8 BMP180_WriteRegister(u8 reg, u8 value)
{
BMP180_I2C_Start();
if(BMP180_I2C_WriteByte(BMP180_ADDRESS_WRITE) == 0U ||
BMP180_I2C_WriteByte(reg) == 0U ||
BMP180_I2C_WriteByte(value) == 0U)
{
BMP180_I2C_Stop();
return 0;
}
BMP180_I2C_Stop();
return 1;
}
/*
* 从指定寄存器开始连续读取 length 个字节。
* 先以写方向发送寄存器地址,再发送重复起始信号并切换到读方向。
*/
static u8 BMP180_ReadRegisters(u8 reg, u8 *buffer, u8 length)
{
u8 i;
if(buffer == 0 || length == 0U)
{
return 0;
}
/* 写入待读取的起始寄存器地址。 */
BMP180_I2C_Start();
if(BMP180_I2C_WriteByte(BMP180_ADDRESS_WRITE) == 0U ||
BMP180_I2C_WriteByte(reg) == 0U)
{
BMP180_I2C_Stop();
return 0;
}
/* 重复起始后切换到读方向,最后一个字节返回 NACK。 */
BMP180_I2C_Start();
if(BMP180_I2C_WriteByte(BMP180_ADDRESS_READ) == 0U)
{
BMP180_I2C_Stop();
return 0;
}
for(i = 0; i < length; i++)
{
buffer[i] = BMP180_I2C_ReadByte((i + 1U < length) ? 1U : 0U);
}
BMP180_I2C_Stop();
return 1;
}
/* 连续读取大端格式的两个字节,并组合成一个 16 位无符号数。 */
static u8 BMP180_ReadU16(u8 reg, u16 *value)
{
u8 bytes[2];
if(value == 0 || BMP180_ReadRegisters(reg, bytes, 2) == 0U)
{
return 0;
}
*value = (u16)(((u16)bytes[0] << 8) | bytes[1]);
return 1;
}
/*
* 从 E2PROM 读取 11 个出厂校准系数。
* AC1、AC2、AC3、B1、B2、MB、MC、MD 为有符号数;
* AC4、AC5、AC6 为无符号数。
*/
static u8 BMP180_ReadCalibration(void)
{
u16 value;
if(BMP180_ReadU16(0xAA, &value) == 0U) return 0;
bmp180_cal.ac1 = (s16)value;
if(BMP180_ReadU16(0xAC, &value) == 0U) return 0;
bmp180_cal.ac2 = (s16)value;
if(BMP180_ReadU16(0xAE, &value) == 0U) return 0;
bmp180_cal.ac3 = (s16)value;
if(BMP180_ReadU16(0xB0, &value) == 0U) return 0;
bmp180_cal.ac4 = value;
if(BMP180_ReadU16(0xB2, &value) == 0U) return 0;
bmp180_cal.ac5 = value;
if(BMP180_ReadU16(0xB4, &value) == 0U) return 0;
bmp180_cal.ac6 = value;
if(BMP180_ReadU16(0xB6, &value) == 0U) return 0;
bmp180_cal.b1 = (s16)value;
if(BMP180_ReadU16(0xB8, &value) == 0U) return 0;
bmp180_cal.b2 = (s16)value;
if(BMP180_ReadU16(0xBA, &value) == 0U) return 0;
bmp180_cal.mb = (s16)value;
if(BMP180_ReadU16(0xBC, &value) == 0U) return 0;
bmp180_cal.mc = (s16)value;
if(BMP180_ReadU16(0xBE, &value) == 0U) return 0;
bmp180_cal.md = (s16)value;
/* 关键无符号校准参数为 0x0000 或 0xFFFF 时,认为校准数据无效。 */
if(bmp180_cal.ac4 == 0U || bmp180_cal.ac4 == 0xFFFFU ||
bmp180_cal.ac5 == 0U || bmp180_cal.ac5 == 0xFFFFU ||
bmp180_cal.ac6 == 0U || bmp180_cal.ac6 == 0xFFFFU)
{
return 0;
}
return 1;
}
/*
* 初始化 BMP180:
* 1. 将 PB6、PB7 配置为 50MHz 开漏输出;
* 2. 恢复并释放软件 I2C 总线;
* 3. 读取 0xD0,确认芯片 ID 为 0x55;
* 4. 读取并检查出厂校准参数。
*/
u8 BMP180_Init(void)
{
GPIO_InitTypeDef gpio;
u8 chip_id;
bmp180_ready = 0;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
gpio.GPIO_Pin = BMP180_SCL_PIN | BMP180_SDA_PIN;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
gpio.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOB, &gpio);
GPIO_SetBits(GPIOB, BMP180_SCL_PIN | BMP180_SDA_PIN);
BMP180_I2C_BusRecovery();
/* 芯片 ID 不为 0x55 时,通常表示接线、供电或 I2C 通信异常。 */
if(BMP180_ReadRegisters(BMP180_REG_CHIP_ID, &chip_id, 1) == 0U ||
chip_id != 0x55U)
{
return 0;
}
if(BMP180_ReadCalibration() == 0U)
{
return 0;
}
bmp180_ready = 1;
return 1;
}
/*
* 读取并计算 BMP180 数据。
* BMP180 的气压结果依赖温度补偿中间量 B5,
* 因此必须先完成温度测量和补偿,再进行气压测量和补偿。
*/
u8 BMP180_ReadData(BMP180_DataTypeDef *data)
{
u8 raw_bytes[3];
u16 raw_temperature;
u32 raw_pressure;
s32 x1;
s32 x2;
s32 x3;
s32 b3;
s32 b5;
s32 b6;
u32 b4;
u32 b7;
s32 pressure;
s32 denominator;
double altitude;
/* 防止使用空指针,或在传感器尚未成功初始化时读取数据。 */
if(data == 0 || bmp180_ready == 0U)
{
return 0;
}
/* 第一步:启动温度转换。0x2E 对应一次温度测量。 */
if(BMP180_WriteRegister(BMP180_REG_CONTROL, BMP180_CMD_TEMPERATURE) == 0U)
{
return 0;
}
/* 数据手册规定温度转换最长约 4.5ms,这里等待 5ms。 */
delay_ms(5);
if(BMP180_ReadRegisters(BMP180_REG_RESULT, raw_bytes, 2) == 0U)
{
return 0;
}
raw_temperature = (u16)(((u16)raw_bytes[0] << 8) | raw_bytes[1]);
/*
* 使用 UT、AC5、AC6、MC、MD 计算真实温度及中间量 B5。
* temperature 的单位为 0.1℃。
*/
x1 = (((s32)raw_temperature – (s32)bmp180_cal.ac6) *
(s32)bmp180_cal.ac5) >> 15;
denominator = x1 + (s32)bmp180_cal.md;
if(denominator == 0)
{
return 0;
}
x2 = ((s32)bmp180_cal.mc << 11) / denominator;
b5 = x1 + x2;
data->temperature = (b5 + 8) >> 4;
/* 第二步:启动 OSS=0(超低功耗模式)气压转换。 */
if(BMP180_WriteRegister(BMP180_REG_CONTROL, BMP180_CMD_PRESSURE_OSS0) == 0U)
{
return 0;
}
/* OSS=0 气压转换最长约 4.5ms,这里等待 5ms。 */
delay_ms(5);
if(BMP180_ReadRegisters(BMP180_REG_RESULT, raw_bytes, 3) == 0U)
{
return 0;
}
raw_pressure = (((u32)raw_bytes[0] << 16) |
((u32)raw_bytes[1] << 8) |
raw_bytes[2]) >> 8;
/*
* 按照 BMP180 数据手册的整数补偿算法计算真实气压。
* B5 来自前面的温度补偿,因此不能跳过温度测量直接计算气压。
*/
b6 = b5 – 4000;
x1 = ((s32)bmp180_cal.b2 * ((b6 * b6) >> 12)) >> 11;
x2 = ((s32)bmp180_cal.ac2 * b6) >> 11;
x3 = x1 + x2;
b3 = ((((s32)bmp180_cal.ac1 * 4 + x3) + 2) >> 2);
x1 = ((s32)bmp180_cal.ac3 * b6) >> 13;
x2 = ((s32)bmp180_cal.b1 * ((b6 * b6) >> 12)) >> 16;
x3 = (x1 + x2 + 2) >> 2;
b4 = ((u32)bmp180_cal.ac4 * (u32)(x3 + 32768)) >> 15;
if(b4 == 0U || (s32)raw_pressure < b3)
{
return 0;
}
/* 根据 B7 的数值范围选择不同计算顺序,避免 32 位整数溢出。 */
b7 = (raw_pressure – (u32)b3) * 50000U;
if(b7 < 0x80000000UL)
{
pressure = (s32)((b7 << 1) / b4);
}
else
{
pressure = (s32)((b7 / b4) << 1);
}
x1 = (pressure >> 8) * (pressure >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * pressure) >> 16;
pressure += (x1 + x2 + 3791) >> 4;
if(pressure <= 0)
{
return 0;
}
/* pressure 的单位为 Pa,补偿算法的输出分辨率为 1Pa。 */
data->pressure = (u32)pressure;
/*
* 使用标准大气公式估算海拔:
* H = 44330 × [1 – (P / P0)^0.190294957]
* 其中 P0 固定为标准海平面气压 101325Pa,所以该结果是估算值。
*/
altitude = 44330.0 * (1.0 – pow((double)pressure / 101325.0,
0.190294957));
data->altitude = (s32)((altitude >= 0.0) ? (altitude + 0.5) : (altitude – 0.5));
return 1;
}
#ifndef __BMP180_H
#define __BMP180_H
#include "sys.h"
/*
* 文件名称:BMP180.h
* 功能说明:声明 BMP180 驱动对外提供的数据类型和操作接口。
*
* BMP180 使用独立的软件 I2C 总线:
* SCL -> PB6
* SDA -> PB7
* OLED 仍使用 PB8/PB9,两个模块的通信引脚互不影响。
*/
/* BMP180 完成补偿计算后的测量结果。 */
typedef struct
{
s32 temperature; /* 温度,单位为 0.1℃,例如 235 表示 23.5℃。 */
u32 pressure; /* 绝对气压,单位为 Pa,例如 101325 表示 1013.25hPa。 */
s32 altitude; /* 估算海拔,单位为 m,以 101325Pa 为海平面参考气压。 */
} BMP180_DataTypeDef;
/*
* 初始化 BMP180 软件 I2C、检查芯片 ID 并读取芯片校准参数。
* 返回值:1 表示初始化成功;0 表示芯片无应答、ID 错误或校准数据无效。
*/
u8 BMP180_Init(void);
/*
* 启动温度和气压转换,读取原始数据并完成补偿及海拔计算。
* 参数 data:用于保存温度、气压和估算海拔的结构体指针。
* 返回值:1 表示读取成功;0 表示参数无效、未初始化或通信失败。
*/
u8 BMP180_ReadData(BMP180_DataTypeDef *data);
#endif
/*
* BMP180 environmental monitor
* BMP180 SCL -> PB6, SDA -> PB7
* OLED SCL -> PB8, SDA -> PB9 (unchanged)
*/
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "BMP180.h"
#define BMP180_REFRESH_MS 500
static void BMP180_ShowData(const BMP180_DataTypeDef *data);
static void OLED_ShowSignedNum(u8 x, u8 y, s32 value, u8 len, u8 size);
int main(void)
{
BMP180_DataTypeDef sensor_data;
u8 sensor_ok;
delay_init();
delay_ms(1000);
NVIC_Configuration();
OLED_Init();
OLED_ColorTurn(0);
OLED_DisplayTurn(0);
OLED_Clear();
OLED_ShowString(32, 0, (u8 *)"BMP180", 16);
OLED_ShowString(8, 24, (u8 *)"SCL PB6 SDA PB7", 12);
OLED_ShowString(20, 44, (u8 *)"Initializing", 12);
OLED_Refresh();
sensor_ok = BMP180_Init();
delay_ms(200);
while(1)
{
if(sensor_ok != 0U && BMP180_ReadData(&sensor_data) != 0U)
{
BMP180_ShowData(&sensor_data);
}
else
{
OLED_Clear();
OLED_ShowString(32, 0, (u8 *)"BMP180", 16);
OLED_ShowString(18, 24, (u8 *)"Sensor error", 12);
OLED_ShowString(3, 42, (u8 *)"Check PB6 / PB7", 12);
OLED_Refresh();
delay_ms(500);
sensor_ok = BMP180_Init();
}
delay_ms(BMP180_REFRESH_MS);
}
}
static void BMP180_ShowData(const BMP180_DataTypeDef *data)
{
u32 temperature_abs;
u32 pressure_hpa;
temperature_abs = (u32)((data->temperature < 0) ?
-data->temperature : data->temperature);
pressure_hpa = data->pressure / 100U;
OLED_Clear();
OLED_ShowString(32, 0, (u8 *)"BMP180", 16);
OLED_ShowString(0, 18, (u8 *)"Temp:", 12);
if(data->temperature < 0)
OLED_ShowString(30, 18, (u8 *)"-", 12);
OLED_ShowNum(36, 18, temperature_abs / 10U, 2, 12);
OLED_ShowString(48, 18, (u8 *)".", 12);
OLED_ShowNum(54, 18, temperature_abs % 10U, 1, 12);
OLED_ShowString(60, 18, (u8 *)" C", 12);
OLED_ShowString(0, 32, (u8 *)"Press:", 12);
OLED_ShowNum(36, 32, pressure_hpa, 4, 12);
OLED_ShowString(60, 32, (u8 *)".", 12);
OLED_ShowNum(66, 32, data->pressure % 100U, 2, 12);
OLED_ShowString(78, 32, (u8 *)" hPa", 12);
OLED_ShowString(0, 46, (u8 *)"Alt:", 12);
OLED_ShowSignedNum(30, 46, data->altitude, 5, 12);
OLED_ShowString(60, 46, (u8 *)" m", 12);
OLED_Refresh();
}
static void OLED_ShowSignedNum(u8 x, u8 y, s32 value, u8 len, u8 size)
{
u32 magnitude;
if(value < 0)
{
OLED_ShowString(x, y, (u8 *)"-", size);
magnitude = (u32)(-value);
}
else
{
OLED_ShowString(x, y, (u8 *)" ", size);
magnitude = (u32)value;
}
OLED_ShowNum((u8)(x + size / 2U), y, magnitude, (u8)(len – 1U), size);
}



