w494143467 发表于 2021-6-13 15:46

【环境专家之智能手表】Part4:OLED驱动及界面自动切换

<p><strong>1.介绍</strong></p>

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

<p><strong>2.界面设计</strong></p>

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

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图1</p>

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

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图2</p>

<p><strong>3.设计过程</strong></p>

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

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图3</p>

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

<pre>
<code class="language-cpp">static void oled_write_dats(uint8_t *oled_dat, uint8_t len)
{
    uint32_t status = 0;

    uint8_t send_data = {0};

    send_data = 0x40;

    memcpy(&amp;send_data, oled_dat, len);

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

}

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

    uint8_t send_data = {0x00, oled_command};

    i2c-&gt;MasterTransmit(OLED_ADDR, send_data, 2, false);
    while (i2c-&gt;GetStatus().busy);
}</code></pre>

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

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

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图4</p>

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

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

<pre>
<code class="language-cpp">void SCAN_APP_Initialize(void)
{
    /* Initialize UART, register callback function and set mode/baud rate */
    uart = &amp;Driver_USART0;
    uart-&gt;Initialize(SCAN_UART_EventHandler);
    uart-&gt;PowerControl(ARM_POWER_FULL);
    uart-&gt;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;
}</code></pre>

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

<pre>
<code class="language-cpp">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)
{
</code></pre>

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

<pre>
<code class="language-cpp">//显示指定编号的人员信息,被定时器或者任务调用
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 &gt;= go_well_cnt)        //没有该编号的人员
                return;

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

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

        oled_config_num2str(&amp;rssi_display, go_well_poeple.rssi_value, 3);
        oled_config_num2str(&amp;tem_display, go_well_poeple.temp_value, 2);
        oled_config_num2str(&amp;hum_display, go_well_poeple.humidity, 3);
        oled_config_num2str(&amp;ill_display, go_well_poeple.light_value, 3);
        oled_config_num2str(&amp;pre_display, go_well_poeple.pressure, 3);

        OLED_Clear();        //清屏

        OLED_ShowString(0, 0, go_well_poeple.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);
}</code></pre>

<p>最终的效果如下图所示。</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图5</p>

<p><strong>4.结论</strong></p>

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

Jacktang 发表于 2021-6-13 21:26

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

<p>期待楼主下面的设备之间的通信测试</p>

reayfei 发表于 2021-6-26 14:09

<p>cool</p>
页: [1]
查看完整版本: 【环境专家之智能手表】Part4:OLED驱动及界面自动切换