本文是介绍如何接收手机发送的时间戳,解析后显示在OLED屏幕上。
本次我选择用IIC驱动0.91寸的OLED(分辨率128 * 32),来手机通过蓝牙发送的时间戳,解析后在OLED上当前显示时间。
1、驱动OLED屏幕
这种OLED屏幕是某宝最容易找到的屏幕之一,科普什么的,去百度,我就不赘述了。
屏幕的驱动IC是 SSD1306,所以我们驱动这块屏幕是控制主控把我们想要现实的内容按SSD1306能够识别的格式整理后,通过IIC发送给 SSD1306 ,接到数据后SSD1306会按照对应的格式解析 并 显示在屏幕上。
和大多数传感器一样我们需要初始化OLED屏幕,设定分辨率,左上角坐标、刷图方式等等,以下是我找到的初始化参数。
PS:注意每一款屏幕的初始化参数都是不一致的!!需要确认参数是否可用。
0xAE //--display off
0x40 //---set low column address
0xB0 //---set high column address
0xC8 //-not offset
0x81 // contract control
0xFF //--128
0xA1 //set segment remap
0xA6 //--normal / reverse
0xA8 //--set multiplex ratio(1 to 64)
0x1F
0xD3 //-set display offset
0x00
0xD5 //set osc division
0xF0
0xD9 //Set Pre-Charge Period
0x22
0xDA //set com pin configuartion
0x02
0xDB //set Vcomh
0x49
0x8D //set charge pump enable
0x14
0xAF //--turn on oled panel
具体代表什么,可用对照SSD1306的规格书,一一理解。
将以上的数据通过IIC正确发送数据后,就可以成功初始化屏幕了。下图是屏幕初始化成功的效果。
至此我们就验证了IIC 和 屏幕通讯正常,初始化正常后就移植gui代码,就可以显示任意数据了。
后续我会写文章一起讨论的,本次先跳过。
2、解析时间戳
C语言的标准库里有解析时间戳的函数,我们按照规范调用即可,有人应该会问时间戳是什么吧!
什么是时间戳?
Unix 时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。
时间戳只表示1970年1月1日到当前的秒数,但是本地的时间通常不能直接时间戳来转化,除非你恰好在0时区。
我们初中地理就有学过时区概念,时区是为了按统一标准分区计时,将地球表面按经线分为24区。 每一时区的经度宽为15度,每区按中央子午线上的时间作为该区的标准时,具体实施中往往根据各国的行政区界或自然界线来确定。
中国使用东8区(中国幅员辽阔,跨越了多个时区,选择东8区作为统一时间,是应为北京在东8区),所以我们使用时间戳需要加上8小时的秒数(28,800),才能转化为本地时间!!
time 标准库
time.h 头文件定义了四个变量类型、两个宏和各种操作日期和时间的函数,其中我们要用到一个叫
tm 的结构体 和 localtime这个函数
tm 结构的定义如下:
struct tm {
int tm_sec; /* 秒,范围从 0 到 59 */
int tm_min; /* 分,范围从 0 到 59 */
int tm_hour; /* 小时,范围从 0 到 23 */
int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
int tm_mon; /* 月,范围从 0 到 11 */
int tm_year; /* 自 1900 年起的年数 */
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时 */
};
具体实现的代码如下:
struct tm *ts;
time_t now;
char str_time[80];
now=1688628481;
ts = localtime( &now );
sprintf(str_time,"%04d-%02d-%02d %02d:%02d:%02d",
ts->tm_year+1900,
ts->tm_mon+1,
ts->tm_mday,
ts->tm_hour+8, //时区要加8
ts->tm_min,
ts->tm_sec );
调用localtime解析时间戳后,赋值给ts这个结构体,然后再将得到的时间,拼接、处理后得到我们想要显示的数据!!
注意调用localtime转化出来的数据中的,年是当前年减去1900,月是当前月减去1,而且是0时区 的小时。所以年+1900,月+1,而且在东8区,还要把小时+8,这样处理后才是正常显示的中国本地时间。
3、接收手机下发的时间
结合前文 ,我们已经学会了使用蓝牙下发数据,并判断是什么数据,来控制点灯。在本文里我会使用手机下发时间戳,WB55接收后,将数据按照格式拼接起来,并且赋值给全局变量后显示在屏幕上,完成时间同步功能。
发送数据
不同设备之间的数据传输会涉及数据发送的大小端的问题,可以理解为顺序 和 逆序发送数据。
我写此文章时 时间戳是 1690176600 对应的北京时间是 2023-07-24 13:30:00。
对应的16进制数是64 BE 0C 58
接收数据
手机发送的数据都是U8的,所以我们需要把接收到的数据,拼接处理后,才能传参给localtime这个函数。
custom_stm.c
case ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE:
/* USER CODE BEGIN EVT_BLUE_GATT_ATTRIBUTE_MODIFIED_BEGIN */
/* USER CODE END EVT_BLUE_GATT_ATTRIBUTE_MODIFIED_BEGIN */
attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data;
if (attribute_modified->Attr_Handle == (CustomContext.CustomBle_Stm32Hdle + CHARACTERISTIC_VALUE_ATTRIBUTE_OFFSET))
{
return_value = SVCCTL_EvtAckFlowEnable;
/* USER CODE BEGIN CUSTOM_STM_Service_1_Char_1_ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE */
printf(" ----- %02x %02x %02x %02x -----\n",
attribute_modified->Attr_Data[0],attribute_modified->Attr_Data[1],
attribute_modified->Attr_Data[2],attribute_modified->Attr_Data[3]);
extern uint32_t timestamp;
timestamp=((uint32_t)attribute_modified->Attr_Data[0] << 24) |
((uint32_t)attribute_modified->Attr_Data[1] << 16) |
((uint32_t)attribute_modified->Attr_Data[2] << 8) |
((uint32_t)attribute_modified->Attr_Data[3]);
get_time(timestamp);
/* USER CODE END CUSTOM_STM_Service_1_Char_1_ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE */
} /* if (attribute_modified->Attr_Handle == (CustomContext.CustomBle_Stm32Hdle + CHARACTERISTIC_VALUE_ATTRIBUTE_OFFSET))*/
/* USER CODE BEGIN EVT_BLUE_GATT_ATTRIBUTE_MODIFIED_END */
/* USER CODE END EVT_BLUE_GATT_ATTRIBUTE_MODIFIED_END */
break;
接收数据的LOG
细说处理数据
手机发送的数据都是U8的,所以我们需要把接收到的数据,拼接处理后
timestamp=((uint32_t)attribute_modified->Attr_Data[0] << 24) |
((uint32_t)attribute_modified->Attr_Data[1] << 16) |
((uint32_t)attribute_modified->Attr_Data[2] << 8) |
((uint32_t)attribute_modified->Attr_Data[3]);
以上的代码是将接收到的U8强转成U32,然后左移之后,按照大端序拼接成一个U32的数据。
接收前后显示效果
前
后
视频效果