[i=s] 本帖最后由 kai迪皮 于 2025-6-30 20:06 编辑 [/i]<br />
<br />
1. 背景:我们已经能测得很准,但还要更“便携”
在先前我们进行了
- APM32F402 & HC-SR04超声测距宝典:GPIO+定时器双驾齐驱
- APM32F402 & HC-SR04超声测距经典操作:波形输出与滤波
- APM32F402 & HC-SR04超声测距应用场景:温湿度补偿
可是在前面的实验里,我们通过 USB 连接到电脑,看串口打印的结果——这没什么不好,可是当我们想把这个小东西拿到现场去测试时,就会发现,拖着 USB 线、插着电脑太不方便。难道要当一个“抱着笔记本奔跑的测距侠”吗?这个操作可不优雅,也不利于后期的现场使用。
因此,从需求层面看:如果能在 APB402 + HC-SR04 + DHT11 的这个测距系统上加块小屏,让它在脱离电脑的情况下就能即时显示,那就太棒了!这样一来,我们只要给整套方案接个电源(例如手机充电宝),就能轻便地拿在手里满世界量距离、量温湿度,不再依赖上位机软件。
所以,“加个小屏幕”的念头便油然而生。而这里,我选择了常见的 0.96 英寸 OLED(SSD1306 驱动),它在嵌入式应用里极为常见,扮相简洁,显示效果清晰,且功耗低、引脚少。下面,让我们一起看看如何在 APM32F402 平台上和这块小屏“愉快共舞”吧!
2. 选择 OLED0.96:小巧玲珑,效果出众
2.1 为何选 OLED0.96
OLED 屏可以说是嵌入式开发中一朵“秀气的小花”——它身材不大、功耗不高,对 MCU 的资源占用也不算太苛刻。对于不需要复杂彩色 GUI 的场景(比如只想简单显示数字或字符),黑白OLED 0.96 英寸屏幕就已足够美观、易读。
此外,SSD1306 这款驱动芯片有现成的开源库和移植教程,不论是采用 I2C 通信还是 SPI 通信,都能快速上手。只要稍加修改,就能在 APM32、STM32、甚至其他品牌的 MCU 上跑个欢。正所谓“万金油式驱动”,开发者再熟悉不过。
我之前就利用APM32F4 DAL库驱动OLED(SSD1306)介绍过这个OLED在APM32F4上的驱动,但我们之前选择的是DAL驱动库,在APM32F402平台上我想选择极海的标准库,那从DAL库到标准库,我们看看要经历什么吧。
2.2 I2C 初始化(移植到 APM32F4 标准库)
我们这里使用 PB8 / PB9 这两脚来做 I2C1 的 SCL 和 SDA( PB6/PB7 在APM32F402EVB板上用作 LED ),只需要通过 GPIO_ConfigPinRemap 等函数来把引脚重映射即可。下面是示例初始化函数:
void I2C1_Config(void)
{
GPIO_Config_T gpioConfigStruct;
I2C_Config_T i2cConfigStruct;
/* Enable I2C related Clock */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOB | RCM_APB2_PERIPH_AFIO);
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_I2C1);
GPIO_ConfigPinRemap(GPIO_REMAP_I2C1); // SCL—PB8, SDA—PB9
/* Free I2C_SCL and I2C_SDA */
gpioConfigStruct.mode = GPIO_MODE_AF_OD;
gpioConfigStruct.speed = GPIO_SPEED_50MHz;
gpioConfigStruct.pin = GPIO_PIN_8;
GPIO_Config(GPIOB, &gpioConfigStruct);
gpioConfigStruct.mode = GPIO_MODE_AF_OD;
gpioConfigStruct.speed = GPIO_SPEED_50MHz;
gpioConfigStruct.pin = GPIO_PIN_9;
GPIO_Config(GPIOB, &gpioConfigStruct);
/* Config I2C1 */
I2C_Reset(I2C1);
i2cConfigStruct.mode = I2C_MODE_I2C;
i2cConfigStruct.dutyCycle = I2C_DUTYCYCLE_2;
i2cConfigStruct.ackAddress = I2C_ACK_ADDRESS_7BIT;
i2cConfigStruct.ownAddress1 = 0XA0;
i2cConfigStruct.ack = I2C_ACK_ENABLE;
i2cConfigStruct.clockSpeed = 400000; // fast mode 400k
I2C_Config(I2C1, &i2cConfigStruct);
/* Enable I2Cx */
I2C_Enable(I2C1);
}
初始化思路相当简单,无非是:
1. 时钟打开;
2. GPIO 重映射并配置成开漏输出;
3. I2C 外设设定模式、地址、时钟;
4. 开启 I2C 外设。
2.3 ssd1306 驱动迁移到 APM32F402 标准库
接下来,就该说到驱动文件了。我们在把DAL库中 使用 I2C 写SSD1306寄存器操作改成APM32F402 标准库即可。下面这段示例演示了如何发送指令和数据:
void OLED_I2C_SendByte(uint8_t addr, uint8_t Byte)
{
I2C_EnableGenerateStart(I2C1);
while (!I2C_ReadStatusFlag(I2C1, I2C_FLAG_START));
I2C_Tx7BitAddress(I2C1, SSD1306_I2C_ADDR, I2C_DIRECTION_TX);
while (!I2C_ReadStatusFlag(I2C1, I2C_FLAG_ADDR));
(void)I2C1->STS2;
I2C_TxData(I2C1, addr);
while (!I2C_ReadStatusFlag(I2C1, I2C_FLAG_BTC));
(void)I2C1->STS2;
I2C_TxData(I2C1, Byte);
while (!I2C_ReadStatusFlag(I2C1, I2C_FLAG_BTC));
I2C_EnableGenerateStop(I2C1);
I2C_ClearIntFlag(I2C1, I2C_INT_FLAG_STOP);
}
然后替换核心的ssd1306_WriteCommand、ssd1306_WriteData函数:
// Send a byte to the command register
void ssd1306_WriteCommand(uint8_t byte) {
OLED_I2C_SendByte(0x00, byte);
}
// Send data
void ssd1306_WriteData(uint8_t* buffer, size_t buff_size) {
while (buff_size--) {
OLED_I2C_SendByte(0x40, *buffer);
buffer++;
}}
还有就是在 ssd1306_conf.h 中定义一些宏:
- 添加APM32F402xx_STD、SSD1306_USE_I2C_STD,这些宏声明我们在使用标准库
- 替换DAL库的延时函数等,我这里直接使用极海官方的板载延时驱动
// Choose a microcontroller family
#define APM32F402xx_STD
// Choose a bus
#define SSD1306_USE_I2C
#define SSD1306_USE_I2C_STD
#define DAL_LOGI(tag, fmt) printf("[INFO ][%s] " fmt, tag)
#define DAL_Delay BOARD_Delay_Ms
#define DAL_GetTick BOARD_ReadTick
3. 显示设置:来点“花式摆盘”
有了驱动,咱们就可以在屏幕上随心所欲地显示各种信息。我写了个函数 ssd1306_DisPlay(...),把当前距离、温度、湿度值,转成字符串并绘制到 OLED 上。示例使用 Font_11x18 这种较大的字体,让数字更清晰:
void ssd1306_DisPlay(float Dist, int8_t temp, int8_t humi)
{
// Use a buffer to store strings
char buff[32];
// 1) 在第一行显示 "Dist:xxx m"
ssd1306_SetCursor(2, 0);
snprintf(buff, sizeof(buff), "Dist:%.2f m", Dist/1000);
ssd1306_WriteString(buff, Font_11x18, White);
// 2) 在第二行显示 "Temp: xx °C"
ssd1306_SetCursor(2, 18); // 18 是行高(根据字体大小调节)
snprintf(buff, sizeof(buff), "Temp:%d *C", temp);
ssd1306_WriteString(buff, Font_11x18, White);
// 3) 在第三行显示 "Humi: xx %"
ssd1306_SetCursor(2, 36); // 18*2 = 36
snprintf(buff, sizeof(buff), "Humi:%d %%", humi);
ssd1306_WriteString(buff, Font_11x18, White);
// 4) 调用更新函数,将显示缓冲区的内容刷新到屏幕
ssd1306_UpdateScreen();
}
这里说明一下“°C”这个符号在字库里可能没有,所以我这里示例用 “*C” 或者你可以改成“°C”,然后保证字体库包含度符号即可。
屏幕显示效果:

+罗马仕充电宝效果:

4. 总结:从测量到可视化,只差一块屏
前面我们利用 APM32F402 强大的定时器功能来驱动 HC-SR04,借助滤波算法稳定测量数据,并考虑温湿度补偿应对环境因素,以确保测量精度,如今借助SSD1306显示,让它足以手持外出测量。
这里是代码哦:
附件:APM32F402_403_SDK_V1.0.1_HC_SR04_DHT11__OLED.zip
至此,我们已从“测量”跨越到“可视化”,实现了一个真正脱离电脑、独立运行的超声测距仪表雏形。现在,不妨带上这块小屏,走出实验室,尽情测量吧!