你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32+RT-Thread的新冠肺炎疫情监控平台

[复制链接]
STMCU 发布时间:2020-8-25 17:05
基于STM32+RT-Thread的新冠肺炎疫情监控平台


上周末加班,这周末休息,有时间整理一篇之前做的基于RT-Thread的疫情监控平台。上一篇文章我们使用STM32F103 MCU裸机开发的方式实现了疫情监控平台。这次我们玩点高端的,使用RT-Thread Studio来实现同样的功能,一起来看看吧!


·文章目录
·使用到的软件包
·0.RT-Thread Studio的下载和安装


1.硬件准备
2.新建工程
3.添加LED闪烁功能
4.添加ESP8266软件包
5.疫情数据的获取
6.疫情数据的解析
7.疫情数据的显示
开源地址


最终的显示效果:
1.png
显示效果
有效文件就这9个,其他的就全是图形化配置:
2.png
有效文件
整个流程下来,如果顺利的话,可以在2个小时内完成。

使用到的软件包
·at device:用于ESP8266配网
·webclient:用于发送HTTPS请求
·mbdetls:用于HTTPS加密
·cJSON:用于JSON数据解析

0.RT-Thread Studio的下载和安装
一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效
3.png
RT-Thread Studio
·支持多种芯片,STM32全系列
·支持创建裸机工程、RT-Thread Nano和Master工程
·强大的代码编辑功能,基于Eclipse框架
·免费无版权限制,基于开源Eclipse和ARM-GCC编译器。
·支持多种仿真器,J-Link,ST-link等,支持在线调试,变量观察。
·SDK管理器,图形化配置RT-Thread软件包,同步RT-Thread最新版本。
·集成Putty串口终端工具


更多的使用教程:
http://www.rt-thread.org/page/studio.html


目前最新版本为1.1.3版本,支持3种下载方式,我们选择最后一个下载方式,从RT-Thread 官网服务器上下载。


下载地址:


http://117.143.63.254:9012/www/studio/download/RT-Thread%20Studio-v1.1.3-setup-x86_64_20200731-2100.exe


4.png
下载链接
安装过程和常用的软件安装方法一样,选择安装路径,然后Next就行了。


1.硬件准备
开发板用的是我在大四时自己设计的STM32开发板——NiceDay,基于STM32F103RET主控。 这是我设计的第二块板子(第一块是毕业设计两轮平衡车主板),是在大四快毕业时,毕设实物和论文完成之后还有点时间,就设计了这款板子,最开始是准备做桌面天气时钟的。


5.png
开发板
2.新建工程
RT-Thread Studio支持创建裸机工程、包含RT-Thread Nano版本的工程和包含Master版本的工程。这里,我们选择创建RT-Thread 项目,即包含完整版RT-Thread的工程。
6.png
新建项目
工程支持基于芯片创建工程,或者基于已有的BSP创建,这里使用的是我自己设计的开发板,所以选择基于芯片,选择芯片型号:STM32F103RE,调试串口选择串口1,调试器选择J-Link,SWD接口。
7.png
新建项目
创建完成之后,直接按Ctrl+B编译整个工程,第一次编译时间会长一点,如果修改很少,下次再进行编译就会很快了,可以看到无警告无错误。
8.png
编译结果
使用SWD接口连接JLink调试器和开发板,开发板上电,直接点击下载按钮,也可以使用快捷键Ctrl+Alt+D下载
9.png
下载程序
底部可以看到下载信息,从LOG来看,下载的程序文件是Bin文件,比较,擦除,编程,验证,复位整个流程耗时13s左右。
10.png
下载LOG
RT-Thread Studio是自带Putty串口终端的,点击终端图标:
11.png
终端按钮
选择串口号、波特率、文字编码方式等。
12.png
配置终端
底部切换到终端窗口,可以看到串口终端输出信息:
13.png
串口终端
这样,不到5分钟,一个基于STM32F103RET6的工程模板就创建好了,包含RT-Thread完整版操作系统,整个过程不需要写一行代码,完全图形化配置。


3.添加LED闪烁功能
作为单片机点灯小能手,RT-Thread下如何点灯是必须掌握的。打开RT-Thread组件图形化配置界面,可以看到默认开启了PIN和串口设备驱动的。
14.png
图形化配置界面
在main.c文件中添加LED闪烁功能。包含头文件和添加宏定义
  1. #include <board.h>
  2. #include <rtdevice.h>

  3. #define LED_RED_PIN     GET_PIN(A, 7)
  4. #define LED_BLUE_PIN    GET_PIN(A,6)

  5. int main(void)
  6. {
  7.     int count = 1;
  8.     rt_pin_mode(LED_RED_PIN, PIN_MODE_OUTPUT);
  9.     rt_pin_mode(LED_BLUE_PIN, PIN_MODE_OUTPUT);

  10.     while (count++)
  11.     {
  12.         rt_pin_write(LED_BLUE_PIN, PIN_LOW);
  13.         rt_pin_write(LED_RED_PIN, PIN_LOW);
  14.         rt_thread_mdelay(100);
  15.         rt_pin_write(LED_BLUE_PIN, PIN_HIGH);
  16.         rt_pin_write(LED_RED_PIN, PIN_HIGH);
  17.         rt_thread_mdelay(100);
  18.     }
  19.     return RT_EOK;
  20. }
复制代码
重新编译,下载。可以看到LED闪烁起来了。工程默认是使用内部RC作为输入时钟,所以无论你的板子是8M还是12M,都可以正常闪烁。我的开发板是8M晶体,这里我们配置使用外部HSE作为输入时钟。


打开drivers->stm32f1xx_hal_conf.h文件,修改HSE_VALUE宏定义为8M。
15.png
晶体频率修改
打开drivers->drv_clk.c文件:
16.png
时钟源修改
配置PLL时钟源为HSE,并设置倍频系数为9。
17.png
时钟源修改
18.png
倍频系数
这里根据实际板子晶体频率来设置,如果是12M晶体,倍频系数应该设置为6,如果是16M,需要参考时钟树,先2倍分频,然后9倍倍频。
  1. #include <rtdbg.h>

  2. void system_clock_config(int target_freq_Mhz)
  3. {
  4.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};

  5.     /** Initializes the CPU, AHB and APB busses clocks
  6.     */
  7.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  8.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  9.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  10.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  11.      ........   
  12.     //9倍频
  13.     RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;   //8*9=72M
  14.     ........
  15. }
复制代码
这样就修改为外部8M晶体作为PLL时钟源,再次编译下载,和之前的现象是一样的。


4.添加ESP8266软件包
联网设备,我们选择的是ESP8266-01S,如果看过上一篇疫情监控三部曲——在STM32F103 MCU上实现(裸机版),里面介绍了如何配置ESP8266 GET HTTPS请求, 配置工作模式 > 连接WiFi > 与服务器建立SSL连接 > 发送GET请求获取数据等等,整个流程固定而繁琐,那么能不能封装成一个模块,直接拿来使用呢?
19.png
esp8266
这里就要介绍RT-Thread的AT Device软件包了,


AT device 软件包是由 RT-Thread AT 组件针对不同 AT 设备的移植文件和示例代码组成,目前支持的 AT 设备有:ESP8266、ESP32、M26、MC20、RW007、MW31、SIM800C、W60X 、SIM76XX、A9/A9G、BC26 、AIR720、ME3616、M6315、BC28、EC200X、M5311系列设备等,目前上述设备都完成对 AT socket 功能的移植,及设备通过 AT 命令实现标准 socket 编程接口,完成 socket 通讯的功能,具体功能介绍可参考 《RT-Thread 编程指南》AT 命令章节 。
http://www.rt-thread.org/document/site/programming-manual/at/at/


简单的说,就是我只需要调用这个软件包,然后修改WiFi账号和密码,就可以直接配置ESP8266联网了。


由于AT Device依赖于libc组件,所以在添加AT Device软件包之前,先开启libc。


在RT-Thread Settings中点击libc灰色图标,变成彩色说明已经开启。
20.png
组件配置
添加AT Device软件包,点击立即添加
21.png
软件包
在弹出的软件包中心,搜索at_device,然后点击添加,添加到当前工程。
22.png
软件包
在at_device软件包上右键,选择详细配置:
23.png
软件包
在弹出的页面,选择我们使用的WiFi模块类型,乐鑫的ESP8266系列,并配置WiFi账号和密码,WiFi模块所连接的串口号。
24.png
WiFi配置
点击保存之后,工程会重新进行配置,添加相应的软件包文件到当前工程,重新生成Makefile文件,rtconfig文件等等。


虽然我们在at_device配置中选择了uart2作为at_device设备连接的串口。但此时串口2并没有开启,还需要我们手动使能。


打开drivers->board.h文件,通过宏定义的方式使能串口2。
  1. #define BSP_USING_UART2
  2. #define BSP_UART2_TX_PIN       "PA2"
  3. #define BSP_UART2_RX_PIN       "PA3"
复制代码
这样就开启了UART2的片上外设,Ctrl + B重新进行编译,时间会有些长,编译完成之后,可以看到flash文件大小明显比之前大了。
25.png
编译结果
Ctrl + Alt + D重新下载运行,打开串口终端:
26.png
终端
可以看到,UART2初始化成功,WiFi连接成功。说明我们的串口模块已经可以正常工作了。提示[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!失败信息,是因为当前ESP8266的固件版本不支持AT+CIPDNS_CUR?这条命令,把固件升级到最新版本就好了。这个不影响后面的操作,所以就不用在意这个了。


测试一下ifconfig和ping命令,都是正常的。
27.png
终端
在RT-Thread Studio中配置ESP8266模块联网,整个流程只写了3行代码,可以说是非常的快速方便。


5.疫情数据的获取
WiFi模块连接上互联网之后,就可以连接GET疫情数据的API接口http://lab.isaaclin.cn/nCoV/api/overall,然后读取返回的疫情数据。在上一篇的裸机工程中,是通过先和服务器建立SSL连接,然后发送GET HTTPS请求,获取到的返回数据,那RT-Thread有没有这样功能的软件包呢?这里就需要添加另一个软件包webclient。


WebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。
WebClient 软件包功能特点如下:


·支持 IPV4/IPV6 地址;
·支持 GET/POST 请求方法;
·支持文件的上传和下载功能;
·支持 HTTPS 加密传输;
·完善的头部数据添加和处理方式。


和添加at_device一样,在软件包中心中搜索webclient,
28.png
软件包
然后添加到当前工程,右键进行配置,由于我们的http://lab.isaaclin.cn/nCoV/api/overall这个疫情数据接口是HTTPS类型的,根据软件包使用手册,我们需要选择TLS模式中的 MbedTLS。勾选添加GET和POST示例。
29.png
软件包配置
保存配置,看一下当前已经添加了哪些功能,可以看到有一些组件我们并没有去打开,但是已经被开启了,这是因为有些软件包是会依赖一些组件的,当使能软件包时,一些依赖的组件也被同时使能。
30.png
软件包
Ctrl + B编译,Ctrl + Alt + D下载运行。在终端输入web_get_test测试GET请求功能。
31.png
GET示例
可以看到,执行get命令之后,会返回一个字符串,那么GET的是哪个地址呢?打开packages->webclient-v2.1.2->samples->webclient_get_sample.c文件,
32.png
示例代码
可以看到GET的是这个地址:http://www.rt-thread.com/service/rt-thread.txt,我们用电脑上的浏览器访问一下:
33.png
浏览器访问
经过实际测试发现,GET HTTPS请求,还需要使能软件模拟RTC这个组件,否则会报assertion failed at function:gettimeofday, line number:19错误。
34.png
使能RTC
我们重新写一个获取疫情数据的函数,并导出到MSH。


usr_ncov.c文件内容
  1. //usr_ncov.c
  2. #include "usr_ncov.h"

  3. int get_NCOV_Data(void)
  4. {
  5.     char *uri = RT_NULL;
  6.     struct webclient_session* session = RT_NULL;
  7.     uint8_t *buffer = RT_NULL;
  8.     int index, ret = 0;
  9.     int bytes_read, resp_status;
  10.     int content_length = -1;
  11.     int buffer_size = 1600;
  12.     uri = web_strdup(API_NCOV);
  13.     rt_kprintf("start get api: %s\r\n", API_NCOV);
  14.     if(uri != RT_NULL)
  15.     {
  16.         buffer = (unsigned char *) web_malloc(buffer_size);
  17.         if (buffer == RT_NULL)
  18.         {
  19.             rt_kprintf("no memory for receive buffer.\n");
  20.             ret = -RT_ENOMEM;
  21.             goto __exit;
  22.         }

  23.         /* create webclient session and set header response size */
  24.         session = webclient_session_create(buffer_size);
  25.         if (session == RT_NULL)
  26.         {
  27.             ret = -RT_ENOMEM;
  28.             goto __exit;
  29.         }

  30.         /* send GET request by default header */
  31.         if ((resp_status = webclient_get(session, uri)) != 200)
  32.         {
  33.             rt_kprintf("webclient GET request failed, response(%d) error.\n", resp_status);
  34.             ret = -RT_ERROR;
  35.             goto __exit;
  36.         }

  37.         rt_kprintf("webclient get response data: \n");

  38.         content_length = webclient_content_length_get(session);
  39.         if (content_length < 0)
  40.         {
  41.             rt_kprintf("webclient GET request type is chunked.\n");

  42.             do
  43.             {
  44.                 bytes_read = webclient_read(session, buffer, buffer_size);
  45.                 if (bytes_read <= 0)
  46.                     break;

  47.                 for (index = 0; index < bytes_read; index++)
  48.                 {
  49.                     rt_kprintf("%c", buffer[index]);
  50.                 }
  51.             } while (1);

  52.             rt_kprintf("\n");
  53.         }
  54.         else
  55.         {
  56.             /* 读取服务器响应的数据 */
  57.             bytes_read = webclient_read(session, buffer, content_length);
  58.             rt_kprintf("data length:%d\n", bytes_read);

  59.             buffer[bytes_read] = '\0';
  60.             rt_kprintf("\n\n %s \n\n", buffer);
  61. //            rt_kprintf("parse data\r\n");
  62.             // parseData(buffer);        //解析函数
  63.             rt_kprintf("\n");
  64.         }

  65.         __exit:
  66.         if (session)
  67.             webclient_close(session);

  68.         if (buffer)
  69.             web_free(buffer);
  70.     }
  71.     else
  72.         rt_kprintf("api error: %s\n", API_NCOV);

  73.     return ret;
  74. }
  75. MSH_CMD_EXPORT(get_NCOV_Data, get api ncov);
复制代码
usr_ncov.h文件内容
  1. #ifndef APPLICATIONS_USR_NCOV_H_
  2. #define APPLICATIONS_USR_NCOV_H_
  3. #include <webclient.h>
  4. #include <rtdevice.h>
  5. #include <rtthread.h>
  6. #define API_NCOV     "http://lab.isaaclin.cn/nCoV/api/overall"
  7. int get_NCOV_Data(void);
  8. #endif /* APPLICATIONS_USR_NCOV_H_
复制代码
重新编译,下载,运行。在终端运行这个命令:
35.png
命令获取疫情数据
可以看到获取到了返回的数据,长度1366个字节。下一步就是对这个JSON数据进行解析,获取到我们想要的疫情数据。


6.疫情数据的解析
API返回的数据是JSON格式的,关于JSON的介绍和解析,可以查看使用cJSON库解析和构建JSON字符串。数据的解析使用的开源小巧的cJSON解析库,我们可以在软件包管理中心直接添加:
36.png
添加cJSON
在进行解析之前,先来分析一下JSON原始数据的格式:results键的值是一个数组,数组只有一个JSON对象,获取这个对象对应键的值可以获取到国内现存和新增确诊人数、累计和新增死亡人数,累计和新增治愈人数等数据。


全球疫情数据保存在globalStatistics键里,它的值是一个JSON对象,对象仅包含简单的键值对,这些键的值,就是全球疫情数据,其中updateTime键的值是更新时间,这是毫秒级UNIX时间戳,可以转换为标准北京时间。
  1. {
  2.     "results": [{
  3.         "currentConfirmedCount": 509,
  4.         "currentConfirmedIncr": 16,
  5.         "confirmedCount": 85172,
  6.         "confirmedIncr": 24,
  7.         "suspectedCount": 1899,
  8.         "suspectedIncr": 4,
  9.         "curedCount": 80015,
  10.         "curedIncr": 8,
  11.         "deadCount": 4648,
  12.         "deadIncr": 0,
  13.         "seriousCount": 106,
  14.         "seriousIncr": 9,
  15.         "globalStatistics": {
  16.             "currentConfirmedCount": 4589839,
  17.             "confirmedCount": 9746927,
  18.             "curedCount": 4663778,
  19.             "deadCount": 493310,
  20.             "currentConfirmedIncr": 281,
  21.             "confirmedIncr": 711,
  22.             "curedIncr": 424,
  23.             "deadIncr": 6
  24.         },
  25.         "updateTime": 1593227489355
  26.     }],
  27.     "success": true
  28. }
复制代码
先定义了结构体NCOV_DATA,用于存储国内和全球疫情数据:
  1. struct NCOV_DATA{
  2.     int currentConfirmedCount;
  3.     int currentConfirmedIncr;
  4.     int confirmedCount;
  5.     int confirmedIncr;
  6.     int curedCount;
  7.     int curedIncr;
  8.     int deadCount;
  9.     int deadIncr;
  10.     int seriousCount;
  11.     int seriousIncr;

  12.     char updateTime[20];
  13. };
复制代码
对应的解析函数:
  1. #include <cJSON.h>

  2. struct NCOV_DATA dataChina = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "06-13 16:22"};;
  3. struct NCOV_DATA dataGlobal = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};

  4. int parseData(uint8_t *str)
  5. {
  6.     int ret = 0;
  7.     cJSON *root, *result_arr;
  8.     cJSON *result, *global;
  9.     time_t updateTime;
  10.     struct tm *time;

  11.     root = cJSON_Parse((const char *)str);   //创建JSON解析对象,返回JSON格式是否正确

  12.     if (root != 0)
  13.     {
  14.         rt_kprintf("JSON format ok, start parse!!!\n");
  15.         result_arr = cJSON_GetObjectItem(root, "results");
  16.         if(result_arr->type == cJSON_Array)
  17.         {
  18. //            rt_kprintf("result is array\n");
  19.             result = cJSON_GetArrayItem(result_arr, 0);
  20.             if(result->type == cJSON_Object)
  21.             {
  22. //                rt_kprintf("result_arr[0] is object\n");

  23.                 /* china data parse */
  24.                 dataChina.currentConfirmedCount = cJSON_GetObjectItem(result, "currentConfirmedCount")->valueint;
  25.                 dataChina.currentConfirmedIncr = cJSON_GetObjectItem(result, "currentConfirmedIncr")->valueint;
  26.                 dataChina.confirmedCount = cJSON_GetObjectItem(result, "confirmedCount")->valueint;
  27.                 dataChina.confirmedIncr = cJSON_GetObjectItem(result, "confirmedIncr")->valueint;
  28.                 dataChina.curedCount = cJSON_GetObjectItem(result, "curedCount")->valueint;
  29.                 dataChina.curedIncr = cJSON_GetObjectItem(result, "curedIncr")->valueint;
  30.                 dataChina.deadCount = cJSON_GetObjectItem(result, "deadCount")->valueint;
  31.                 dataChina.deadIncr = cJSON_GetObjectItem(result, "deadIncr")->valueint;

  32.                 rt_kprintf("**********china ncov data**********\n");
  33.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataChina.currentConfirmedCount, "currentConfirmedIncr", dataChina.currentConfirmedIncr);
  34.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataChina.confirmedCount, "confirmedIncr", dataChina.confirmedIncr);
  35.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataChina.curedCount, "curedIncr", dataChina.curedIncr);
  36.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataChina.deadCount, "deadIncr", dataChina.deadIncr);

  37.                 /* global data parse */
  38.                 global = cJSON_GetObjectItem(result, "globalStatistics");
  39.                 if(global->type == cJSON_Object)
  40.                 {
  41.                     dataGlobal.currentConfirmedCount = cJSON_GetObjectItem(global, "currentConfirmedCount")->valueint;
  42.                     dataGlobal.currentConfirmedIncr = cJSON_GetObjectItem(global, "currentConfirmedIncr")->valueint;
  43.                     dataGlobal.confirmedCount = cJSON_GetObjectItem(global, "confirmedCount")->valueint;
  44.                     dataGlobal.confirmedIncr = cJSON_GetObjectItem(global, "confirmedIncr")->valueint;
  45.                     dataGlobal.curedCount = cJSON_GetObjectItem(global, "curedCount")->valueint;
  46.                     dataGlobal.curedIncr = cJSON_GetObjectItem(global, "curedIncr")->valueint;
  47.                     dataGlobal.deadCount = cJSON_GetObjectItem(global, "deadCount")->valueint;
  48.                     dataGlobal.deadIncr = cJSON_GetObjectItem(global, "deadIncr")->valueint;

  49.                     rt_kprintf("\n**********global ncov data**********\n");
  50.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataGlobal.currentConfirmedCount, "currentConfirmedIncr", dataGlobal.currentConfirmedIncr);
  51.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataGlobal.confirmedCount, "confirmedIncr", dataGlobal.confirmedIncr);
  52.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataGlobal.curedCount, "curedIncr", dataGlobal.curedIncr);
  53.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataGlobal.deadCount, "deadIncr", dataGlobal.deadIncr);

  54.                 } else return 1;

  55.                 /* 毫秒级时间戳转字符串 */
  56.                 updateTime = (time_t )(cJSON_GetObjectItem(result, "updateTime")->valuedouble / 1000);
  57.                 updateTime += 8 * 60 * 60; /* UTC8校正 */
  58.                 time = localtime(&updateTime);
  59.                 /* 格式化时间 */
  60.                 strftime(dataChina.updateTime, 20, "%m-%d %H:%M", time);
  61.                 rt_kprintf("update: %s\r\n", dataChina.updateTime);/* 06-24 11:21 */
  62.                 //数据在LCD显示
  63.                 //gui_show_ncov_data(dataChina, dataGlobal);
  64.             } else return 1;
  65.         } else return 1;
  66.         rt_kprintf("\nparse complete \n");
  67.     }
  68.     else
  69.     {
  70.         rt_kprintf("JSON format error:%s\n", cJSON_GetErrorPtr()); //输出json格式错误信息
  71.         return 1;
  72.     }
  73.     cJSON_Delete(root);

  74.     return ret;
  75. }
复制代码
在数据接收完成之后,对JSON数据进行解析。
37.png
解析结果
7.疫情数据的显示
数据解析出来之后,剩下的就简单了,把上一篇文章中9341的驱动文件移植过来就好了。


液晶屏使用的是3.2寸 LCD,IL9341驱动芯片,320*240分辨率,16位并口。由于屏幕分辨率比较低,可显示的内容有限,所以只是显示了最基本的几个疫情数据。为了减小程序大小,GUI只实现了基本的画点,画线函数,字符的显示,采用的是部分字符取模,只对程序中用到的汉字和字符进行取模。为了增强可移植性,程序中并没有使用外置SPI Flash存储整个字库。


由于RT-Thread Studio使用的HAL库,所以LCD的GPIO初始化函数需要修改一下:
  1. void lcd_gpio_init(void)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure;

  4.     __HAL_RCC_GPIOA_CLK_ENABLE();
  5.     __HAL_RCC_GPIOC_CLK_ENABLE();
  6.     __HAL_RCC_GPIOB_CLK_ENABLE();
  7.     __HAL_RCC_AFIO_CLK_ENABLE();
  8.     __HAL_AFIO_REMAP_SWJ_NOJTAG();

  9.     GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  10.     GPIO_InitStructure.Pull = GPIO_PULLUP;
  11.     GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
  12.     GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6;
  13.     HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOC

  14.     GPIO_InitStructure.Pin = GPIO_PIN_8;    //背光引脚PA8
  15.     HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); //GPIOC

  16.     GPIO_InitStructure.Pin = GPIO_PIN_All;
  17.     HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);

  18.     HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6, GPIO_PIN_SET);
  19.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
  20.     HAL_GPIO_WritePin(GPIOB, GPIO_PIN_All, GPIO_PIN_SET);
  21. }
复制代码
延时函数换成:
  1. rt_thread_mdelay(nms);
复制代码
还有一点,在Keil中,文字编码选择GBK编码,1个汉字占用2个字节,而RT-Thread Studio为UTF-8编码,1个汉字占用3个字节,汉字显示函数需要调整:
  1. void gui_show_chn(uint16_t x0, uint16_t y0, char *chn)
  2. {
  3.     uint8_t idx = 0;
  4.     uint8_t* code[3]; //UTF-8:国=E59BBD

  5.     uint8_t size = sizeof(FONT_16X16_TABLE) / sizeof(FONT_16X16_TABLE[0]);
  6.     /* 遍历汉字,获取索引 */
  7.     for(idx = 0; idx < size; idx++)
  8.     {
  9.         code[0] = FONT_16X16_TABLE[idx].chn;
  10.         code[1] = FONT_16X16_TABLE[idx].chn + 1;
  11.         code[2] = FONT_16X16_TABLE[idx].chn + 2;
  12.         //汉字内码一致
  13.         if(!(strcmp(code[0], chn) || strcmp(code[1], chn+1) || strcmp(code[2], chn+2)))
  14.         {
  15.             gui_show_F16X16_Char(x0, y0, idx, WHITE);
  16.             return;
  17. //            break;
  18.         }
  19.     }
  20. }
复制代码
疫情数据显示函数:
  1. void gui_show_ncov_data(struct NCOV_DATA china, struct NCOV_DATA global)
  2. {
  3.     uint8_t y0 = 20;

  4.     lcd_clear(BLACK);
  5.     gui_show_bar();

  6.     gui_drawLine(0, 18, 320, DIR_X, WHITE);
  7.     gui_drawLine(0, 38, 320, DIR_X, WHITE);
  8.     gui_drawLine(0, 138, 320, DIR_X, WHITE);
  9.     gui_drawLine(0, 158, 320, DIR_X, WHITE);
  10.     gui_drawLine(0, 220, 320, DIR_X, WHITE);

  11.     /* "国内疫情" */
  12.     gui_show_chn_string(128, y0, "国内疫情");
  13.     gui_show_line_data(40, "现存确诊:", china.currentConfirmedCount, "较昨日:", china.currentConfirmedIncr);
  14.     gui_show_line_data(60, "累计确诊:", china.confirmedCount, "较昨日:", china.confirmedIncr);
  15.     gui_show_line_data(80, "累计治愈:", china.curedCount, "较昨日:", china.curedIncr);
  16.     gui_show_line_data(100, "现存重症:", china.seriousCount, "较昨日:", china.seriousIncr);
  17.     gui_show_line_data(120, "累计死亡:", china.deadCount, "较昨日:", china.deadIncr);

  18.     /* 全球疫情 */
  19.     gui_show_chn_string(128, 140, "全球疫情");
  20.     gui_show_line_data(160, "现存确诊:", global.currentConfirmedCount, "较昨日:", global.currentConfirmedIncr);
  21.     gui_show_line_data(180, "累计治愈:", global.curedCount, "较昨日:", global.curedIncr);
  22.     gui_show_line_data(200, "累计死亡:", global.deadCount, "较昨日:", global.deadIncr);

  23.     gui_show_chn_string(160, 222, "更新于:");
  24.     gui_show_F8X16_String(230, 222, china.updateTime, GREEN);
复制代码
最终显示效果
38.png
最终效果
开源地址
代码已经开源,地址在文末,欢迎大家参与,丰富这个小项目的功能!


基于STM32+RT-Thread的疫情监控平台
http://github.com/whik/rtt_2019_ncov


基于STM32F103的疫情监控平台(裸机版)
http://github.com/whik/stm32_2019_ncov



文章出处:[color=var(--weui-LINK)][url=]RTThread物联网操作系统[/url]

收藏 1 评论7 发布时间:2020-8-25 17:05

举报

7个回答
乐天乐 回答时间:2020-8-25 18:12:29
谢谢分享
davidwyq 回答时间:2020-8-25 20:10:34
谢谢分享
子曰好人 回答时间:2020-8-26 15:43:56
厉害啦
ts2000 回答时间:2020-8-27 10:47:00
谢谢分享!!
alicekuonji 回答时间:2020-8-28 08:51:30
现在的图形化真的是方便快速开发
sumoon_yao 回答时间:2020-8-28 10:15:36
感谢分享!
laohu361 回答时间:2020-9-1 17:10:54
谢谢分享,RT-Thread也是不错的平台。

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似分享

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版