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

基于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扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版