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

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

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

% w" S- F) d8 G) q1 D8 F, O& L% L
7 {% b# w7 S6 ^! Y* ?" ^
上周末加班,这周末休息,有时间整理一篇之前做的基于RT-Thread的疫情监控平台。上一篇文章我们使用STM32F103 MCU裸机开发的方式实现了疫情监控平台。这次我们玩点高端的,使用RT-Thread Studio来实现同样的功能,一起来看看吧!; l: o1 C1 Z5 X% ?/ m: Q
+ |$ C7 o1 d# P) p% \* r

* _) ~; w+ a8 B5 ^4 w; v) ~·文章目录1 K% A# I1 w0 \7 w' {
·使用到的软件包8 T! j# a5 ?0 i. \' v6 E
·0.RT-Thread Studio的下载和安装0 ]/ r5 @  h  g% _9 f
: r; m3 Q9 N( @5 t) ^" t  R& h

! k% y9 [  P4 V2 {& S1.硬件准备7 G( }/ O) X+ E% A% z
2.新建工程
9 V: p4 @) n& d- e+ R3.添加LED闪烁功能
8 H: T/ }" w" N4.添加ESP8266软件包
3 M3 Y/ Z2 r6 B' _) P5.疫情数据的获取5 h+ h$ C3 Z/ R- @# X
6.疫情数据的解析% m! T( r9 i: O9 t; l! A
7.疫情数据的显示0 J5 ?, I0 `% U5 U6 n
开源地址
+ x6 D) W' z) S5 x) |7 I) J: T
, g6 s% ~) l2 L

+ P. F( j5 Y: k  o# L  Z最终的显示效果:' s3 a; m) M2 Z' y' V
1.png
显示效果
有效文件就这9个,其他的就全是图形化配置:
+ ^' u% C9 m8 C- Z2 [! ?
2.png
有效文件
整个流程下来,如果顺利的话,可以在2个小时内完成。+ Q0 W( N* p( d8 o

& F  D; t- r! l" v" E: H使用到的软件包
* U$ l8 y- N% b  s1 \, A·at device:用于ESP8266配网: g  f, `, e. E' n8 {  z- T9 d. q, _
·webclient:用于发送HTTPS请求
# a+ j, F5 @% s" n; \1 h: J! A·mbdetls:用于HTTPS加密
9 n. Q% J/ l4 K. U- E# v·cJSON:用于JSON数据解析; G" k. h" q2 q9 }5 f) d. w
1 I3 A% G1 j/ L' J
0.RT-Thread Studio的下载和安装
* ^2 I/ q. Q$ e) O
一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效
3.png
RT-Thread Studio
·支持多种芯片,STM32全系列& w0 Z6 c, y& z9 I& _- u+ }) p
·支持创建裸机工程、RT-Thread Nano和Master工程
2 u1 O: r! V- ?5 w% Y# v' f  K3 a·强大的代码编辑功能,基于Eclipse框架
2 y0 G; y: O! J7 {1 r·免费无版权限制,基于开源Eclipse和ARM-GCC编译器。, Z1 n, g5 Y; u7 b: w5 M& u
·支持多种仿真器,J-Link,ST-link等,支持在线调试,变量观察。
; L8 _2 t1 N, B# p. O·SDK管理器,图形化配置RT-Thread软件包,同步RT-Thread最新版本。( V: a0 I  O, N: ]; ]. M$ j; C* q
·集成Putty串口终端工具
+ M1 ]$ V0 h* ^- P$ d0 J: l& `% m7 W. L, m1 u4 e/ E- G
; j7 x+ F+ C5 x! K
更多的使用教程:& e5 i9 p9 f; V+ ^
http://www.rt-thread.org/page/studio.html; w$ |$ x, l7 F! N8 y
$ G: |* f. h& e& k. ^1 Q
. q% Z$ S$ [( ]5 @
目前最新版本为1.1.3版本,支持3种下载方式,我们选择最后一个下载方式,从RT-Thread 官网服务器上下载。
* Q2 F. M: u# n+ [% H0 O& B" G& `
/ x) |, W* Z( ^) p" f: H4 ^

$ m. Y5 B  M) e& X下载地址:0 x- J6 G8 t5 B% b7 d! w, q
  D" g4 _3 @1 ]. C0 U
" ^( f9 E% U: s+ M4 M5 L
http://117.143.63.254:9012/www/studio/download/RT-Thread%20Studio-v1.1.3-setup-x86_64_20200731-2100.exe9 E4 W0 Y6 k" C! s# ^. y
% Q% `/ w5 N# t, r* u" F( r8 h
; u) ~3 ~$ W7 V! J# q2 C
4.png
下载链接
安装过程和常用的软件安装方法一样,选择安装路径,然后Next就行了。4 f. r5 J0 h# Q7 h. }9 @
, \! N% p4 G; `

- b% H) k) D2 X5 ^; O: q7 q# I0 p1.硬件准备/ D5 G( M4 J5 v0 Z
开发板用的是我在大四时自己设计的STM32开发板——NiceDay,基于STM32F103RET主控。 这是我设计的第二块板子(第一块是毕业设计两轮平衡车主板),是在大四快毕业时,毕设实物和论文完成之后还有点时间,就设计了这款板子,最开始是准备做桌面天气时钟的。
- z5 @" W# [+ d( f' F( f+ {. R: x
$ B5 t( A9 C6 Z  V$ z
5.png
开发板
2.新建工程7 O3 K, g! R* g/ t
RT-Thread Studio支持创建裸机工程、包含RT-Thread Nano版本的工程和包含Master版本的工程。这里,我们选择创建RT-Thread 项目,即包含完整版RT-Thread的工程。  u5 @# h6 k& s- t3 A" W3 d
6.png
新建项目
工程支持基于芯片创建工程,或者基于已有的BSP创建,这里使用的是我自己设计的开发板,所以选择基于芯片,选择芯片型号:STM32F103RE,调试串口选择串口1,调试器选择J-Link,SWD接口。, S* }& Z' N: A/ D7 L
7.png
新建项目
创建完成之后,直接按Ctrl+B编译整个工程,第一次编译时间会长一点,如果修改很少,下次再进行编译就会很快了,可以看到无警告无错误。
2 T% O. m  d; r/ f
8.png
编译结果
使用SWD接口连接JLink调试器和开发板,开发板上电,直接点击下载按钮,也可以使用快捷键Ctrl+Alt+D下载. F9 f* @  X4 E3 v; W- `; t
9.png
下载程序
底部可以看到下载信息,从LOG来看,下载的程序文件是Bin文件,比较,擦除,编程,验证,复位整个流程耗时13s左右。6 M* r  t$ t2 o  ?
10.png
下载LOG
RT-Thread Studio是自带Putty串口终端的,点击终端图标:
9 L3 V0 p) I" }6 Q0 W% [+ _
11.png
终端按钮
选择串口号、波特率、文字编码方式等。9 _# }& I" \/ ^+ p1 Q
12.png
配置终端
底部切换到终端窗口,可以看到串口终端输出信息:
4 l; e/ Z$ p3 l$ {" ?% ^
13.png
串口终端
这样,不到5分钟,一个基于STM32F103RET6的工程模板就创建好了,包含RT-Thread完整版操作系统,整个过程不需要写一行代码,完全图形化配置。4 e/ U' g6 y/ h7 l7 I% i& p7 P% `

" r2 \' w7 g' R1 a

) ^8 y: J' D1 D, T5 F1 |3.添加LED闪烁功能( r- a; Q' H+ r2 C4 N. |* B+ F
作为单片机点灯小能手,RT-Thread下如何点灯是必须掌握的。打开RT-Thread组件图形化配置界面,可以看到默认开启了PIN和串口设备驱动的。
& u# w( v5 w2 {" [
14.png
图形化配置界面
在main.c文件中添加LED闪烁功能。包含头文件和添加宏定义
, G$ T" s# J$ O* F9 E. R
  1. #include <board.h>9 j- U5 R8 A/ B9 z( v# l7 R
  2. #include <rtdevice.h>4 e% M1 p2 R4 q8 \4 f
  3. * \/ C9 G, A/ U$ ^+ G! V
  4. #define LED_RED_PIN     GET_PIN(A, 7)' h- K* Q% Y/ [- E; P
  5. #define LED_BLUE_PIN    GET_PIN(A,6)
    % c3 p3 u( D3 i  a. h9 K, N

  6. 7 V! y8 B! |+ t0 Y% f/ R
  7. int main(void)  c0 W$ `' t; I( h8 l( A
  8. {
    ) B$ c9 e, d6 t* J* u% G- Q# e
  9.     int count = 1;2 r" [; x6 n; N6 }
  10.     rt_pin_mode(LED_RED_PIN, PIN_MODE_OUTPUT);5 I8 `9 Q6 B1 Z
  11.     rt_pin_mode(LED_BLUE_PIN, PIN_MODE_OUTPUT);
    . ?& J7 M9 V8 r0 f9 Q& f. |( B$ d
  12. / K5 G! V0 W; ?! }9 {4 I0 D  m3 k2 t/ k
  13.     while (count++)
    / t6 ?; r3 i6 n0 Q* [, U" @
  14.     {
    2 [+ q4 X/ D0 g4 h& k& R
  15.         rt_pin_write(LED_BLUE_PIN, PIN_LOW);( z# D% M9 _. s( y8 l  P( \3 ^0 }
  16.         rt_pin_write(LED_RED_PIN, PIN_LOW);$ d' c% }: ~3 L
  17.         rt_thread_mdelay(100);* a7 t$ q& I3 _0 o$ {2 n$ x; R7 d# z
  18.         rt_pin_write(LED_BLUE_PIN, PIN_HIGH);
    6 e$ d$ m) i3 v, Y. S, O
  19.         rt_pin_write(LED_RED_PIN, PIN_HIGH);
    # ~0 H% X$ \5 g2 v, R3 m
  20.         rt_thread_mdelay(100);" y$ B$ O. r6 _# D4 k5 G
  21.     }2 O+ `( E3 h; L
  22.     return RT_EOK;
    & g0 U. v2 Z0 g
  23. }
复制代码
重新编译,下载。可以看到LED闪烁起来了。工程默认是使用内部RC作为输入时钟,所以无论你的板子是8M还是12M,都可以正常闪烁。我的开发板是8M晶体,这里我们配置使用外部HSE作为输入时钟。2 Z& o. ^. S! E2 j

/ k+ C2 I" p7 o# x2 ^; Y
! e: I$ ]9 w$ y2 d, ~
打开drivers->stm32f1xx_hal_conf.h文件,修改HSE_VALUE宏定义为8M。
0 i) f' R: e( I6 |5 N) c: b0 f
15.png
晶体频率修改
打开drivers->drv_clk.c文件:
- L& ~3 y. n: N0 \3 K% H
16.png
时钟源修改
配置PLL时钟源为HSE,并设置倍频系数为9。0 s9 m) s4 B( D+ G/ `0 g2 B
17.png
时钟源修改
18.png
倍频系数
这里根据实际板子晶体频率来设置,如果是12M晶体,倍频系数应该设置为6,如果是16M,需要参考时钟树,先2倍分频,然后9倍倍频。
3 B+ e9 ~$ ~; Y+ V# a2 c
  1. #include <rtdbg.h># x$ m7 f) Z2 }" e

  2. $ {" a' [2 K9 ?' z5 b
  3. void system_clock_config(int target_freq_Mhz)
    2 h* U! F& x8 u7 t* z7 D* a
  4. {
    9 }: T- ]+ D8 d* d5 @) i
  5.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};* Y+ R! S# F. r" b% X
  6. 7 ~; ]) l; Q& |, K
  7.     /** Initializes the CPU, AHB and APB busses clocks" F3 Q+ a2 q6 E: W7 Q# J
  8.     */) g% H3 \- G# P& e( i
  9.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    ( D2 \% R: L; N7 ?' m
  10.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;$ j0 q. ]2 t, E. j3 {- c
  11.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;# @* p. K) _, _5 d. ^$ e
  12.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;$ n2 ~2 E, k: ]# J+ V5 Y
  13.      ........   
    : V0 w/ h/ U/ |8 `5 L2 Q
  14.     //9倍频
    5 I$ z$ ^& L+ R. ~. r
  15.     RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;   //8*9=72M% t) N/ h- C4 j3 v3 v, J8 c! p
  16.     ......../ v: [' M, M. a3 o
  17. }
复制代码
这样就修改为外部8M晶体作为PLL时钟源,再次编译下载,和之前的现象是一样的。
# A, P1 \% \! e7 @7 P. b3 X5 u9 M# y
# @, ~( h- @0 {2 p% `, D
4.添加ESP8266软件包: r' y- o( D, S9 C& [) s
联网设备,我们选择的是ESP8266-01S,如果看过上一篇疫情监控三部曲——在STM32F103 MCU上实现(裸机版),里面介绍了如何配置ESP8266 GET HTTPS请求, 配置工作模式 > 连接WiFi > 与服务器建立SSL连接 > 发送GET请求获取数据等等,整个流程固定而繁琐,那么能不能封装成一个模块,直接拿来使用呢?
4 b4 q/ p" m( s: ^; O% I$ g
19.png
esp8266
这里就要介绍RT-Thread的AT Device软件包了,
+ g* J! o6 E% t/ h7 ?1 ^. j5 J# H! Q, B( S1 o& a3 r

4 i. S% ]( O0 o, ^$ E6 P3 y/ UAT 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 命令章节 。
' ?* F- B$ I& k9 I0 ^/ |. H# ohttp://www.rt-thread.org/document/site/programming-manual/at/at/
4 Y0 _2 K! h: d$ Z2 Q) m4 c2 E  ]/ }1 p' C- T# }0 b4 C) f

2 i. d1 p, v$ U8 G) ?5 p( n简单的说,就是我只需要调用这个软件包,然后修改WiFi账号和密码,就可以直接配置ESP8266联网了。
/ S2 R2 P0 A+ `
# f: U' X: |( a8 z

6 s) c* c2 Z3 d! T$ p" b由于AT Device依赖于libc组件,所以在添加AT Device软件包之前,先开启libc。1 _; O- |. v0 q; z# l2 I0 m

0 {( p6 r0 T, X* V6 `0 M
$ L8 ~3 j0 [' T2 ^% u/ {
在RT-Thread Settings中点击libc灰色图标,变成彩色说明已经开启。( |- h% t' B. v; K1 B+ y
20.png
组件配置
添加AT Device软件包,点击立即添加
/ [" q% I: y4 n( ~! n
21.png
软件包
在弹出的软件包中心,搜索at_device,然后点击添加,添加到当前工程。2 ?1 y  G9 W: L  _
22.png
软件包
在at_device软件包上右键,选择详细配置:4 i7 s8 T% N, l5 i7 Y
23.png
软件包
在弹出的页面,选择我们使用的WiFi模块类型,乐鑫的ESP8266系列,并配置WiFi账号和密码,WiFi模块所连接的串口号。
" P; |9 J  ?4 Z3 M& k! E; v
24.png
WiFi配置
点击保存之后,工程会重新进行配置,添加相应的软件包文件到当前工程,重新生成Makefile文件,rtconfig文件等等。' C5 m& S) `# W# H

. \8 \# S' F* ]0 d0 i6 W$ S

7 m* p- ~( f# a: D. v虽然我们在at_device配置中选择了uart2作为at_device设备连接的串口。但此时串口2并没有开启,还需要我们手动使能。! g! q" x% C( Q. I5 E* E

! q% Z# `3 d; D/ U& h# F
) R; M" |' x4 Z  S, L* A
打开drivers->board.h文件,通过宏定义的方式使能串口2。
! Z% F1 ^- p+ j. X9 Z" E) s; ]
  1. #define BSP_USING_UART2
    2 D: V; J( z0 N( E! ?
  2. #define BSP_UART2_TX_PIN       "PA2"& r3 Z1 y) }9 _
  3. #define BSP_UART2_RX_PIN       "PA3"
复制代码
这样就开启了UART2的片上外设,Ctrl + B重新进行编译,时间会有些长,编译完成之后,可以看到flash文件大小明显比之前大了。) l8 E2 [0 e3 h% S
25.png
编译结果
Ctrl + Alt + D重新下载运行,打开串口终端:$ j3 U% P, ]4 c# Y& _" ]
26.png
终端
可以看到,UART2初始化成功,WiFi连接成功。说明我们的串口模块已经可以正常工作了。提示[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!失败信息,是因为当前ESP8266的固件版本不支持AT+CIPDNS_CUR?这条命令,把固件升级到最新版本就好了。这个不影响后面的操作,所以就不用在意这个了。
2 b9 Y, p2 U! B4 N
+ r1 B0 h* g  K6 {
, X7 |2 ?  P" ]/ x, l! O. |% ]
测试一下ifconfig和ping命令,都是正常的。0 _% h: B4 f5 `! T( k- M6 y
27.png
终端
在RT-Thread Studio中配置ESP8266模块联网,整个流程只写了3行代码,可以说是非常的快速方便。# }# q' z- O+ x8 a1 _0 ^
5 R7 B# S1 V6 Q5 C, a( |

( n" W( Y, i& u; g  |9 s5.疫情数据的获取% B" [4 q8 Z* u
WiFi模块连接上互联网之后,就可以连接GET疫情数据的API接口http://lab.isaaclin.cn/nCoV/api/overall,然后读取返回的疫情数据。在上一篇的裸机工程中,是通过先和服务器建立SSL连接,然后发送GET HTTPS请求,获取到的返回数据,那RT-Thread有没有这样功能的软件包呢?这里就需要添加另一个软件包webclient。
! o1 `( l, k  D7 J) F5 r: |, @# a$ @. W' r

$ t$ _% Z- ~% @; z0 T3 U/ o) xWebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。) {! Q9 m# P% |5 g5 l  O0 ]
WebClient 软件包功能特点如下:% |: u. j+ z) a9 i
. `, G) N/ a! |' c7 @; T

  g) n1 e% l& v+ Z: _·支持 IPV4/IPV6 地址;
  z# w; x5 j& s: X0 Y  H·支持 GET/POST 请求方法;
4 _9 k# |9 p5 g·支持文件的上传和下载功能;
- L, {7 b$ W6 P$ t: U·支持 HTTPS 加密传输;
) ^! ^( [1 M( z' \$ J·完善的头部数据添加和处理方式。
% G- f' \8 m6 g( B& c% H% \  t; @& v
  P% Q( Q$ P' V
和添加at_device一样,在软件包中心中搜索webclient,
# v2 y0 ], ]1 g; j( v1 I
28.png
软件包
然后添加到当前工程,右键进行配置,由于我们的http://lab.isaaclin.cn/nCoV/api/overall这个疫情数据接口是HTTPS类型的,根据软件包使用手册,我们需要选择TLS模式中的 MbedTLS。勾选添加GET和POST示例。; [1 J' ?* k+ Z: [# Z& S( w
29.png
软件包配置
保存配置,看一下当前已经添加了哪些功能,可以看到有一些组件我们并没有去打开,但是已经被开启了,这是因为有些软件包是会依赖一些组件的,当使能软件包时,一些依赖的组件也被同时使能。* T; m1 V6 E. d) S$ L3 ^, V
30.png
软件包
Ctrl + B编译,Ctrl + Alt + D下载运行。在终端输入web_get_test测试GET请求功能。
: n- [: ]1 V7 p
31.png
GET示例
可以看到,执行get命令之后,会返回一个字符串,那么GET的是哪个地址呢?打开packages->webclient-v2.1.2->samples->webclient_get_sample.c文件,$ y5 h3 w' t  f& u& W8 v& W. {: X+ X6 f. J
32.png
示例代码
可以看到GET的是这个地址:http://www.rt-thread.com/service/rt-thread.txt,我们用电脑上的浏览器访问一下:2 j: P& V: D) d8 j4 c; x
33.png
浏览器访问
经过实际测试发现,GET HTTPS请求,还需要使能软件模拟RTC这个组件,否则会报assertion failed at function:gettimeofday, line number:19错误。
' [/ D; P; U8 H2 q2 \& a( ^, t8 E
34.png
使能RTC
我们重新写一个获取疫情数据的函数,并导出到MSH。! P. D- o2 l- i" z' F) n% i
0 O2 r: ]( g+ d& w. Y
  s( r- t/ m* K; ]. A
usr_ncov.c文件内容- V* G# d/ g& s0 s5 b
  1. //usr_ncov.c! |! ~3 w/ i( f  B
  2. #include "usr_ncov.h"- a" Y* f( ]  L% M" P$ V

  3. / Q4 {" U/ @# T: \3 g+ y. ^: l
  4. int get_NCOV_Data(void)
    " g) U% `- v  k; f/ {4 s
  5. {
    . }3 C% s) \. D# A- A3 x
  6.     char *uri = RT_NULL;3 B1 W; Z! F0 W2 t' v$ Q( e# |% I
  7.     struct webclient_session* session = RT_NULL;
    . ]+ Q, O& U$ Y/ O: p$ |
  8.     uint8_t *buffer = RT_NULL;
    ! k5 _5 o1 a! j
  9.     int index, ret = 0;
    * i5 ]3 H, b% B8 i% t* r
  10.     int bytes_read, resp_status;% L# ~3 n. p4 O; {8 [: [( ?# e  e
  11.     int content_length = -1;
    ' n( V  T5 X& L* Z% x4 U) i5 C) M
  12.     int buffer_size = 1600;8 c) Y  P% X$ `! u
  13.     uri = web_strdup(API_NCOV);
    # U0 J9 @4 v% p0 V  ~0 h* @
  14.     rt_kprintf("start get api: %s\r\n", API_NCOV);  O: @4 V8 F$ Z, e! ]0 D" D4 z
  15.     if(uri != RT_NULL)' I7 Q( b& v" H6 Q: a
  16.     {
    + K& g( O2 A; J/ D
  17.         buffer = (unsigned char *) web_malloc(buffer_size);
    ! f9 K, n0 X+ E4 j: p
  18.         if (buffer == RT_NULL)
    9 t% }: V  h/ |2 _9 J
  19.         {
    1 `9 n+ ?# ^* D; Q- T7 L
  20.             rt_kprintf("no memory for receive buffer.\n");
    . P( ]! E. |( `. x$ g: D. \1 X
  21.             ret = -RT_ENOMEM;$ K: p8 e1 p! L6 M
  22.             goto __exit;  G5 d3 q% _' u, B' D  q
  23.         }" j' ]9 W( G5 f! P9 Q. N/ T
  24. $ U8 b1 V$ z7 ~- _# G
  25.         /* create webclient session and set header response size */( o& O( V7 _& r" I, r
  26.         session = webclient_session_create(buffer_size);# O6 o) i4 G/ a& L; h0 x1 H  G1 l
  27.         if (session == RT_NULL)* t- n* t! e+ }- S4 U+ [: S
  28.         {
      |0 T- ^0 r  ~+ n2 N0 `0 I4 B
  29.             ret = -RT_ENOMEM;8 r( G. |: F, d# U
  30.             goto __exit;
    ( H# |# e4 y  g! E/ L: I  _- m
  31.         }0 `( \6 g( }' J2 b9 c

  32. 8 ?, H  R9 g: A. b6 V( N" x
  33.         /* send GET request by default header *// Z# V' i3 l8 ^% K+ P! o1 u9 ]$ ]0 h
  34.         if ((resp_status = webclient_get(session, uri)) != 200)# H" ?! i! k2 e4 t7 U) ]
  35.         {0 G# h6 r4 G: T! Z# g& K& P
  36.             rt_kprintf("webclient GET request failed, response(%d) error.\n", resp_status);
    ; C# L. ]7 ^' F" ]  Q. Q& x5 O& s
  37.             ret = -RT_ERROR;
    9 [/ }% M: d0 R, C9 b
  38.             goto __exit;1 ^# z6 S/ Q2 p1 o) Y1 w
  39.         }6 a+ t+ G. Z$ ]& @1 b6 ^" I7 O
  40. 8 Z1 n. p$ a9 Q3 u1 _9 v5 b: h. e# D
  41.         rt_kprintf("webclient get response data: \n");
      X4 v: ~8 g7 L- s* @, j

  42. % _4 U7 u( X+ D
  43.         content_length = webclient_content_length_get(session);7 y) z7 L  B2 A) b
  44.         if (content_length < 0), U& I/ C+ Q1 G- A0 Y6 [0 X
  45.         {
    2 {4 S) V1 k/ }& w4 ^. c& t" J7 M
  46.             rt_kprintf("webclient GET request type is chunked.\n");6 X: d  R# q6 P2 C

  47. 7 n3 u% `% `( ]6 Y/ l" _
  48.             do
    / W) X9 a5 j( P& w5 V* g7 O
  49.             {& P& L% Q$ C! z
  50.                 bytes_read = webclient_read(session, buffer, buffer_size);
    # B. ^* @2 C) i3 i7 T; E8 k; [
  51.                 if (bytes_read <= 0)0 Z8 J2 Z5 D4 I& }3 w7 r
  52.                     break;
    0 o5 Y" X5 I: y0 f8 |9 O7 K

  53.   O$ F( e' \) ]! `( q* m. m0 q6 }
  54.                 for (index = 0; index < bytes_read; index++)
    . R6 y0 j6 Y8 k" C, O2 Y1 ]
  55.                 {0 I4 w) g$ U( h9 x, ^+ ~0 e
  56.                     rt_kprintf("%c", buffer[index]);1 P- t0 w) P2 ]2 c
  57.                 }
    ! w' b/ n! x: D; _0 c6 @
  58.             } while (1);. |! }$ z8 J. h) C& U, f; N
  59. 4 C9 \% i1 h5 Z1 ]2 m
  60.             rt_kprintf("\n");
    ! `( R' k. v# i& ^
  61.         }) g7 |) R& e; S' e0 h5 W
  62.         else
    ! P9 K. X! U8 K
  63.         {
    ( c- g' J7 D7 H) v! S
  64.             /* 读取服务器响应的数据 */8 w" z* X9 D0 h* o) J( {" _
  65.             bytes_read = webclient_read(session, buffer, content_length);
    ! @- _: G3 h# W9 V& d
  66.             rt_kprintf("data length:%d\n", bytes_read);  Z# q9 F3 p0 s4 c9 ]& }. x
  67. ( F! V: T# @6 k* t9 P/ c
  68.             buffer[bytes_read] = '\0';
    % o& U. A; Z' A/ Q9 \) m
  69.             rt_kprintf("\n\n %s \n\n", buffer);4 D+ \% A" r: Y; b8 s  ]  n: I: j' N
  70. //            rt_kprintf("parse data\r\n");' N. \( c% |- |3 a+ L* T4 u: d. k
  71.             // parseData(buffer);        //解析函数
    ( W1 z7 P: o9 T: n4 x
  72.             rt_kprintf("\n");
    : o" U1 ~$ n2 Z4 f4 y7 ~' R
  73.         }
    ! {  S+ c1 m: Z1 `$ f! L/ r7 b
  74. - W7 t3 Z/ r  K4 I5 O1 u8 v* M
  75.         __exit:
    3 V0 f/ K2 D( @$ ^
  76.         if (session)
    . p, {! ?) Z/ h, b. I# D
  77.             webclient_close(session);
    , m8 p, C4 [6 [% w9 A

  78. , S0 I$ d5 p+ N' x' v! ~7 B- L
  79.         if (buffer)
    " Q" ~& {1 e5 \' V
  80.             web_free(buffer);
    " ?! N+ v3 T0 l* M0 t
  81.     }
    7 g7 c* p' Z8 V' S- j
  82.     else
    0 W, `* r& ?- e1 q/ d; m* r" Y( s
  83.         rt_kprintf("api error: %s\n", API_NCOV);. {6 ?0 d  e7 k8 I$ A+ b
  84. ) i! W  ?$ R# |" ?! C5 c& g
  85.     return ret;
      b" U0 I9 N0 X8 B
  86. }, s& I2 r9 k$ n( Y0 d
  87. MSH_CMD_EXPORT(get_NCOV_Data, get api ncov);
复制代码
usr_ncov.h文件内容( e, W, u, J8 {8 L% C
  1. #ifndef APPLICATIONS_USR_NCOV_H_
    5 ]( k; j1 O8 |' e. I' T
  2. #define APPLICATIONS_USR_NCOV_H_
    0 I) u& C2 V7 M) e
  3. #include <webclient.h>4 a* ~# w( t1 o" l+ _8 s
  4. #include <rtdevice.h>
    - Q% _& e4 P- w9 {  x$ f
  5. #include <rtthread.h>
    ' H9 F$ _; M( c( f
  6. #define API_NCOV     "http://lab.isaaclin.cn/nCoV/api/overall"
    , T) ]7 u: g4 i4 r7 A( z2 R" D
  7. int get_NCOV_Data(void);/ _" d; t  U; C- `* l' l
  8. #endif /* APPLICATIONS_USR_NCOV_H_
复制代码
重新编译,下载,运行。在终端运行这个命令:
' ^% S/ m7 K3 Z! J. A8 J
35.png
命令获取疫情数据
可以看到获取到了返回的数据,长度1366个字节。下一步就是对这个JSON数据进行解析,获取到我们想要的疫情数据。
  T  h9 S/ W' A5 G
% ?! J, S; \5 ?+ y! n

- ^( z. p4 w! p. o+ Y6.疫情数据的解析
$ x0 Y" I3 e; |, G5 ^API返回的数据是JSON格式的,关于JSON的介绍和解析,可以查看使用cJSON库解析和构建JSON字符串。数据的解析使用的开源小巧的cJSON解析库,我们可以在软件包管理中心直接添加:
( T8 L1 ^& k# l5 E0 Y3 t  ?
36.png
添加cJSON
在进行解析之前,先来分析一下JSON原始数据的格式:results键的值是一个数组,数组只有一个JSON对象,获取这个对象对应键的值可以获取到国内现存和新增确诊人数、累计和新增死亡人数,累计和新增治愈人数等数据。
5 Q2 g9 |  \6 J, f
: `0 E0 H, n0 c" S/ A  E
- a1 }$ x9 d: `- m% _% a/ y
全球疫情数据保存在globalStatistics键里,它的值是一个JSON对象,对象仅包含简单的键值对,这些键的值,就是全球疫情数据,其中updateTime键的值是更新时间,这是毫秒级UNIX时间戳,可以转换为标准北京时间。# [+ I* R5 |) y$ X/ r7 y7 t" W
  1. {, a0 |# V: U; S% \7 ?* B* P
  2.     "results": [{
    4 F( e, J; f8 o5 F
  3.         "currentConfirmedCount": 509,
    3 T7 O  D4 S1 x2 ?9 g
  4.         "currentConfirmedIncr": 16,
    1 N0 e* @' ^, q0 z; d. b
  5.         "confirmedCount": 85172,* \, w0 k1 V& C" m9 S" n
  6.         "confirmedIncr": 24,. I7 D; E/ q/ v/ y
  7.         "suspectedCount": 1899,' s+ V3 L$ X$ H
  8.         "suspectedIncr": 4,
    , D  u2 B: l" b
  9.         "curedCount": 80015,2 [3 }; C2 @/ f' {; _
  10.         "curedIncr": 8,' {! E1 J% y5 @' @) B1 V. m! p
  11.         "deadCount": 4648,
    , l* _' ]; h& c3 t- }8 ^
  12.         "deadIncr": 0,
    % V  _6 j$ R7 w% O! S+ s( N
  13.         "seriousCount": 106,
    " M( Q4 d# X. @. _
  14.         "seriousIncr": 9,
    3 I4 K; Z1 L( o/ F
  15.         "globalStatistics": {
    ) @( Z/ E7 l5 T" Q
  16.             "currentConfirmedCount": 4589839,; _) ^/ }' D, J. Y; u: }
  17.             "confirmedCount": 9746927,
    ) O) n5 i, B0 U+ F; V' q9 O/ G
  18.             "curedCount": 4663778," ~9 N1 P+ k5 d' R& E! c! R
  19.             "deadCount": 493310,
    1 L' {5 }2 X, U) c# m6 O# ^# p
  20.             "currentConfirmedIncr": 281,
    ! T3 b7 y7 F+ W. f
  21.             "confirmedIncr": 711,& q% J/ Z7 q/ D4 v" e
  22.             "curedIncr": 424,8 J4 [) m% \' Z( `' j
  23.             "deadIncr": 6
    + b( n4 Q, a& H% w5 i# T% G' x
  24.         },+ N7 a- e6 S( B' Q  J4 p: m3 Y! W
  25.         "updateTime": 1593227489355; _  c% V0 s, {6 ?) J- K
  26.     }],
    ) ^! E" S  T- q& Q( Y$ V3 \4 D
  27.     "success": true4 r' n7 n) t5 c9 h' a
  28. }
复制代码
先定义了结构体NCOV_DATA,用于存储国内和全球疫情数据:
) p( B6 t' B$ m$ J8 {
  1. struct NCOV_DATA{( f$ p) d% K  s; x1 y% p# ~
  2.     int currentConfirmedCount;
    . l/ y1 [' |, z: i
  3.     int currentConfirmedIncr;
    ; G1 V+ O1 ]9 ?7 w9 g- c, P8 m
  4.     int confirmedCount;/ u) M' ?7 l0 R' E& m$ @
  5.     int confirmedIncr;4 K5 j8 s( ^. l; l0 I& Z
  6.     int curedCount;
    3 H. ]: H  r( J9 ~! F
  7.     int curedIncr;
    & j1 @2 U! ]4 o) V
  8.     int deadCount;  J% X3 z# {0 W3 w1 q4 _
  9.     int deadIncr;
    " \8 N6 A( L' ?3 K/ P4 Z
  10.     int seriousCount;3 Y8 O4 y+ q6 F# l' _2 f  V
  11.     int seriousIncr;
    8 p8 X$ M/ d9 ]* f7 O) x

  12. 0 e3 t+ W# a+ R& G+ m# Z$ o
  13.     char updateTime[20];
    & C- |5 c5 m9 {/ T4 I7 L" n
  14. };
复制代码
对应的解析函数:
5 E! w# s7 y2 u) ]
  1. #include <cJSON.h>9 q) N: O' f( q& N
  2. ; t# x" K* p( _  K( w1 N0 Z
  3. struct NCOV_DATA dataChina = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "06-13 16:22"};;
    # ^. K+ p3 |7 F* m) `: R
  4. struct NCOV_DATA dataGlobal = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
    0 H9 S# ^* L& u4 w9 L- W

  5. 4 k" m, v) Z0 g; I# R+ E3 Z
  6. int parseData(uint8_t *str)4 N* ^/ e/ f$ D( B  L$ \4 V
  7. {! a$ V6 J+ }% r0 i( X3 }
  8.     int ret = 0;! X& E: q7 D- m+ L
  9.     cJSON *root, *result_arr;
      m3 B, [: _) T0 Z
  10.     cJSON *result, *global;% h3 N1 M. R; o. ?
  11.     time_t updateTime;
    $ p/ f2 |( B4 y2 l6 k$ R4 x
  12.     struct tm *time;
    3 b/ U) Z* j; x" u* x% [, D. p
  13. 6 a: d1 p* C/ f1 W
  14.     root = cJSON_Parse((const char *)str);   //创建JSON解析对象,返回JSON格式是否正确
    8 |% c! U+ `( [" Z* \" Z

  15. - Z! l# f$ Q# p% @2 z! l) k0 T4 o
  16.     if (root != 0)0 t$ x6 V8 T  P) Z$ I; `
  17.     {2 v+ z0 {$ s$ {
  18.         rt_kprintf("JSON format ok, start parse!!!\n");+ |3 @- I. z  o9 L8 l& P
  19.         result_arr = cJSON_GetObjectItem(root, "results");
      e+ A) b9 {! R, R
  20.         if(result_arr->type == cJSON_Array)
    ( T- H. x. K$ p. q3 X2 a
  21.         {
    " T2 ]; g( }& t
  22. //            rt_kprintf("result is array\n");
    4 b& I; T. z8 P. J8 P& O, t) a
  23.             result = cJSON_GetArrayItem(result_arr, 0);
    # e& S% L, x. `" J  |
  24.             if(result->type == cJSON_Object)6 `5 b) m. {; G( k
  25.             {4 f# v  V9 q+ @2 ?) m% N8 C" [
  26. //                rt_kprintf("result_arr[0] is object\n");
    , T: Y! x* e/ ?$ e& J

  27. * X3 {; Y" ]6 m/ s1 |: ~
  28.                 /* china data parse */
    % {; i- w6 R, L. Z
  29.                 dataChina.currentConfirmedCount = cJSON_GetObjectItem(result, "currentConfirmedCount")->valueint;( B) c, k, M' ^5 v* w+ E
  30.                 dataChina.currentConfirmedIncr = cJSON_GetObjectItem(result, "currentConfirmedIncr")->valueint;! I! _* T8 |6 A+ S- ?
  31.                 dataChina.confirmedCount = cJSON_GetObjectItem(result, "confirmedCount")->valueint;
    9 C7 T, G6 n& Q$ _3 d! c# ?6 b
  32.                 dataChina.confirmedIncr = cJSON_GetObjectItem(result, "confirmedIncr")->valueint;9 _+ \- Y, }0 q: k5 }# r5 P7 _
  33.                 dataChina.curedCount = cJSON_GetObjectItem(result, "curedCount")->valueint;# e( i7 ?( c1 r) ]
  34.                 dataChina.curedIncr = cJSON_GetObjectItem(result, "curedIncr")->valueint;) V; |; ~& ]- c) E$ N2 ~( f
  35.                 dataChina.deadCount = cJSON_GetObjectItem(result, "deadCount")->valueint;
    % {! u# U7 `& J) M2 D4 W9 Y) N) V
  36.                 dataChina.deadIncr = cJSON_GetObjectItem(result, "deadIncr")->valueint;7 b: M3 l- A7 X+ h0 f% C
  37. 2 \9 C) x5 O3 t
  38.                 rt_kprintf("**********china ncov data**********\n");: g) v+ @8 q. c, \, T5 h
  39.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataChina.currentConfirmedCount, "currentConfirmedIncr", dataChina.currentConfirmedIncr);
    : x' K/ `5 l/ p" s$ S# z* R
  40.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataChina.confirmedCount, "confirmedIncr", dataChina.confirmedIncr);
    7 n8 U; S- G! C. t0 i
  41.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataChina.curedCount, "curedIncr", dataChina.curedIncr);) ~: G7 l" ], x! K3 N5 V7 i. ^5 K
  42.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataChina.deadCount, "deadIncr", dataChina.deadIncr);
    : m! S% K2 o  L% S

  43. 7 `) X! \5 ]4 ~; A
  44.                 /* global data parse */. e; p+ _* Y- x- A6 Y+ \
  45.                 global = cJSON_GetObjectItem(result, "globalStatistics");
    1 l8 v* A* M; {5 i1 H6 \2 v9 T" O
  46.                 if(global->type == cJSON_Object)
    6 A( x( G3 B& Y3 C0 h5 f8 m# y$ G9 m. E
  47.                 {
    : i! J$ ?9 F, x( t
  48.                     dataGlobal.currentConfirmedCount = cJSON_GetObjectItem(global, "currentConfirmedCount")->valueint;  v2 G% \+ i; U9 ~* C, o: M) q% P0 ~
  49.                     dataGlobal.currentConfirmedIncr = cJSON_GetObjectItem(global, "currentConfirmedIncr")->valueint;! U$ ]1 G" \& R. U
  50.                     dataGlobal.confirmedCount = cJSON_GetObjectItem(global, "confirmedCount")->valueint;. j8 x- Q2 i% _7 H; ?0 Q
  51.                     dataGlobal.confirmedIncr = cJSON_GetObjectItem(global, "confirmedIncr")->valueint;/ H8 s, K+ N2 c8 u
  52.                     dataGlobal.curedCount = cJSON_GetObjectItem(global, "curedCount")->valueint;+ ^7 h: I3 h' X% y) @
  53.                     dataGlobal.curedIncr = cJSON_GetObjectItem(global, "curedIncr")->valueint;
    / M% S6 y7 \/ n* A4 d7 J; Q
  54.                     dataGlobal.deadCount = cJSON_GetObjectItem(global, "deadCount")->valueint;
    . Y1 f6 I1 H. E2 @$ a
  55.                     dataGlobal.deadIncr = cJSON_GetObjectItem(global, "deadIncr")->valueint;; F4 j5 J  O) |9 i; m

  56. 0 c* w# H# ~1 w; n/ D3 y; e
  57.                     rt_kprintf("\n**********global ncov data**********\n");
    : o2 j9 \) _7 K# @  A
  58.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataGlobal.currentConfirmedCount, "currentConfirmedIncr", dataGlobal.currentConfirmedIncr);$ `5 }& I4 e# Q) l
  59.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataGlobal.confirmedCount, "confirmedIncr", dataGlobal.confirmedIncr);8 Q3 d  c% ^5 \! S, D( q, S
  60.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataGlobal.curedCount, "curedIncr", dataGlobal.curedIncr);
    . E& o- e. C. B
  61.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataGlobal.deadCount, "deadIncr", dataGlobal.deadIncr);
    9 _8 o$ u0 P5 l$ b: V

  62. , @" o9 R, r" R" J
  63.                 } else return 1;% K0 C; e" E* r4 j

  64. ) y) v# X8 v8 P; n
  65.                 /* 毫秒级时间戳转字符串 */
    5 I# Y3 m. L- v( y. ^- u
  66.                 updateTime = (time_t )(cJSON_GetObjectItem(result, "updateTime")->valuedouble / 1000);
    + V1 q7 c' e1 O" c  _
  67.                 updateTime += 8 * 60 * 60; /* UTC8校正 */6 B+ S( g+ X& b1 [" q8 ]' P; _
  68.                 time = localtime(&updateTime);% d& i; T. @" @& y1 A
  69.                 /* 格式化时间 */% }: C* _7 T8 p/ \. y1 A4 ~
  70.                 strftime(dataChina.updateTime, 20, "%m-%d %H:%M", time);! U; [9 a7 G+ y
  71.                 rt_kprintf("update: %s\r\n", dataChina.updateTime);/* 06-24 11:21 */* D3 B! R" @5 v
  72.                 //数据在LCD显示
    4 N' m+ z. ?, ?0 K( [5 ~4 m* K: n& Q
  73.                 //gui_show_ncov_data(dataChina, dataGlobal);, }! N; Z- @3 L- S; I: P* v
  74.             } else return 1;
    : z/ t9 p6 r- E: q
  75.         } else return 1;
    # i; Y% U9 n: G
  76.         rt_kprintf("\nparse complete \n");8 p6 j" M, l% w, S
  77.     }
    . y  L! {2 A4 @5 t; e  X
  78.     else
    + E1 n( E) v$ t( _% I
  79.     {
    ( r" d0 I1 Z: d1 F
  80.         rt_kprintf("JSON format error:%s\n", cJSON_GetErrorPtr()); //输出json格式错误信息
    , w/ {. I8 W. a! w5 d. \& {8 s
  81.         return 1;
    ' R& e& I3 [. v" |& k
  82.     }4 m( T6 A: X9 w6 g8 w4 T
  83.     cJSON_Delete(root);- P* ^% r% x. {% }% v

  84. ' q% U- V7 p! `( I3 I) _
  85.     return ret;$ N+ ~, O; d& _$ j3 H! S3 H+ \
  86. }
复制代码
在数据接收完成之后,对JSON数据进行解析。
3 `* ~- m3 `# i0 i) q! H8 L9 J
37.png
解析结果
7.疫情数据的显示* U) t1 i- j$ t6 @
数据解析出来之后,剩下的就简单了,把上一篇文章中9341的驱动文件移植过来就好了。8 P- `3 |' f$ o5 }: E6 _

$ s* q( }3 X; k2 i; x$ F
  l- V& T8 {1 E
液晶屏使用的是3.2寸 LCD,IL9341驱动芯片,320*240分辨率,16位并口。由于屏幕分辨率比较低,可显示的内容有限,所以只是显示了最基本的几个疫情数据。为了减小程序大小,GUI只实现了基本的画点,画线函数,字符的显示,采用的是部分字符取模,只对程序中用到的汉字和字符进行取模。为了增强可移植性,程序中并没有使用外置SPI Flash存储整个字库。
' k7 L# a( p( G0 g) O( a. y5 ^
# l1 c0 h3 n$ s% \5 ~

, ~9 y& Y/ V7 j- n由于RT-Thread Studio使用的HAL库,所以LCD的GPIO初始化函数需要修改一下:. j2 t( q$ S1 b! {" h  i4 s
  1. void lcd_gpio_init(void)
    - _2 {  }, ]( @! G3 n
  2. {5 W* M; ~; T5 C
  3.     GPIO_InitTypeDef GPIO_InitStructure;
    3 A* |# }; [8 F4 f, f  `/ @: y

  4. . z* F$ i  S9 V: D2 B; }! j
  5.     __HAL_RCC_GPIOA_CLK_ENABLE();- k& ?1 a& W& p. x1 Y) R
  6.     __HAL_RCC_GPIOC_CLK_ENABLE();
    5 t5 U8 v' F7 d  s" n
  7.     __HAL_RCC_GPIOB_CLK_ENABLE();1 j& j9 w/ l7 |, o( X/ S6 n! v+ s
  8.     __HAL_RCC_AFIO_CLK_ENABLE();
    * Z/ i; a2 o6 E  W) G
  9.     __HAL_AFIO_REMAP_SWJ_NOJTAG();. [# q' K1 c, @/ E
  10. " S& I; B& I4 V7 Q# k2 g* H& q
  11.     GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    + a, H+ _6 G! V% L
  12.     GPIO_InitStructure.Pull = GPIO_PULLUP;
    $ ]: g& o, o. ~$ C
  13.     GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
    ( c+ }5 C& G4 ^& I; R. f
  14.     GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6;! P% Z" A& c5 m7 G& g
  15.     HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOC3 `; V/ `4 y* L( T: g* R) q8 t2 D
  16. " K& Y" P+ B: h5 O0 j/ I
  17.     GPIO_InitStructure.Pin = GPIO_PIN_8;    //背光引脚PA83 B* p# U5 _. U( ^9 Y! t, @& r. L
  18.     HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); //GPIOC
    0 \6 [  C9 ]* f* s* m/ E* C
  19. & f5 c5 J7 e( ~
  20.     GPIO_InitStructure.Pin = GPIO_PIN_All;1 y. V* S- C2 M$ |- a+ y
  21.     HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
    0 }1 j8 z0 I6 y( [0 S4 C

  22. : a: Y, k# `* u: Q
  23.     HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6, GPIO_PIN_SET);
    - I4 L% e* ~- }  C0 t7 _( o9 H
  24.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);- r* ^" D& J1 X. D
  25.     HAL_GPIO_WritePin(GPIOB, GPIO_PIN_All, GPIO_PIN_SET);6 m; u2 j- @7 W: [$ p/ [% @
  26. }
复制代码
延时函数换成:( X+ f# w% e% P4 ~% R7 z
  1. rt_thread_mdelay(nms);
复制代码
还有一点,在Keil中,文字编码选择GBK编码,1个汉字占用2个字节,而RT-Thread Studio为UTF-8编码,1个汉字占用3个字节,汉字显示函数需要调整:
1 ~' C- d+ B/ n6 d
  1. void gui_show_chn(uint16_t x0, uint16_t y0, char *chn)8 d0 d: N7 [7 R9 j
  2. {
    3 D. y5 A$ c, S# I/ M
  3.     uint8_t idx = 0;
    / P0 @1 f- {, d% B
  4.     uint8_t* code[3]; //UTF-8:国=E59BBD" }) C' ^+ L/ _( _; x( Y

  5. # X" x) A! M' ?/ Z5 R8 m' h
  6.     uint8_t size = sizeof(FONT_16X16_TABLE) / sizeof(FONT_16X16_TABLE[0]);# X4 ]  P; X. B9 I8 S
  7.     /* 遍历汉字,获取索引 */& c0 Q0 ]( {* O5 ?
  8.     for(idx = 0; idx < size; idx++)/ V% P/ g& ]) K3 U* p
  9.     {
    ) w8 ^! g; G: A8 [
  10.         code[0] = FONT_16X16_TABLE[idx].chn;
    . p! _! K1 f- u$ ?" e9 a0 @' h
  11.         code[1] = FONT_16X16_TABLE[idx].chn + 1;5 l1 r& n' @& o% f( D
  12.         code[2] = FONT_16X16_TABLE[idx].chn + 2;% Z4 g& w0 O1 u# |  ?& }
  13.         //汉字内码一致
    2 K' d" L! ^8 h
  14.         if(!(strcmp(code[0], chn) || strcmp(code[1], chn+1) || strcmp(code[2], chn+2)))$ |. S4 J/ Y4 W3 A, C
  15.         {
    4 \2 S. r* f9 h$ i7 f. c) |  u
  16.             gui_show_F16X16_Char(x0, y0, idx, WHITE);
    % J* T- }' d( u2 p
  17.             return;
    ! C8 w3 M: e! U: N6 b  |
  18. //            break;
    " E9 E* Q/ R- |  o9 q
  19.         }+ t; u, r7 o( P; Y2 z
  20.     }
    ; d( v! j4 M" d
  21. }
复制代码
疫情数据显示函数:
1 X: a2 S6 T$ J9 G; M& Q( y
  1. void gui_show_ncov_data(struct NCOV_DATA china, struct NCOV_DATA global)
    - T) v! [: ^( ^) z+ B6 c9 _
  2. {
    4 A% f; T5 E/ L' x. ?" Y' v( _
  3.     uint8_t y0 = 20;4 g; b& h# X7 v

  4. # J) e% R$ G# ]# I2 b
  5.     lcd_clear(BLACK);
    2 C: Z$ P8 R3 \* T% `
  6.     gui_show_bar();8 \" d( p& k, o3 s0 E# C
  7. 1 X6 J( }7 T3 x( p( u- D
  8.     gui_drawLine(0, 18, 320, DIR_X, WHITE);: k. U7 l* ]& t. ?1 p3 m
  9.     gui_drawLine(0, 38, 320, DIR_X, WHITE);: q' C+ e2 Y5 U6 I( a& s3 b! k
  10.     gui_drawLine(0, 138, 320, DIR_X, WHITE);! u  O+ k5 f( k4 [' S# z9 ]* C
  11.     gui_drawLine(0, 158, 320, DIR_X, WHITE);
    ) k4 X2 B, h: ~; f
  12.     gui_drawLine(0, 220, 320, DIR_X, WHITE);( @8 D8 \" E1 v6 l9 I9 i

  13. 3 u' k3 o+ I0 Y) A# ^1 t
  14.     /* "国内疫情" */4 m7 U  K7 X$ U% m
  15.     gui_show_chn_string(128, y0, "国内疫情");: P. e8 s' x6 G( E% I) R* g5 N
  16.     gui_show_line_data(40, "现存确诊:", china.currentConfirmedCount, "较昨日:", china.currentConfirmedIncr);
    , g( ]+ E/ a  n& m& x
  17.     gui_show_line_data(60, "累计确诊:", china.confirmedCount, "较昨日:", china.confirmedIncr);2 f2 d; x) V5 S2 X3 l0 |
  18.     gui_show_line_data(80, "累计治愈:", china.curedCount, "较昨日:", china.curedIncr);
    7 {: s9 e8 K9 b. W# M
  19.     gui_show_line_data(100, "现存重症:", china.seriousCount, "较昨日:", china.seriousIncr);- r. M: t! q% e  K5 ~' C) G2 i* |' E
  20.     gui_show_line_data(120, "累计死亡:", china.deadCount, "较昨日:", china.deadIncr);+ A6 m) C" K8 Z6 t0 s8 q3 L
  21. / ]. V$ @& i$ j
  22.     /* 全球疫情 */
    9 R- A/ |: i0 Y& t! u
  23.     gui_show_chn_string(128, 140, "全球疫情");" C. u( Y' \3 i9 ]6 h1 L
  24.     gui_show_line_data(160, "现存确诊:", global.currentConfirmedCount, "较昨日:", global.currentConfirmedIncr);
    2 O+ B9 G' l0 `+ q5 h% L2 A2 M& W
  25.     gui_show_line_data(180, "累计治愈:", global.curedCount, "较昨日:", global.curedIncr);
    % m; E, H; I! ?- j0 l4 [
  26.     gui_show_line_data(200, "累计死亡:", global.deadCount, "较昨日:", global.deadIncr);
    . ^" c# [2 l) [
  27. ; |6 K9 O" q# q: z3 T1 A! @6 C7 p
  28.     gui_show_chn_string(160, 222, "更新于:");
      n, l/ }. Z* K6 B9 v$ R' c
  29.     gui_show_F8X16_String(230, 222, china.updateTime, GREEN);
复制代码
最终显示效果
' e1 X! l" |% `5 r2 B
38.png
最终效果
开源地址6 q4 p" j+ T/ o' q% M8 `0 E' F# P
代码已经开源,地址在文末,欢迎大家参与,丰富这个小项目的功能!; ?, U7 ~; Y+ b6 e

. {% G; X) \  Z$ }0 j$ D% p  m: S5 N
1 j+ A6 X, M# B3 [" k
基于STM32+RT-Thread的疫情监控平台
+ A& F/ W3 a' X0 Xhttp://github.com/whik/rtt_2019_ncov: ~+ E- @6 J5 h) u, d) e

" z, n7 n! ^  D5 L7 Z

6 F2 k7 @& h, \( {" z基于STM32F103的疫情监控平台(裸机版)4 P- O5 J9 u6 m! n  U
http://github.com/whik/stm32_2019_ncov
8 y- M8 S/ f9 E5 J  Z
$ N, ]; x# ]: l+ [' n! j2 }4 @( K* V# A$ s5 B1 Q
; T% ^/ d3 N: `
文章出处:[color=var(--weui-LINK)][url=]RTThread物联网操作系统[/url]
1 i% x7 G, S+ y7 F: S( Y4 l2 W, I" j+ [; ^  h4 j3 D
收藏 1 评论7 发布时间:2020-8-25 17:05

举报

7个回答
goyhuan 回答时间:2020-8-25 18:12:29
谢谢分享
davidwyq 回答时间:2020-8-25 20:10:34
谢谢分享
子曰好人 回答时间:2020-8-26 15:43:56
厉害啦
小小超 回答时间:2020-8-27 10:47:00
谢谢分享!!
久远寺有珠 回答时间:2020-8-28 08:51:30
提示: 作者被禁止或删除 内容自动屏蔽
sumoon 回答时间:2020-8-28 10:15:36
提示: 作者被禁止或删除 内容自动屏蔽
乎乎 回答时间:2020-9-1 17:10:54
谢谢分享,RT-Thread也是不错的平台。

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版