1.介绍
对于矿井外的设备需要有显示装置,主要是为了查看下井人员的一些信息,而且下井人员数量不定,所以需要动态的对下井人员进行添加,每次下井前需要通过NFC标签录入下井人员的NFC标签,NFC标签中主要存储的是名字,那么在矿井外的人员就能知道有哪些人员进入,并对下井人员的传感器数据进行显示。
2.界面设计
显示采用128x64的OLED屏幕,一行可以显示16个字符,一共有4行,那么可以显示64个字符,所有一页显示一个人的信息,然后定时切换到下一个人的信息,当有下井人员的数据异常,那么就会置顶该人员的信息,并在屏幕中给出提示信息,同时发出警报声,如果有多个人员异常,那么就会在多个异常人员进行轮询显示,正常人员显示界面如下图所示。
图1
异常人员会在名字后面添加上【!】符号,其中【!】为闪烁状态,让工作人员能够注意到。
图2
3.设计过程
首先是OLED的驱动程序,在配置中添加I2C驱动,添加之后,需要重启打开【ON Semiconductor】IED软件,要不左侧项目文件中不会显示IIC驱动文件。
图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
但是发现更好用的了,官方的每一个程序中,都有控制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
4.结论
其实RSL10的代码框架还是非常不错的,只是我对软件框架和IDE不太熟悉,就算不是很熟悉,开发起来也没有太多的坑,也没发现什么致命BUG,这里赞一个!下一篇就是设备之间的通信了。