[作品提交] 【环境专家之智能手表】Part4:OLED驱动及界面自动切换

w494143467   2021-6-13 15:46 楼主

1.介绍

对于矿井外的设备需要有显示装置,主要是为了查看下井人员的一些信息,而且下井人员数量不定,所以需要动态的对下井人员进行添加,每次下井前需要通过NFC标签录入下井人员的NFC标签,NFC标签中主要存储的是名字,那么在矿井外的人员就能知道有哪些人员进入,并对下井人员的传感器数据进行显示。

2.界面设计

显示采用128x64的OLED屏幕,一行可以显示16个字符,一共有4行,那么可以显示64个字符,所有一页显示一个人的信息,然后定时切换到下一个人的信息,当有下井人员的数据异常,那么就会置顶该人员的信息,并在屏幕中给出提示信息,同时发出警报声,如果有多个人员异常,那么就会在多个异常人员进行轮询显示,正常人员显示界面如下图所示。

1.png

图1

异常人员会在名字后面添加上【!】符号,其中【!】为闪烁状态,让工作人员能够注意到。

2.png

图2

3.设计过程

首先是OLED的驱动程序,在配置中添加I2C驱动,添加之后,需要重启打开【ON Semiconductor】IED软件,要不左侧项目文件中不会显示IIC驱动文件。

3.png

图3

添加完成之后,需要进行OLED驱动的添加,创建一个新的文件夹,用于存放自己的代码,然后将之前的移植的OLED驱动代码放入新创建的文件夹中,然后修改I2C通信代码。

static void oled_write_dats(uint8_t *oled_dat, uint8_t len)
{
    uint32_t status = 0;

    uint8_t send_data[20] = {0};

    send_data[0] = 0x40;

    memcpy(&send_data[1], oled_dat, len);

    i2c->MasterTransmit(OLED_ADDR, send_data, 1+len, false);
    while (i2c->GetStatus().busy);

}

static void oled_write_cmd(uint8_t oled_command)
{
    uint32_t status = 0;

    uint8_t send_data[2] = {0x00, oled_command};

    i2c->MasterTransmit(OLED_ADDR, send_data, 2, false);
    while (i2c->GetStatus().busy);
}

其中【i2c->MasterTransmit(OLED_ADDR, send_data, 1+len, false);】为RSL10替换后的内容,其余地方无需修改,【OLED_ADDR】为设备地址,【send_data[0]】为寄存器地址,【send_data】后续的都为要写入的数据,【1+len】为长度,【false】为是否等待,默认选择不等待;

OLED程序移植完成之后,就开始完成让屏幕切换的功能了,本来想使用软定时器的(图4)。

4.png

图4

但是发现更好用的了,官方的每一个程序中,都有控制LED闪烁的功能,那么我们就来使用它提供的这个就可以了,因为对时间精度要求不是很高,所以使用这个是完全没有问题的。

怎么使用呢?我使用的例程是【ble_central_client_scan】例程,在该例程下的【source/app_scan.c】文件中有下面这么一段程序。

void SCAN_APP_Initialize(void)
{
    /* Initialize UART, register callback function and set mode/baud rate */
    uart = &Driver_USART0;
    uart->Initialize(SCAN_UART_EventHandler);
    uart->PowerControl(ARM_POWER_FULL);
    uart->Control(ARM_USART_MODE_ASYNCHRONOUS, UART_BAUDRATE);

    /* Configure application message handlers */
    MsgHandler_Add(TASK_ID_GAPM, SCAN_MsgHandler);
    MsgHandler_Add(TASK_ID_GAPC, SCAN_MsgHandler);
    MsgHandler_Add(SCAN_UART_SCREEN_UPDATE_TIMEOUT, SCAN_MsgHandler);
    MsgHandler_Add(START_CONNECTION_CMD_TIMEOUT, SCAN_MsgHandler);
    MsgHandler_Add(APP_LED_TIMEOUT, SCAN_LED_Timeout_Handler);


    /* Initialize global variables */
    adv_report_list_size = 0;
    device_selection = 0;
    cliTimerStarted = false;
}

可以看到其中有【MsgHandler_Add(...)】这个函数,其中LED和广播扫描就用到了这个功能,这个也类似与一个定时器,设置时间,到时间之后进入回调函数进行调用处理,那么我们就照抄就可以了。

MsgHandler_Add(START_SWITCH_SCREEN_TIMEOUT, Switch_Screen_Timeout_Handler);

ke_timer_set(START_SWITCH_SCREEN_TIMEOUT, TASK_APP, TIMER_SETTING_S(2));

void Switch_Screen_Timeout_Handler(ke_msg_id_t const msg_id, void const *param,
                             ke_task_id_t const dest_id, ke_task_id_t const src_id)
{

 最后来实现一下屏幕切换的功能,首先要判断一下是否有下井人员,如果没有则显示等待添加人员,再一个就是显示下井人员的序号,查看序号是否超过实际下井人员编号,如果超过则不予显示,然后就是对屏幕显示的操作了,这一部分没什么好说的,最终的代码实现如下。

//显示指定编号的人员信息,被定时器或者任务调用
void oled_config_display(uint8_t display_num)
{
	if(go_well_cnt == 0)	//无人员
	{
		OLED_Clear();	//清屏
		OLED_ShowString(16, 0, (uint8_t *)"outside mine", 16);
		OLED_ShowString(40, 2, (uint8_t *)"Device", 16);
		OLED_ShowString(0, 4, (uint8_t *)"Wating Add User.", 16);
		return;
	}

	if(display_num >= go_well_cnt)	//没有该编号的人员
		return;

	uint8_t num_display[4] = {display_num + 0x31, '/', go_well_cnt + 0x30, '\0'};	//显示当前编号
	uint8_t act_display[7] = {"act:No\0"};		//显示活动状态
	uint8_t rssi_display[9] = {"rssi:000\0"};	//显示信号强度
	uint8_t tem_display[8] = {"tem:00C\0"};		//显示温度
	uint8_t hum_display[9] = {"hum:000%\0"};	//显示湿度
	uint8_t ill_display[8] = {"ill:000\0"};		//显示光照强度
	uint8_t pre_display[9] = {"pre:000P\0"};	//显示压力

	//显示活动状态
	switch(go_well_poeple[display_num].activity)
	{
	case WALK:
		act_display[4] = 'W';
		act_display[5] = 'l';
		break;
	case WORK:
		act_display[4] = 'W';
		act_display[5] = 'k';
		break;
	}

	oled_config_num2str(&rssi_display[5], go_well_poeple[display_num].rssi_value, 3);
	oled_config_num2str(&tem_display[4], go_well_poeple[display_num].temp_value, 2);
	oled_config_num2str(&hum_display[4], go_well_poeple[display_num].humidity, 3);
	oled_config_num2str(&ill_display[4], go_well_poeple[display_num].light_value, 3);
	oled_config_num2str(&pre_display[4], go_well_poeple[display_num].pressure, 3);

	OLED_Clear();	//清屏

	OLED_ShowString(0, 0, go_well_poeple[display_num].name, 16);
	OLED_ShowString(104, 0, num_display, 16);
	OLED_ShowString(0, 2, act_display, 16);
	OLED_ShowString(64, 2, rssi_display, 16);
	OLED_ShowString(0, 4, tem_display, 16);
	OLED_ShowString(64, 4, hum_display, 16);
	OLED_ShowString(0, 6, ill_display, 16);
	OLED_ShowString(64, 6, pre_display, 16);
}

最终的效果如下图所示。

5.gif

图5

4.结论

其实RSL10的代码框架还是非常不错的,只是我对软件框架和IDE不太熟悉,就算不是很熟悉,开发起来也没有太多的坑,也没发现什么致命BUG,这里赞一个!下一篇就是设备之间的通信了。

回复评论 (2)

这个测试可以认真做一下实际应用在矿井下,会有很大意义

期待楼主下面的设备之间的通信测试

点赞  2021-6-13 21:26

cool

点赞  2021-6-26 14:09
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复