1.前言
本次实验的目标是把I2C相关的搞定,再尝试驱动SSD1306 0.96寸OLED屏幕以及BME280传感器,最后将传感器读到的数据和实时时间显示在屏幕上。
2,硬件部分
2.1 I2C协议简介
I2C 通讯协议(Inter-Integrated Circuit)是由Philips公司开发的,由于它引脚少,硬件实现简单,可扩展性强, 不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设; 瑞萨的FPS库则是在寄存器与用户代码之间的软件层。对于通讯协议,我们也以分层的方式来理解, 最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。 协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。简单来说物理层规定我们用嘴巴还是用肢体来交流, 协议层则规定我们用中文还是英文来交流。
具体的I2C协议入门可以看TI的A Basic Guide to I2C - Texas Instruments
2.2 OLED屏幕
本次使用的屏幕是0.96寸 4针 I2C协议 OLED屏幕,其驱动IC为SSD1306,屏幕分辨率为128x64。
编程时参考的数据手册,以及基于商家给的STM32驱动库修改,具体的修改参考软件部分。
2.3 BME280温湿度气压传感器
BME280是一款由Bosch Sensortec开发的多功能环境传感器,可同时精确测量温度、湿度和气压,具有低功耗和小尺寸的特点,广泛应用于气象监测、室内导航、健康监测及物联网等领域。
3.软件部分
将先前03_RTC
工程复制一份,重命名为04_OLED_BME280-I2C
。
3.1 配置I2C
首先在e2s内配置I2C
序号 |
操作 |
---|
1 |
点击界面下方标签栏中的Pins 标签,进入引脚配置界面。 |
2 |
在Pin Selection 区域,展开Connectivity:I2C 选项,选择I2C0 。 |
3 |
在Pin Configuration 区域,将Pin Group Selection 设置为_A only ,Operation Mode 设置为Enabled 。 |
4 |
勾选SDA0 对应的P401 引脚和SCL0 对应的P400 引脚。 |

序号 |
操作 |
---|
1 |
在Pin Selection 区域,分别选择P400 和P401 引脚。 |
2 |
将Output type 设置为n-ch open drain ,把P400 和P401 配置成开漏输出。 |

序号 |
操作 |
---|
1 |
点击界面下方标签栏中的Stacks 标签,进入堆栈配置页面。 |
2 |
在HAL/Common Stacks 区域,点击New Stack 按钮。 |
3 |
在弹出菜单中,选择Connectivity 选项。 |
4 |
在Connectivity 子菜单中,选择I2C Master (r_iic_master) 。 |

序号 |
操作 |
---|
1 |
在HAL/Common Stacks 区域,点击选中g_i2c_master0 I2C Master (r_iic_master) 。 |
2 |
在下方Settings 设置区域的Module g_i2c_master0 I2C Master (r_iic_master) 部分,将Rate 设置为Fast-mode 。 |
3 |
Module g_i2c_master0 I2C Master (r_iic_master) 部分,设置Slave Address 为0x3c 。 |
4 |
Module g_i2c_master0 I2C Master (r_iic_master) 部分,设置Callback 为iic_callback ,Interrupt Priority Level 为Priority 2 。 |

这里说明一下,在移植商家给的STM32 OLED驱动库时看到屏幕地址为0x78
,即 01111000
,是包含读写位的(最低位)。而瑞萨这里是7位地址,不含读写位,因此要将0x78右移1位,即0x3C
(0111100)。
确认上面设置没问题后,生成项目代码。
2 编写代码
2.1 I2C通信相关
新建i2c.c
和i2c.h
文件。
i2c.h
:
::: details 查看代码
#ifndef I2C_H_
#define I2C_H_
extern volatile bool i2c_rx_complete;
extern volatile bool i2c_tx_complete;
void i2c_wait_rx();
void i2c_wait_tx();
#endif
:::
i2c.c
:
::: details 查看代码
#include "hal_data.h"
#include "i2c.h"
volatile bool i2c_rx_complete = false;
volatile bool i2c_tx_complete = false;
uint16_t timeout = 0;
void iic_callback(i2c_master_callback_args_t *p_args)
{
if (p_args->event == I2C_MASTER_EVENT_RX_COMPLETE)
{
i2c_rx_complete = true;
}
else if (p_args->event == I2C_MASTER_EVENT_TX_COMPLETE)
{
i2c_tx_complete = true;
}
}
void i2c_wait_tx()
{
timeout = 1000;
while (!i2c_tx_complete && timeout > 0)
{
timeout--;
}
i2c_tx_complete = false;
}
void i2c_wait_rx()
{
timeout = 1000;
while (!i2c_rx_complete && timeout > 0)
{
timeout--;
}
i2c_rx_complete = false;
}
:::
由于瑞萨FSP库的高集成度,我只需要编写代码实现回调函数iic_callback
、等待发送函数i2c_wait_tx
、等待接收i2c_wait_rx
函数改变标志位即可。
3.2.2 BME280操作相关
bme280.h
:
::: details 查看代码
#ifndef BME280_H_
#define BME280_H_
#include "hal_data.h"
#define BME280_ID 0x60
typedef struct
{
double humi, temp, press;
bool initialized;
} BME_Struct;
void BME280_Get_Data(BME_Struct *bme);
void BME280_Init(BME_Struct *bme);
void BME280_Write_then_Read(uint8_t *src, uint8_t write_bytes, uint8_t *data_dest, uint8_t read_bytes);
void BME280_Trimming_Values();
double BME280_compensate_T_double(int32_t adc_T);
double BME280_compensate_P_double(int32_t adc_P);
double bme280_compensate_H_double(int32_t adc_H);
#endif
:::
bme280.c
:
::: details 查看代码
#include "bme280.h"
#include "hal_data.h"
#include "i2c.h"
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
int8_t dig_H1;
int16_t dig_H2;
int8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;
void BME280_Write_then_Read(uint8_t *src, uint8_t write_bytes, uint8_t *data_dest, uint8_t read_bytes)
{
g_i2c_master0.p_api->slaveAddressSet(&g_i2c_master0_ctrl, 0x76, I2C_MASTER_ADDR_MODE_7BIT);
g_i2c_master0.p_api->write(&g_i2c_master0_ctrl, src, write_bytes, true);
i2c_wait_tx();
g_i2c_master0.p_api->read(&g_i2c_master0_ctrl, data_dest, read_bytes, false);
i2c_wait_rx();
g_i2c_master0.p_api->slaveAddressSet(&g_i2c_master0_ctrl, 0x3C, I2C_MASTER_ADDR_MODE_7BIT);
}
void BME280_Init(BME_Struct *bme)
{
uint8_t reg = 0xD0;
uint8_t write_settings[7] = {0x00};
uint8_t read_data;
BME280_Write_then_Read(®, 1, &read_data, 1);
if (read_data != BME280_ID)
{
printf("Init BME280 Failed!\n");
bme->initialized = false;
return;
}
else
{
bme->initialized = true;
}
write_settings[0] = 0xF2;
write_settings[1] = 0x05;
write_settings[2] = 0xF4;
write_settings[3] = 0x93;
write_settings[4] = 0xF5;
write_settings[5] = 0x10;
g_i2c_master0.p_api->slaveAddressSet(&g_i2c_master0_ctrl, 0x76, I2C_MASTER_ADDR_MODE_7BIT);
g_i2c_master0.p_api->write(&g_i2c_master0_ctrl, &write_settings[0], 6, false);
i2c_wait_tx();
g_i2c_master0.p_api->slaveAddressSet(&g_i2c_master0_ctrl, 0x3C, I2C_MASTER_ADDR_MODE_7BIT);
R_BSP_SoftwareDelay(2, BSP_DELAY_UNITS_MILLISECONDS);
BME280_Trimming_Values();
}
void BME280_Trimming_Values()
{
uint8_t data[33] = {
0,
};
uint8_t reg = 0x88;
BME280_Write_then_Read(®, 1, &data[0], 24);
R_BSP_SoftwareDelay(5, BSP_DELAY_UNITS_MILLISECONDS);
reg = 0xA1;
BME280_Write_then_Read(®, 1, &data[24], 1);
R_BSP_SoftwareDelay(5, BSP_DELAY_UNITS_MILLISECONDS);
reg = 0xE1;
BME280_Write_then_Read(®, 1, &data[25], 7);
R_BSP_SoftwareDelay(5, BSP_DELAY_UNITS_MILLISECONDS);
dig_T1 = (data[1] << 8) | data[0];
dig_T2 = (data[3] << 8) | data[2];
dig_T3 = (data[5] << 8) | data[4];
dig_P1 = (data[7] << 8) | data[6];
dig_P2 = (data[9] << 8) | data[8];
dig_P3 = (data[11] << 8) | data[10];
dig_P4 = (data[13] << 8) | data[12];
dig_P5 = (data[15] << 8) | data[14];
dig_P6 = (data[17] << 8) | data[16];
dig_P7 = (data[19] << 8) | data[18];
dig_P8 = (data[21] << 8) | data[20];
dig_P9 = (data[23] << 8) | data[22];
dig_H1 = data[24];
dig_H2 = (data[26] << 8) | data[25];
dig_H3 = data[27];
dig_H4 = (data[28] << 4) | (data[29] & 0x0F);
dig_H5 = (data[30] << 4) | ((data[29] >> 4));
dig_H6 = data[31];
}
volatile long signed int t_fine;
double BME280_compensate_T_double(long signed int adc_T)
{
double var1, var2, T;
var1 = (((double)adc_T) / 16384.0 - ((double)dig_T1) / 1024.0) * ((double)dig_T2);
var2 = ((((double)adc_T) / 131072.0 - ((double)dig_T1) / 8192.0) *
(((double)adc_T) / 131072.0 - ((double)dig_T1) / 8192.0)) *
((double)dig_T3);
t_fine = (long signed int)(var1 + var2);
T = (var1 + var2) / 5120.0;
return T;
}
double BME280_compensate_P_double(long signed int adc_P)
{
double var1, var2, p;
var1 = ((double)t_fine / 2.0) - 64000.0;
var2 = var1 * var1 * ((double)dig_P6) / 32768.0;
var2 = var2 + var1 * ((double)dig_P5) * 2.0;
var2 = (var2 / 4.0) + (((double)dig_P4) * 65536.0);
var1 = (((double)dig_P3) * var1 * var1 / 524288.0 + ((double)dig_P2) * var1) / 524288.0;
var1 = (1.0 + var1 / 32768.0) * ((double)dig_P1);
if (var1 == 0.0)
{
return 0;
}
p = 1048576.0 - (double)adc_P;
p = (p - (var2 / 4096.0)) * 6250.0 / var1;
var1 = ((double)dig_P9) * p * p / 2147483648.0;
var2 = p * ((double)dig_P8) / 32768.0;
p = p + (var1 + var2 + ((double)dig_P7)) / 16.0;
return p;
}
double bme280_compensate_H_double(long signed int adc_H)
{
double var_H;
var_H = (((double)t_fine) - 76800.0);
var_H = (adc_H - (((double)dig_H4) * 64.0 + ((double)dig_H5) / 16384.0 *
var_H)) *
(((double)dig_H2) / 65536.0 * (1.0 + ((double)dig_H6) / 67108864.0 * var_H * (1.0 + ((double)dig_H3) / 67108864.0 * var_H)));
var_H = var_H * (1.0 - ((double)dig_H1) * var_H / 524288.0);
if (var_H > 100.0)
var_H = 100.0;
else if (var_H < 0.0)
var_H = 0.0;
return var_H;
}
void BME280_Get_Data(BME_Struct *bme)
{
uint8_t dat[8] = {0};
uint32_t press_t, temp_t, hum_t = 0;
uint8_t reg = 0xF7;
BME280_Write_then_Read(®, 1, &dat[0], 8);
R_BSP_SoftwareDelay(2, BSP_DELAY_UNITS_MILLISECONDS);
press_t = ((((uint32_t)dat[0] << 12) | ((uint32_t)dat[1] << 4)) | ((uint32_t)dat[2] >> 4));
temp_t = ((((uint32_t)dat[3] << 12) | ((uint32_t)dat[4] << 4)) | ((uint32_t)dat[5] >> 4));
hum_t = (((uint32_t)dat[6] << 8) | (uint32_t)dat[7]);
bme->temp = BME280_compensate_T_double(temp_t);
bme->press = BME280_compensate_P_double(press_t) / 100.0;
bme->humi = bme280_compensate_H_double(hum_t);
}
:::
BME280_compensate_T_double
、bme280_compensate_H_double
、bme280_compensate_P_double
这三个函数分别为温度、湿度、气压的补偿算法函数,借鉴了BME280官方数据手册内给出的参考代码。
bme280工作流程为
步骤 |
内容 |
---|
1 |
上电初始化 |
2 |
写入0xF2、0xF4、0xF5寄存器以设定过采样率等参数 |
3 |
获取校准数据 |
4 |
调用BME280_Get_Data函数,读取0xF7~0xFE寄存器的数据 |
5 |
调用补偿算法函数得到人类可读的数值 |
:::warning 注意
在写入+读取函数后记得跟1~5ms的延时,再进行下一步操作,否则会因为bme280侧的数据未准备好,有极大概率读取到错误数据或读不到数据。
:::
3.2.3 OLED屏幕操作相关
oled.h
:
::: details 查看代码
#ifndef OLED_H_
#define OLED_H_
#include "hal_data.h"
#define OLED_CMD 0
#define OLED_DATA 1
void OLED_ClearPoint(uint8_t x, uint8_t y);
void OLED_ColorTurn(uint8_t i);
void OLED_DisplayTurn(uint8_t i);
void OLED_WR_Byte(uint8_t dat, uint8_t mode);
void OLED_DisPlay_On(void);
void OLED_DisPlay_Off(void);
void OLED_Refresh(void);
void OLED_Clear(void);
void OLED_DrawPoint(uint8_t x, uint8_t y, uint8_t t);
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode);
void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r);
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t size1);
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr, uint8_t size1);
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size1);
void OLED_ShowChinese(uint8_t x, uint8_t y, uint8_t num, uint8_t size1);
void OLED_ScrollDisplay(uint8_t num, uint8_t space);
void OLED_ShowPicture(uint8_t x, uint8_t y, uint8_t sizex, uint8_t sizey, uint8_t BMP[], uint8_t mode);
void OLED_Init(void);
#endif
oled.c
:
::: details 查看代码
#include "oled.h"
#include "oled_font.h"
#include "i2c.h"
volatile uint8_t OLED_GRAM[144][8];
void OLED_ColorTurn(uint8_t i)
{
if (i == 0)
{
OLED_WR_Byte(0xA6, OLED_CMD);
}
if (i == 1)
{
OLED_WR_Byte(0xA7, OLED_CMD);
}
}
void OLED_DisplayTurn(uint8_t i)
{
if (i == 0)
{
OLED_WR_Byte(0xC8, OLED_CMD);
OLED_WR_Byte(0xA1, OLED_CMD);
}
if (i == 1)
{
OLED_WR_Byte(0xC0, OLED_CMD);
OLED_WR_Byte(0xA0, OLED_CMD);
}
}
void OLED_WR_Byte(uint8_t dat, uint8_t mode)
{
uint8_t data[2];
if (mode)
{
data[0] = 0x40;
}
else
{
data[0] = 0x00;
}
data[1] = dat;
R_IIC_MASTER_Write(&g_i2c_master0_ctrl, data, 2, false);
i2c_wait_tx();
}
void OLED_DisPlay_On(void)
{
OLED_WR_Byte(0x8D, OLED_CMD);
OLED_WR_Byte(0x14, OLED_CMD);
OLED_WR_Byte(0xAF, OLED_CMD);
}
void OLED_DisPlay_Off(void)
{
OLED_WR_Byte(0x8D, OLED_CMD);
OLED_WR_Byte(0x10, OLED_CMD);
OLED_WR_Byte(0xAE, OLED_CMD);
}
void OLED_Refresh(void)
{
uint8_t i, n;
for (i = 0; i < 8; i++)
{
OLED_WR_Byte(0xb0 + i, OLED_CMD);
OLED_WR_Byte(0x00, OLED_CMD);
OLED_WR_Byte(0x10, OLED_CMD);
for (n = 0; n < 128; n++)
{
OLED_WR_Byte(OLED_GRAM[n][i], OLED_DATA);
}
}
}
void OLED_Clear(void)
{
uint8_t i, n;
for (i = 0; i < 8; i++)
{
for (n = 0; n < 128; n++)
{
OLED_GRAM[n][i] = 0;
}
}
OLED_Refresh();
}
void OLED_DrawPoint(uint8_t x, uint8_t y, uint8_t t)
{
uint8_t i, m, n;
i = y / 8;
m = y % 8;
n = 1 << m;
if (t)
{
OLED_GRAM[x][i] |= n;
}
else
{
OLED_GRAM[x][i] = ~OLED_GRAM[x][i];
OLED_GRAM[x][i] |= n;
OLED_GRAM[x][i] = ~OLED_GRAM[x][i];
}
}
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode)
{
uint16_t t;
int xerr = 0, yerr = 0, delta_x, delta_y, distance;
int incx, incy, uRow, uCol;
delta_x = x2 - x1;
delta_y = y2 - y1;
uRow = x1;
uCol = y1;
if (delta_x > 0)
incx = 1;
else if (delta_x == 0)
incx = 0;
else
{
incx = -1;
delta_x = -delta_x;
}
if (delta_y > 0)
incy = 1;
else if (delta_y == 0)
incy = 0;
else
{
incy = -1;
delta_y = -delta_x;
}
if (delta_x > delta_y)
distance = delta_x;
else
distance = delta_y;
for (t = 0; t < distance + 1; t++)
{
OLED_DrawPoint(uRow, uCol, mode);
xerr += delta_x;
yerr += delta_y;
if (xerr > distance)
{
xerr -= distance;
uRow += incx;
}
if (yerr > distance)
{
yerr -= distance;
uCol += incy;
}
}
}
void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r)
{
int a, b, num;
a = 0;
b = r;
while (2 * b * b >= r * r)
{
OLED_DrawPoint(x + a, y - b, 1);
OLED_DrawPoint(x - a, y - b, 1);
OLED_DrawPoint(x - a, y + b, 1);
OLED_DrawPoint(x + a, y + b, 1);
OLED_DrawPoint(x + b, y + a, 1);
OLED_DrawPoint(x + b, y - a, 1);
OLED_DrawPoint(x - b, y - a, 1);
OLED_DrawPoint(x - b, y + a, 1);
a++;
num = (a * a + b * b) - r * r;
if (num > 0)
{
b--;
a--;
}
}
}
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t size1)
{
uint8_t i, m, temp, size2, chr1;
uint8_t x0 = x, y0 = y;
if (size1 == 8)
size2 = 6;
else
size2 = (size1 / 8 + ((size1 % 8) ? 1 : 0)) * (size1 / 2);
chr1 = chr - ' ';
for (i = 0; i < size2; i++)
{
if (size1 == 8)
{
temp = asc2_0806[chr1][i];
}
else if (size1 == 12)
{
temp = asc2_1206[chr1][i];
}
else if (size1 == 16)
{
temp = asc2_1608[chr1][i];
}
else if (size1 == 24)
{
temp = asc2_2412[chr1][i];
}
else
{
return;
}
for (m = 0; m < 8; m++)
{
if (temp & 0x01)
OLED_GRAM[x][y / 8] |= (1 << (y % 8));
else
OLED_GRAM[x][y / 8] &= ~(1 << (y % 8));
temp >>= 1;
y++;
}
x++;
if ((size1 != 8) && ((x - x0) == size1 / 2))
{
x = x0;
y0 = y0 + 8;
}
y = y0;
}
}
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr, uint8_t size1)
{
while ((*chr >= ' ') && (*chr <= '~'))
{
OLED_ShowChar(x, y, *chr, size1);
if (size1 == 8)
x += 6;
else
x += size1 / 2;
chr++;
}
OLED_Refresh();
}
uint32_t OLED_Pow(uint8_t m, uint8_t n)
{
uint32_t result = 1;
while (n--)
{
result *= m;
}
return result;
}
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size1)
{
uint8_t t, temp, m = 0;
if (size1 == 8)
m = 2;
for (t = 0; t < len; t++)
{
temp = (num / OLED_Pow(10, len - t - 1)) % 10;
if (temp == 0)
{
OLED_ShowChar(x + (size1 / 2 + m) * t, y, '0', size1);
}
else
{
OLED_ShowChar(x + (size1 / 2 + m) * t, y, temp + '0', size1);
}
}
OLED_Refresh();
}
void OLED_ShowChinese(uint8_t x, uint8_t y, uint8_t num, uint8_t size1)
{
uint8_t m, temp;
uint8_t x0 = x, y0 = y;
uint16_t i, size3 = (size1 / 8 + ((size1 % 8) ? 1 : 0)) * size1;
uint8_t mask;
for (i = 0; i < size3; i++)
{
if (size1 == 16)
{
temp = Hzk1[num][i];
}
else
{
return;
}
for (m = 0; m < 8; m++)
{
mask = (1 << (y % 8));
if (temp & 0x01)
{
OLED_GRAM[x][y / 8] |= mask;
}
else
{
OLED_GRAM[x][y / 8] &= ~mask;
}
temp >>= 1;
y++;
}
x++;
if ((x - x0) == size1)
{
x = x0;
y0 = y0 + 8;
}
y = y0;
}
OLED_Refresh();
}
void OLED_ScrollDisplay(uint8_t num, uint8_t space)
{
uint8_t i, n, t = 0, m = 0, r;
while (1)
{
if (m == 0)
{
OLED_ShowChinese(128, 24, t, 16);
t++;
}
if (t == num)
{
for (r = 0; r < 16 * space; r++)
{
for (i = 1; i < 144; i++)
{
for (n = 0; n < 8; n++)
{
OLED_GRAM[i - 1][n] = OLED_GRAM[i][n];
}
}
OLED_Refresh();
}
t = 0;
}
m++;
if (m == 16)
{
m = 0;
}
for (i = 1; i < 144; i++)
{
for (n = 0; n < 8; n++)
{
OLED_GRAM[i - 1][n] = OLED_GRAM[i][n];
}
}
OLED_Refresh();
}
}
void OLED_ShowPicture(uint8_t x, uint8_t y, uint8_t sizex, uint8_t sizey, uint8_t BMP[], uint8_t mode)
{
uint16_t j = 0;
uint8_t i, n, temp, m;
uint8_t x0 = x, y0 = y;
sizey = sizey / 8 + ((sizey % 8) ? 1 : 0);
for (n = 0; n < sizey; n++)
{
for (i = 0; i < sizex; i++)
{
temp = BMP[j];
j++;
for (m = 0; m < 8; m++)
{
if (temp & 0x01)
OLED_DrawPoint(x, y, mode);
else
OLED_DrawPoint(x, y, !mode);
temp >>= 1;
y++;
}
x++;
if ((x - x0) == sizex)
{
x = x0;
y0 = y0 + 8;
}
y = y0;
}
}
OLED_Refresh();
}
void OLED_Init(void)
{
OLED_WR_Byte(0xAE, OLED_CMD);
OLED_WR_Byte(0x00, OLED_CMD);
OLED_WR_Byte(0x10, OLED_CMD);
OLED_WR_Byte(0x40, OLED_CMD);
OLED_WR_Byte(0x81, OLED_CMD);
OLED_WR_Byte(0xCF, OLED_CMD);
OLED_WR_Byte(0xA1, OLED_CMD);
OLED_WR_Byte(0xC8, OLED_CMD);
OLED_WR_Byte(0xA6, OLED_CMD);
OLED_WR_Byte(0xA8, OLED_CMD);
OLED_WR_Byte(0x3f, OLED_CMD);
OLED_WR_Byte(0xD3, OLED_CMD);
OLED_WR_Byte(0x00, OLED_CMD);
OLED_WR_Byte(0xd5, OLED_CMD);
OLED_WR_Byte(0x80, OLED_CMD);
OLED_WR_Byte(0xD9, OLED_CMD);
OLED_WR_Byte(0xF1, OLED_CMD);
OLED_WR_Byte(0xDA, OLED_CMD);
OLED_WR_Byte(0x12, OLED_CMD);
OLED_WR_Byte(0xDB, OLED_CMD);
OLED_WR_Byte(0x30, OLED_CMD);
OLED_WR_Byte(0x20, OLED_CMD);
OLED_WR_Byte(0x02, OLED_CMD);
OLED_WR_Byte(0x8D, OLED_CMD);
OLED_WR_Byte(0x14, OLED_CMD);
OLED_Clear();
OLED_WR_Byte(0xAF, OLED_CMD);
}
:::tip
最开始OLED屏幕上显示字的速度非常慢,几乎是一个字一个字地往外蹦。
解决方法是开一个显存数组OLED_GRAM
,将内容先缓存到显存数组,再调用OLED_Refresh
一次性地写给OLED屏幕控制器
:::
3.2.4 修改hal_entry.c
在hal_entry.c
开头加入
::: details 查看代码
#include "hal_data.h"
#include "debug_bsp_uart.h"
#include "oled.h"
#include "bme280.h"
#include "rtc.h"
#include <stdio.h>
BME_Struct bme = {0, 0, 0, false};
rtc_time_t get_time;
:::
在hal_entry函数中加入
::: details 查看代码
Debug_UART9_Init();
g_i2c_master0.p_api->open(&g_i2c_master0_ctrl, &g_i2c_master0_cfg);
BME280_Init(&bme);
OLED_Init();
RTC_Init();
printf("I2C OLED屏幕+BME280获取温湿度实验\n");
printf("若要通过串口设置时间,请输入类似time:20250126080910的字符串\n");
while (1)
{
uint8_t t1[50] = {0}, t2[50] = {0}, t3[50] = {0}, t4[50] = {0};
if (rtc_flag)
{
g_rtc0.p_api->calendarTimeGet(&g_rtc0_ctrl, &get_time);
rtc_flag = 0;
sprintf((char *)t1, "%4d.%02d.%02d",
get_time.tm_year + 1900, get_time.tm_mon + 1, get_time.tm_mday);
sprintf((char *)t2, "%02d:%02d:%02d",
get_time.tm_hour, get_time.tm_min, get_time.tm_sec);
if (bme.initialized)
{
BME280_Get_Data(&bme);
sprintf((char *)t3, "%.1fC %.1f%%RH", bme.temp, bme.humi);
sprintf((char *)t4, "%.1fhPa", bme.press);
OLED_ShowString(12, 32, t3, 16);
OLED_ShowString(24, 48, t4, 16);
}
OLED_ShowString(24, 0, t1, 16);
OLED_ShowString(32, 16, t2, 16);
}
if (uart_rx_complete_flag)
{
char *time;
uart_rx_complete_flag = 0;
if (strncmp(rx_data, "time:", 5) == 0)
{
time = rx_data + 5;
set_time.tm_year = ((time[0] - '0') * 1000) + ((time[1] - '0') * 100) +
((time[2] - '0') * 10) + (time[3] - '0') - 1900;
set_time.tm_mon = ((time[4] - '0') * 10) + (time[5] - '0') - 1;
set_time.tm_mday = ((time[6] - '0') * 10) + (time[7] - '0');
set_time.tm_hour = ((time[8] - '0') * 10) + (time[9] - '0');
set_time.tm_min = ((time[10] - '0') * 10) + (time[11] - '0');
set_time.tm_sec = ((time[12] - '0') * 10) + (time[13] - '0');
g_rtc0.p_api->calendarTimeSet(&g_rtc0_ctrl, &set_time);
}
else{
printf("若要通过串口设置时间,请输入类似time:20250126080910的字符串\n");
}
}
}
:::
这段程序实现了每1秒刷新一次OLED屏幕上的时间和温湿度气压数据,同时能从串口接收格式化的数据以设定时间。
4.下载测试
把编译好的程序下载到开发板并复位。观察到OLED屏幕上正确显示了预设的时间和获取到的温湿度气压值。
可以打开串口助手,在发送框输入
time:20250128235958
工程附件
把代码全贴上来的话超字数限制了,请下载附件查看完整代码
*附件:04_OLED_BME280-I2C.zip