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

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

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

9 S( h6 r3 ^9 J% f
# q9 D7 z6 I, }; R' W
上周末加班,这周末休息,有时间整理一篇之前做的基于RT-Thread的疫情监控平台。上一篇文章我们使用STM32F103 MCU裸机开发的方式实现了疫情监控平台。这次我们玩点高端的,使用RT-Thread Studio来实现同样的功能,一起来看看吧!6 ^* L( Y4 ?4 N+ O  N

: p5 R8 l% G4 r% ^' V2 z4 H. x
8 ^1 S: k* y; A- [
·文章目录
) o' l/ Y8 p( K; [·使用到的软件包
3 U* \+ Z% j, G# \·0.RT-Thread Studio的下载和安装9 K2 ], i- W, v: D) e

; Q2 F7 o/ j1 [; G& {
/ G, d+ n: ]2 W4 ?5 e1 D* @
1.硬件准备
8 F- T/ j4 A+ H7 S$ s, f2.新建工程% u% y3 C2 f6 G* k: T1 b
3.添加LED闪烁功能. d' M6 Q7 {! N, z0 i
4.添加ESP8266软件包: S- ^+ q" L% V8 d3 G
5.疫情数据的获取
- p+ W# l- W$ D$ o* \0 D* z6 a5 m6.疫情数据的解析4 f4 X3 T4 Z' b3 C% j! s" M
7.疫情数据的显示* G' O2 U, r. Y
开源地址
- D4 J  J# [0 a! d
# ^. ^& u# A" B! c5 `
% n7 J' |6 t: c  u
最终的显示效果:' M% ~% V$ ~: B$ L1 z3 B: N! w! H
1.png
显示效果
有效文件就这9个,其他的就全是图形化配置:
/ f9 I, ~, P: o# N' R/ q7 d
2.png
有效文件
整个流程下来,如果顺利的话,可以在2个小时内完成。
5 U% t% t$ ]7 N5 e, g* |% @" w2 y2 N7 W
使用到的软件包  d, S6 F! R3 p! E: R5 J
·at device:用于ESP8266配网4 v& F! c+ l5 D* ?# k9 y! k
·webclient:用于发送HTTPS请求0 q$ G/ \* c; c8 V
·mbdetls:用于HTTPS加密
) a  |  ?) j9 d9 n( t+ I·cJSON:用于JSON数据解析
" _; g  W8 [! R+ ^. a( X9 n. `
* e) I9 y9 J' U: a; \. ?2 q, n0.RT-Thread Studio的下载和安装8 w: _, x( e* ^4 D. B1 L3 i
一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效
3.png
RT-Thread Studio
·支持多种芯片,STM32全系列
$ O  X7 C+ E: z6 }' Q) p0 c·支持创建裸机工程、RT-Thread Nano和Master工程
- v6 W7 u8 [- s: O# h0 I·强大的代码编辑功能,基于Eclipse框架
3 K2 B% C$ d8 m* S·免费无版权限制,基于开源Eclipse和ARM-GCC编译器。# S) g! k" r1 R" @$ l& K
·支持多种仿真器,J-Link,ST-link等,支持在线调试,变量观察。  t9 p- |  r) b4 }
·SDK管理器,图形化配置RT-Thread软件包,同步RT-Thread最新版本。
2 @9 Q& V) T) |. ?9 S- j·集成Putty串口终端工具
( L1 l3 ?1 e7 z  t
/ l' ]: e& y: K

3 U9 f" O/ D+ X更多的使用教程:* E, h- u( L+ Z- v1 a* ]3 j& O
http://www.rt-thread.org/page/studio.html
: Q/ x0 j$ P* e9 g
1 Z. p1 U2 S7 T5 R% d! ^! M- S' P

& E4 Z; t3 [/ p  z  s. }; r2 o目前最新版本为1.1.3版本,支持3种下载方式,我们选择最后一个下载方式,从RT-Thread 官网服务器上下载。
' Z/ Q: a! U! X& Y1 `2 u' s
: v" T9 V9 n6 v5 r9 f0 r
8 E! h; x( P) ^/ H, {+ [* F9 I, M
下载地址:
' c, r/ f1 R; I: e
9 S% V9 g! ]/ C1 G9 X: U

5 K! r4 i4 i5 Khttp://117.143.63.254:9012/www/studio/download/RT-Thread%20Studio-v1.1.3-setup-x86_64_20200731-2100.exe
- E: |6 V! s) M5 _2 ~; M/ E2 a9 S- w% `7 X- ?

' r3 @+ w& m6 I3 C* K% {9 q# e7 b
4.png
下载链接
安装过程和常用的软件安装方法一样,选择安装路径,然后Next就行了。
3 p* l, n8 i0 O' K& }! D$ ~- m* j& C/ v! b: j0 ~3 Z
1 |- C3 [: e# Q1 W8 }5 b' |5 s: R
1.硬件准备- ?, H$ `* h3 D1 L
开发板用的是我在大四时自己设计的STM32开发板——NiceDay,基于STM32F103RET主控。 这是我设计的第二块板子(第一块是毕业设计两轮平衡车主板),是在大四快毕业时,毕设实物和论文完成之后还有点时间,就设计了这款板子,最开始是准备做桌面天气时钟的。
. H# n' a8 t  O
& B6 s* i& ]# D" M) d

* ^' P6 B7 V5 ?1 _5 ?& u$ i
5.png
开发板
2.新建工程4 w7 U& l4 y3 i+ j) y: H- Q
RT-Thread Studio支持创建裸机工程、包含RT-Thread Nano版本的工程和包含Master版本的工程。这里,我们选择创建RT-Thread 项目,即包含完整版RT-Thread的工程。
! A' k# x/ b; L+ F( s1 M
6.png
新建项目
工程支持基于芯片创建工程,或者基于已有的BSP创建,这里使用的是我自己设计的开发板,所以选择基于芯片,选择芯片型号:STM32F103RE,调试串口选择串口1,调试器选择J-Link,SWD接口。1 |. F4 M. h% L. _
7.png
新建项目
创建完成之后,直接按Ctrl+B编译整个工程,第一次编译时间会长一点,如果修改很少,下次再进行编译就会很快了,可以看到无警告无错误。
' k; Y* z% j0 B; o
8.png
编译结果
使用SWD接口连接JLink调试器和开发板,开发板上电,直接点击下载按钮,也可以使用快捷键Ctrl+Alt+D下载; J+ Q, z6 n" a# N
9.png
下载程序
底部可以看到下载信息,从LOG来看,下载的程序文件是Bin文件,比较,擦除,编程,验证,复位整个流程耗时13s左右。. d0 c# \' r2 }5 L
10.png
下载LOG
RT-Thread Studio是自带Putty串口终端的,点击终端图标:: c& v. ], S9 B# G1 o/ N
11.png
终端按钮
选择串口号、波特率、文字编码方式等。: j7 {) `" }* g2 p
12.png
配置终端
底部切换到终端窗口,可以看到串口终端输出信息:4 b1 V* o: I8 I% b
13.png
串口终端
这样,不到5分钟,一个基于STM32F103RET6的工程模板就创建好了,包含RT-Thread完整版操作系统,整个过程不需要写一行代码,完全图形化配置。
* m7 c/ b$ D" r& F  v8 E3 r$ X
! B9 h1 B2 e' K6 a, a
2 R: w3 ?  c- d+ P) f! a" g
3.添加LED闪烁功能
0 i3 F6 T8 a  n* y  E作为单片机点灯小能手,RT-Thread下如何点灯是必须掌握的。打开RT-Thread组件图形化配置界面,可以看到默认开启了PIN和串口设备驱动的。0 b' p  l. P: O# B
14.png
图形化配置界面
在main.c文件中添加LED闪烁功能。包含头文件和添加宏定义. Q  V7 ?) x- [) p2 x/ n$ P
  1. #include <board.h>; F2 [& h9 A3 o- H9 r
  2. #include <rtdevice.h>
    ( j* u1 ^- @' M# F% j8 {$ s

  3. & A) K" X8 \- ?, m1 ]. x8 O
  4. #define LED_RED_PIN     GET_PIN(A, 7)0 a, l2 S+ G& n
  5. #define LED_BLUE_PIN    GET_PIN(A,6)
    7 F$ O( M  o. o! `: a3 t9 ~( o
  6. 6 a8 |3 l- t( q& X" J, i
  7. int main(void)  C& R0 O; ~+ `! @0 d- N
  8. {; Z1 V$ H) y$ |
  9.     int count = 1;
    # T; t, r6 E7 Y( d, P# n: F  ^
  10.     rt_pin_mode(LED_RED_PIN, PIN_MODE_OUTPUT);
    7 Y" @" X* L! u' l$ Z
  11.     rt_pin_mode(LED_BLUE_PIN, PIN_MODE_OUTPUT);: E' g+ p) s/ t$ s  ]
  12. : |! R6 i8 J6 _2 |
  13.     while (count++)4 r# [: c7 w1 P8 d+ y3 S/ Z0 g
  14.     {; J& o7 P: E* d8 M, U; E) @# y9 C
  15.         rt_pin_write(LED_BLUE_PIN, PIN_LOW);
    & y/ S$ \4 y9 R
  16.         rt_pin_write(LED_RED_PIN, PIN_LOW);* z0 G4 o5 A1 Q' L  t+ z
  17.         rt_thread_mdelay(100);4 u- D6 a' u1 n3 T3 M% R0 E8 n7 u8 m
  18.         rt_pin_write(LED_BLUE_PIN, PIN_HIGH);( N5 x! X& L; V0 |5 u
  19.         rt_pin_write(LED_RED_PIN, PIN_HIGH);
    $ i& ?' g5 M+ j) q" f
  20.         rt_thread_mdelay(100);
    8 [; G( p/ R' i) Q! k
  21.     }
    ) Q  u$ ~% I) w
  22.     return RT_EOK;
    , Q( g2 U" C* P6 w7 d
  23. }
复制代码
重新编译,下载。可以看到LED闪烁起来了。工程默认是使用内部RC作为输入时钟,所以无论你的板子是8M还是12M,都可以正常闪烁。我的开发板是8M晶体,这里我们配置使用外部HSE作为输入时钟。2 M$ ^3 O$ ~* q2 W

3 w6 z' f- _5 T4 t2 {

8 U. z5 v3 J8 Q- `+ H6 S* B打开drivers->stm32f1xx_hal_conf.h文件,修改HSE_VALUE宏定义为8M。
& ^# Z* p5 g1 u. }" N
15.png
晶体频率修改
打开drivers->drv_clk.c文件:) \* h5 w& W9 m8 O7 p) v
16.png
时钟源修改
配置PLL时钟源为HSE,并设置倍频系数为9。
/ y1 T# P3 d' a- r; o+ ]) Y
17.png
时钟源修改
18.png
倍频系数
这里根据实际板子晶体频率来设置,如果是12M晶体,倍频系数应该设置为6,如果是16M,需要参考时钟树,先2倍分频,然后9倍倍频。
3 R: _( ]& [( g: u
  1. #include <rtdbg.h>! W( Q7 {5 \& [" y( d" {# O
  2.   h+ Z" P; r6 [# q  p
  3. void system_clock_config(int target_freq_Mhz)4 ?1 b# V! G7 B, l
  4. {1 p8 _0 C/ v! y! _3 V
  5.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    ( r' G" S  F/ l# \

  6. 3 v+ r6 |2 M% y* ?8 W: |
  7.     /** Initializes the CPU, AHB and APB busses clocks
      F1 @8 E" f* [5 @9 Y, }1 P) f: v8 Q
  8.     */
    8 C* m! b; J# h; ?9 U& |
  9.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    5 n1 @" {# F* U. H; ~4 g
  10.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;4 t2 c, k- `" @( ^) x8 ~2 M
  11.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    ( e4 k5 E% x+ T
  12.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;8 P8 ^- z" ~( d; y  v- b
  13.      ........   9 Q5 a: c, q! V0 K: s" W
  14.     //9倍频1 {6 Z) E2 S3 R( p
  15.     RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;   //8*9=72M! e" B+ l6 Y2 b3 s, r6 D
  16.     ........
    + B' q$ z& V- q% I6 ~) ?
  17. }
复制代码
这样就修改为外部8M晶体作为PLL时钟源,再次编译下载,和之前的现象是一样的。
  |- g; s: \* ^
3 ?3 F" k8 l7 T; _
0 k5 G$ T' T0 T( J0 `9 U* R" o7 |
4.添加ESP8266软件包
5 w7 I$ K5 g1 y4 M% b联网设备,我们选择的是ESP8266-01S,如果看过上一篇疫情监控三部曲——在STM32F103 MCU上实现(裸机版),里面介绍了如何配置ESP8266 GET HTTPS请求, 配置工作模式 > 连接WiFi > 与服务器建立SSL连接 > 发送GET请求获取数据等等,整个流程固定而繁琐,那么能不能封装成一个模块,直接拿来使用呢?  Y4 G/ @6 ]3 K9 N
19.png
esp8266
这里就要介绍RT-Thread的AT Device软件包了,3 _/ L' }1 o# L4 N4 g

/ ^" ?0 @) w% X, P3 e
& i$ P- w2 j+ a3 R  I1 N) M
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 命令章节 。
4 v) p* n7 j# \3 A$ L3 ^; j5 o9 bhttp://www.rt-thread.org/document/site/programming-manual/at/at/
6 |) ^% w! F2 y# G- i5 r( X
) _$ S* |2 B+ i! m6 Y# j

4 @7 b1 Q* S& M1 w简单的说,就是我只需要调用这个软件包,然后修改WiFi账号和密码,就可以直接配置ESP8266联网了。. }1 a- u' Y  Y! t- v
* \# |$ M/ L+ i5 x

, {- U0 z0 i  L9 E4 X4 l由于AT Device依赖于libc组件,所以在添加AT Device软件包之前,先开启libc。8 h1 Q0 s& B$ k% D2 e( S

4 x& |2 D5 h% Z

/ C5 d! p9 \" r: f' c在RT-Thread Settings中点击libc灰色图标,变成彩色说明已经开启。; |5 z% {! f; |: D
20.png
组件配置
添加AT Device软件包,点击立即添加
9 D9 w1 P+ N& r5 T3 \1 _& w
21.png
软件包
在弹出的软件包中心,搜索at_device,然后点击添加,添加到当前工程。
5 r& \) \* [5 w* K8 j& r
22.png
软件包
在at_device软件包上右键,选择详细配置:
. t& o1 Q3 U$ ]: {, s9 T
23.png
软件包
在弹出的页面,选择我们使用的WiFi模块类型,乐鑫的ESP8266系列,并配置WiFi账号和密码,WiFi模块所连接的串口号。2 B1 O; f2 ?# c, o8 |6 |) ^( D
24.png
WiFi配置
点击保存之后,工程会重新进行配置,添加相应的软件包文件到当前工程,重新生成Makefile文件,rtconfig文件等等。
) c5 O, t( X7 k0 N/ L6 {+ U
5 Q3 l6 q* R6 Q$ p8 }# Y6 P

( p' W2 T! C4 d" o虽然我们在at_device配置中选择了uart2作为at_device设备连接的串口。但此时串口2并没有开启,还需要我们手动使能。
) C; v; s) a$ l
( `; Q5 ~8 w: g- q% f! ^

8 F3 e# y3 W7 O$ m; |2 y* q打开drivers->board.h文件,通过宏定义的方式使能串口2。
9 y: A' N) L% }* Q
  1. #define BSP_USING_UART2  ^2 N& p8 o1 r9 K3 r
  2. #define BSP_UART2_TX_PIN       "PA2"
    + c: j, w4 L! i) H
  3. #define BSP_UART2_RX_PIN       "PA3"
复制代码
这样就开启了UART2的片上外设,Ctrl + B重新进行编译,时间会有些长,编译完成之后,可以看到flash文件大小明显比之前大了。+ t( o, G- O4 ^; _0 U
25.png
编译结果
Ctrl + Alt + D重新下载运行,打开串口终端:
% E. A- l; t0 i" ^: |$ r, c* X6 a
26.png
终端
可以看到,UART2初始化成功,WiFi连接成功。说明我们的串口模块已经可以正常工作了。提示[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!失败信息,是因为当前ESP8266的固件版本不支持AT+CIPDNS_CUR?这条命令,把固件升级到最新版本就好了。这个不影响后面的操作,所以就不用在意这个了。
4 |0 r" ]# u% ?. Q0 |) z' K3 u  X. e: b3 |' o/ M- V
! F9 ?1 C6 E( B: M9 K
测试一下ifconfig和ping命令,都是正常的。
; q0 P8 t9 U5 p" s
27.png
终端
在RT-Thread Studio中配置ESP8266模块联网,整个流程只写了3行代码,可以说是非常的快速方便。
8 D. }9 L' G1 c' W/ D
! O) i/ Z0 G* q  i9 d# L
) F4 Y, _% Z: a+ s& ], D# o9 q3 I
5.疫情数据的获取
% y& H+ m$ i5 nWiFi模块连接上互联网之后,就可以连接GET疫情数据的API接口http://lab.isaaclin.cn/nCoV/api/overall,然后读取返回的疫情数据。在上一篇的裸机工程中,是通过先和服务器建立SSL连接,然后发送GET HTTPS请求,获取到的返回数据,那RT-Thread有没有这样功能的软件包呢?这里就需要添加另一个软件包webclient。
% j8 A% I' y% e( C3 m9 W7 N+ u; i& \* ?

7 n: v5 k0 p' L2 v( mWebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。
" P2 Z  }$ k3 K8 j, VWebClient 软件包功能特点如下:) j- G. O* W1 N, C

$ n4 U* K1 P1 A2 a) N
8 d; w- l! Y4 E% I- u3 A* G) {8 J, A
·支持 IPV4/IPV6 地址;
# h& R4 `5 |4 C·支持 GET/POST 请求方法;  S+ Y7 g! r5 W" b: A2 N% _1 v! ?
·支持文件的上传和下载功能;& N/ f. A7 c3 o  h) x* Z
·支持 HTTPS 加密传输;1 R- D0 c9 e. W2 O3 r  H- e! q  {
·完善的头部数据添加和处理方式。5 I$ y. ?2 T$ P

8 X! p+ j2 W* e, G

# ?, z* ]) @$ a# \) T; N5 [' v和添加at_device一样,在软件包中心中搜索webclient,6 `' C2 ^3 T4 `4 Z" h/ e5 f, _
28.png
软件包
然后添加到当前工程,右键进行配置,由于我们的http://lab.isaaclin.cn/nCoV/api/overall这个疫情数据接口是HTTPS类型的,根据软件包使用手册,我们需要选择TLS模式中的 MbedTLS。勾选添加GET和POST示例。! m0 t- j( F6 g2 ^1 o- Y1 Y3 x  d& Z
29.png
软件包配置
保存配置,看一下当前已经添加了哪些功能,可以看到有一些组件我们并没有去打开,但是已经被开启了,这是因为有些软件包是会依赖一些组件的,当使能软件包时,一些依赖的组件也被同时使能。9 k; K+ j: ~- E# z5 x& x) Q# N
30.png
软件包
Ctrl + B编译,Ctrl + Alt + D下载运行。在终端输入web_get_test测试GET请求功能。
- ~& [0 ?/ S- i+ G, u
31.png
GET示例
可以看到,执行get命令之后,会返回一个字符串,那么GET的是哪个地址呢?打开packages->webclient-v2.1.2->samples->webclient_get_sample.c文件,7 W& i6 f  L* w( U% _- [
32.png
示例代码
可以看到GET的是这个地址:http://www.rt-thread.com/service/rt-thread.txt,我们用电脑上的浏览器访问一下:
" ], X4 p1 [+ p6 F
33.png
浏览器访问
经过实际测试发现,GET HTTPS请求,还需要使能软件模拟RTC这个组件,否则会报assertion failed at function:gettimeofday, line number:19错误。1 y4 l. [7 A8 Y" ^# @
34.png
使能RTC
我们重新写一个获取疫情数据的函数,并导出到MSH。5 h# s  ?  R$ G0 i/ ]4 l$ O# g" o8 S. L

6 ^1 V: e  k- f$ c, G- L+ U

/ c$ M7 O: w( T" |: `usr_ncov.c文件内容
& ^. _3 @4 X! J6 |/ Q
  1. //usr_ncov.c
      }2 Z# E9 s1 w9 ?
  2. #include "usr_ncov.h"
    / X4 t9 X, X& |

  3. * C0 H& b* m% Y
  4. int get_NCOV_Data(void)
    % z: \6 ^* k7 l7 |. V
  5. {
    " J. Z! a# s3 S- Q/ v
  6.     char *uri = RT_NULL;1 t- S. _. H9 e6 o  y9 {0 o
  7.     struct webclient_session* session = RT_NULL;8 `2 O- Z) e9 I3 ]$ O
  8.     uint8_t *buffer = RT_NULL;; _7 B& o& @" T" L( \
  9.     int index, ret = 0;3 M" Q! t! x& F) |
  10.     int bytes_read, resp_status;( z2 D! [. e4 E+ W' v
  11.     int content_length = -1;
    " \' B& z- }" J0 o- s$ i) s
  12.     int buffer_size = 1600;5 A9 _- m' F4 A' ]& b8 x" [
  13.     uri = web_strdup(API_NCOV);  |  W9 W8 u  N
  14.     rt_kprintf("start get api: %s\r\n", API_NCOV);3 e9 `" d3 c- E% z" V0 J
  15.     if(uri != RT_NULL)
    # M$ J# y) ]- O  M! Q
  16.     {# d" A+ d- C0 e( b6 }
  17.         buffer = (unsigned char *) web_malloc(buffer_size);4 t' G4 f3 i  \# J2 q- I
  18.         if (buffer == RT_NULL)
    : u* h) R* c3 c, K/ `# i5 N
  19.         {
    6 e5 W5 S, g, S) A4 n7 B% a7 i9 f8 N+ n/ U
  20.             rt_kprintf("no memory for receive buffer.\n");4 A3 S  ?! G/ S2 A5 J9 {3 [
  21.             ret = -RT_ENOMEM;! \% K; o/ W% Y) W: ^2 a" K- T4 V: l
  22.             goto __exit;
    , o- F: q4 e: `$ ?6 w& x1 x3 y
  23.         }+ s( n3 c) C2 c$ n: v& e0 m2 l
  24. # u' m# d1 `5 J2 f) C1 n
  25.         /* create webclient session and set header response size */
    ( g( L$ `4 l  }9 u2 @
  26.         session = webclient_session_create(buffer_size);
    4 A- ]9 z  K8 f
  27.         if (session == RT_NULL)! _6 T4 M2 G9 `- y- i
  28.         {
    6 T+ _8 Z8 U: p% k
  29.             ret = -RT_ENOMEM;
    + ?; h9 k* T1 a1 [% k
  30.             goto __exit;
    8 g2 z) n. \5 s' T
  31.         }6 |( ?3 o( M+ O
  32. : @0 h6 f% f! y
  33.         /* send GET request by default header */6 `, i. J9 `3 [" A4 a
  34.         if ((resp_status = webclient_get(session, uri)) != 200)
    - Y% W7 t. ?, t  j) Y% {/ e
  35.         {
    4 _8 b. @5 c5 ~; \6 ]
  36.             rt_kprintf("webclient GET request failed, response(%d) error.\n", resp_status);+ a( e. P$ q! c& A: m
  37.             ret = -RT_ERROR;
    4 W/ i/ }* H- Y) m
  38.             goto __exit;
    , h: S& R( Q( [: S7 `
  39.         }
    ' v5 ^  R) ]2 G+ [

  40. $ T/ E9 ^8 a9 V1 r; F( b
  41.         rt_kprintf("webclient get response data: \n");
    ! w/ t# V% u: N& J3 T
  42. 9 j3 [/ q! Q( Y
  43.         content_length = webclient_content_length_get(session);
    ' ?1 i7 Q+ g) l6 B
  44.         if (content_length < 0)
    , X5 X$ @5 z  Z
  45.         {
    8 Y2 i& u9 |* F0 d% U" P
  46.             rt_kprintf("webclient GET request type is chunked.\n");
    & i% ~/ K4 f, u3 h) l9 t# j
  47. 0 v/ T  |3 m8 K! Q) X+ e  K7 t7 S6 i' ^
  48.             do
    * F" D: y+ [5 r1 i0 j
  49.             {
    6 j' e# H& k" ]: P  X
  50.                 bytes_read = webclient_read(session, buffer, buffer_size);
    # Y( Z) u1 `4 s8 l2 n5 y0 r. e
  51.                 if (bytes_read <= 0)
    6 c7 e9 F6 Q  `0 q$ j0 E7 p
  52.                     break;
    8 a0 e. I+ E8 P, W
  53. 3 Y' k3 G% K+ c) a
  54.                 for (index = 0; index < bytes_read; index++)) C; n6 J1 L7 Z# v" }! j. H- p% ^
  55.                 {
    ! W) x4 |; C+ P1 K
  56.                     rt_kprintf("%c", buffer[index]);8 `' [+ z- u  ]; y/ K0 C0 l8 ]# H
  57.                 }
    : T( H8 j( v+ L7 a; t& \0 x6 O
  58.             } while (1);2 J; K* t. G$ M# S3 R  f3 s

  59. 0 K) z! N" {  T0 \/ q. R
  60.             rt_kprintf("\n");
    , S' W, }! ~0 F: _4 |5 v3 i- z5 o
  61.         }
    4 c1 v8 x$ }1 e/ f: c
  62.         else
    5 m( H8 j) g' C* x4 B
  63.         {  T" |6 Z: S+ Z+ h8 A* m/ _
  64.             /* 读取服务器响应的数据 */
      O8 N& t$ `$ V9 T  h1 l
  65.             bytes_read = webclient_read(session, buffer, content_length);( \9 z4 A# J. t/ H5 G& N) t
  66.             rt_kprintf("data length:%d\n", bytes_read);
    / x4 H% M. |$ o6 Y9 ?, i2 C6 o6 h
  67. : G7 T& B8 a% Z8 t- D
  68.             buffer[bytes_read] = '\0';* }0 l7 q4 U5 h
  69.             rt_kprintf("\n\n %s \n\n", buffer);
    3 S) f4 x" I4 ?* z3 }2 t
  70. //            rt_kprintf("parse data\r\n");! k: f9 Y9 J* o
  71.             // parseData(buffer);        //解析函数- h9 y* n6 e, z: ^4 |) l& W
  72.             rt_kprintf("\n");  q& S; o4 |% \1 S+ R2 h
  73.         }; D6 @4 G' x' k; _9 b0 i" t3 R* H

  74. $ q  Q1 L$ w% N1 z: |$ X* Y
  75.         __exit:
    / [5 P# I$ x, a4 U7 y- {
  76.         if (session)
    7 C# h+ f  b5 h4 e4 L% n
  77.             webclient_close(session);! y& G) \/ u& _: _6 W0 y' l

  78. " f" p7 {5 b4 S7 h1 X
  79.         if (buffer)' U7 q+ [' I9 w4 a* L
  80.             web_free(buffer);
    ! g: D+ s% _  z4 v3 ~: e, e
  81.     }
    / b7 [9 l: }; z) K$ y
  82.     else
    6 S+ a; v9 W! D6 F0 I3 j: Y* h
  83.         rt_kprintf("api error: %s\n", API_NCOV);
    # K6 V5 t+ c& d

  84. 4 r* T6 s. B, H" N
  85.     return ret;
    4 B) Y' F( R. U2 }& M
  86. }
    2 h3 w- a4 z2 d0 e' h3 ~
  87. MSH_CMD_EXPORT(get_NCOV_Data, get api ncov);
复制代码
usr_ncov.h文件内容# {) U+ X* l# E" q# y( k
  1. #ifndef APPLICATIONS_USR_NCOV_H_7 F. }' i8 f* N/ A( V) y0 s
  2. #define APPLICATIONS_USR_NCOV_H_
    ) q. @8 ?* w7 w
  3. #include <webclient.h>) a% Q* A2 X% g# e1 ~8 W7 S
  4. #include <rtdevice.h>+ Z; S) y- o4 M0 S: @8 O0 X& P
  5. #include <rtthread.h>: R4 k1 o: q( [8 r' O+ X
  6. #define API_NCOV     "http://lab.isaaclin.cn/nCoV/api/overall"  t; W( S9 f5 R% [  h6 h
  7. int get_NCOV_Data(void);
    & t% @: ]3 ^7 b0 E8 V
  8. #endif /* APPLICATIONS_USR_NCOV_H_
复制代码
重新编译,下载,运行。在终端运行这个命令:
% {# z, U1 b. a1 N
35.png
命令获取疫情数据
可以看到获取到了返回的数据,长度1366个字节。下一步就是对这个JSON数据进行解析,获取到我们想要的疫情数据。6 ?2 h5 p( N( R  ]# \# e" x

- o# ^! h5 J$ u7 x# U; K# G

8 _0 ]* f3 R1 M& g7 S9 L% h7 d0 u6.疫情数据的解析( x3 L9 W7 p, R2 H4 X( [1 a
API返回的数据是JSON格式的,关于JSON的介绍和解析,可以查看使用cJSON库解析和构建JSON字符串。数据的解析使用的开源小巧的cJSON解析库,我们可以在软件包管理中心直接添加:, Z: `3 L! a$ d! Y: z
36.png
添加cJSON
在进行解析之前,先来分析一下JSON原始数据的格式:results键的值是一个数组,数组只有一个JSON对象,获取这个对象对应键的值可以获取到国内现存和新增确诊人数、累计和新增死亡人数,累计和新增治愈人数等数据。
( |1 f& J% g; d  N. C. L* }
. G$ v3 @$ y% n3 k: o' l8 J
2 l) i* x* [5 i8 S' P& T/ p
全球疫情数据保存在globalStatistics键里,它的值是一个JSON对象,对象仅包含简单的键值对,这些键的值,就是全球疫情数据,其中updateTime键的值是更新时间,这是毫秒级UNIX时间戳,可以转换为标准北京时间。/ m, x$ ^4 ?6 |3 \9 o
  1. {
    - K6 Y. U( [* Q
  2.     "results": [{+ B9 `- r! _; F7 W  x
  3.         "currentConfirmedCount": 509,
    6 Q7 E9 p3 F# F  j
  4.         "currentConfirmedIncr": 16,
    $ u) X# ?0 [- H6 m
  5.         "confirmedCount": 85172,
    ( I( `* v# ^# {( w$ A1 W
  6.         "confirmedIncr": 24,. I+ ^6 b; I, u" m/ N5 o7 J9 ~
  7.         "suspectedCount": 1899,
    * R7 p1 y: U0 p5 I! p2 W$ i9 T( y
  8.         "suspectedIncr": 4,. L) v! {  R  [$ F6 P0 m/ Q
  9.         "curedCount": 80015,
    ; }  Z3 g+ N$ |; m7 `, y7 `. \
  10.         "curedIncr": 8,' Y+ d; `7 D" c3 D: g! q, ?
  11.         "deadCount": 4648,
    7 n! i$ L! z7 _9 X$ ^- A% k- D# x8 I
  12.         "deadIncr": 0," i" h  A! {3 E7 B( |
  13.         "seriousCount": 106,2 a* i8 X1 E. y+ x+ l9 t
  14.         "seriousIncr": 9,+ N  a0 }5 |* j2 o
  15.         "globalStatistics": {
    . O0 k4 ]& e  z" g% _. L
  16.             "currentConfirmedCount": 4589839,
    , A& K; f+ T6 l8 J* M+ N- h( O
  17.             "confirmedCount": 9746927,
    1 K( d/ N' i' d  A9 ^
  18.             "curedCount": 4663778,
    # n' N' Q+ G0 R/ U' }  M
  19.             "deadCount": 493310,
      M8 |) m: F5 @
  20.             "currentConfirmedIncr": 281,
    + l8 t* e+ t4 u  ^( [6 M% y
  21.             "confirmedIncr": 711,
    0 t5 y1 B/ U$ w  s& |
  22.             "curedIncr": 424,+ g. X4 Q' j) ^+ q7 N. L
  23.             "deadIncr": 6
    $ {+ P) [+ o5 L) E% t/ E1 z7 S
  24.         },
    2 K6 G  a* L& [7 J8 q' s+ p+ z5 `3 }5 R
  25.         "updateTime": 1593227489355% T3 v% g5 R$ k$ T6 h+ O
  26.     }],4 g! h4 x1 s0 t( W( q& |
  27.     "success": true# a$ V- D0 i& S' B
  28. }
复制代码
先定义了结构体NCOV_DATA,用于存储国内和全球疫情数据:5 ]+ N: n7 b# B
  1. struct NCOV_DATA{
    " B7 Z& s$ `* f: U
  2.     int currentConfirmedCount;
    $ B- j2 G- t4 s' _8 h" r( ?9 D" R
  3.     int currentConfirmedIncr;3 X$ T9 A) l3 L; H0 Y2 K+ n4 Y
  4.     int confirmedCount;6 h2 ~- @* j, D% p
  5.     int confirmedIncr;
    ( R( a) Y% u% R2 U
  6.     int curedCount;
    ! R$ [& h+ }/ D! q& O4 \/ ^5 U( ^
  7.     int curedIncr;3 a" @2 i$ v# {! a1 j
  8.     int deadCount;0 B8 X$ X, n' z; I7 F" ^
  9.     int deadIncr;5 X+ L% h4 V2 }, B. `- A; P  s  M/ w
  10.     int seriousCount;6 Q8 c! `  Q2 L3 G+ r4 E* [
  11.     int seriousIncr;# T( M# V7 X& J0 B" R+ T
  12. 2 V6 ?0 o0 \* T& U
  13.     char updateTime[20];4 n' Y$ o3 W5 M5 q5 F& d) V
  14. };
复制代码
对应的解析函数:
6 ^& E# L0 m7 L: M
  1. #include <cJSON.h>
    , `* ^& Q5 f+ Z- x
  2. * V! Z8 _& ^' m6 L3 E9 c6 W7 V
  3. struct NCOV_DATA dataChina = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "06-13 16:22"};;! z. I3 i$ \, h6 E6 f
  4. struct NCOV_DATA dataGlobal = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};1 t( l' w. g. [6 c! k- n

  5. 0 l* E$ Q5 ]  Q8 T: j& r* ?# K
  6. int parseData(uint8_t *str)* g0 C4 U6 J% ]$ t* T0 N" V
  7. {
    7 [, X; P* ^0 k3 P, U
  8.     int ret = 0;3 F' Q" F' n5 _; k" U, `; U2 R
  9.     cJSON *root, *result_arr;
    + Z  ^2 G3 a& n1 p9 P, w; \, i
  10.     cJSON *result, *global;
    : I& k& j$ a1 r2 g$ H9 n: x) q
  11.     time_t updateTime;/ e0 n, C/ C% ]; @3 W% ^$ W: s
  12.     struct tm *time;
    4 |! T) ^' ~$ H: h" Y- _

  13. 2 a$ Z% }: ]5 ~
  14.     root = cJSON_Parse((const char *)str);   //创建JSON解析对象,返回JSON格式是否正确
    $ A6 n) ], D4 A/ b- S
  15. ! t/ |7 m7 V0 F- V8 h
  16.     if (root != 0)
    1 g/ S2 q8 I+ T0 f
  17.     {
    , Z! Y8 x  S0 C8 N2 N
  18.         rt_kprintf("JSON format ok, start parse!!!\n");9 B. S4 N+ ^. N. q; }( E
  19.         result_arr = cJSON_GetObjectItem(root, "results");
    1 [8 m) a7 w4 d, Z
  20.         if(result_arr->type == cJSON_Array)
    % W1 i3 m! s  g) S6 l2 d
  21.         {
    & e5 C" l/ A+ [+ ?7 b
  22. //            rt_kprintf("result is array\n");
    : B$ ~( b) q" W% {
  23.             result = cJSON_GetArrayItem(result_arr, 0);  _9 @# V* L; p# B' Y9 ]
  24.             if(result->type == cJSON_Object)
    1 Z. p) t& A7 Y) f& G  f+ o2 `
  25.             {  Q0 Q9 f9 r- w
  26. //                rt_kprintf("result_arr[0] is object\n");
    1 d8 N; @& q7 A) u" F' K

  27. 7 s" t2 V: v: Q2 B: c
  28.                 /* china data parse */" ~2 P# J3 Q/ j- A+ F/ X7 x
  29.                 dataChina.currentConfirmedCount = cJSON_GetObjectItem(result, "currentConfirmedCount")->valueint;/ u# G' |% s# a/ E
  30.                 dataChina.currentConfirmedIncr = cJSON_GetObjectItem(result, "currentConfirmedIncr")->valueint;: R0 e* K! G8 o9 c
  31.                 dataChina.confirmedCount = cJSON_GetObjectItem(result, "confirmedCount")->valueint;) d, j- G  x/ |" j& y
  32.                 dataChina.confirmedIncr = cJSON_GetObjectItem(result, "confirmedIncr")->valueint;
    : A) \7 ?0 ~* |% _/ W$ D
  33.                 dataChina.curedCount = cJSON_GetObjectItem(result, "curedCount")->valueint;
    5 C4 H, X! @" g8 t: Y
  34.                 dataChina.curedIncr = cJSON_GetObjectItem(result, "curedIncr")->valueint;
    ' T  z4 x* Q7 Q8 c* D
  35.                 dataChina.deadCount = cJSON_GetObjectItem(result, "deadCount")->valueint;
    ) Q5 I' I* e9 ?8 }
  36.                 dataChina.deadIncr = cJSON_GetObjectItem(result, "deadIncr")->valueint;) _# o) u4 r  e& i# Z- E6 z1 ~

  37. 6 N, a7 I0 v7 O$ C
  38.                 rt_kprintf("**********china ncov data**********\n");
    ) ^5 w0 Z& j  y$ i! w
  39.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataChina.currentConfirmedCount, "currentConfirmedIncr", dataChina.currentConfirmedIncr);
    9 j& _* B3 w8 D% T( C- w0 p8 Q+ B# B
  40.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataChina.confirmedCount, "confirmedIncr", dataChina.confirmedIncr);
    / _# d9 |5 ?: T9 ?) C& o
  41.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataChina.curedCount, "curedIncr", dataChina.curedIncr);+ N' j8 e; L7 N: g
  42.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataChina.deadCount, "deadIncr", dataChina.deadIncr);
    ( J1 z: r; a- I2 ?1 O  T& B) @
  43. ( F+ y4 H% n# k$ U( B+ m9 U2 |! |
  44.                 /* global data parse */
    $ i' k, J; t4 R8 I
  45.                 global = cJSON_GetObjectItem(result, "globalStatistics");
    ! @' b8 G% w. A) }" F; e
  46.                 if(global->type == cJSON_Object), i, W& q( T1 c8 H3 l4 ]) W( O/ P7 q
  47.                 {
    ! x+ P9 w# H) V, T$ S; L7 M
  48.                     dataGlobal.currentConfirmedCount = cJSON_GetObjectItem(global, "currentConfirmedCount")->valueint;% [  `, `; `9 i6 s7 C
  49.                     dataGlobal.currentConfirmedIncr = cJSON_GetObjectItem(global, "currentConfirmedIncr")->valueint;
    7 D9 h6 \8 Q* A" q9 G3 h
  50.                     dataGlobal.confirmedCount = cJSON_GetObjectItem(global, "confirmedCount")->valueint;, R* M% s0 O: d: f- L8 m  S' C
  51.                     dataGlobal.confirmedIncr = cJSON_GetObjectItem(global, "confirmedIncr")->valueint;$ H1 Y0 Z8 k9 b% I1 q! G. Z
  52.                     dataGlobal.curedCount = cJSON_GetObjectItem(global, "curedCount")->valueint;
    * H0 e$ Z% d( A3 I
  53.                     dataGlobal.curedIncr = cJSON_GetObjectItem(global, "curedIncr")->valueint;7 Q" C% `8 z: H8 y& p6 D0 {
  54.                     dataGlobal.deadCount = cJSON_GetObjectItem(global, "deadCount")->valueint;3 [. L" ~7 C. e; `0 O& d6 f/ c
  55.                     dataGlobal.deadIncr = cJSON_GetObjectItem(global, "deadIncr")->valueint;
    % W2 X( I* Q0 C- H/ i8 x: S

  56. 7 T9 x+ k1 D6 z9 L, N* d; j
  57.                     rt_kprintf("\n**********global ncov data**********\n");1 ^" x, \  m# ?% S# |3 k+ [2 Y8 h
  58.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataGlobal.currentConfirmedCount, "currentConfirmedIncr", dataGlobal.currentConfirmedIncr);
    ! E1 {) N$ F9 }7 Z4 r
  59.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataGlobal.confirmedCount, "confirmedIncr", dataGlobal.confirmedIncr);! ]+ P% c4 @8 q  ^1 U
  60.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataGlobal.curedCount, "curedIncr", dataGlobal.curedIncr);
    7 W" R* ]' j$ e3 w" K
  61.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataGlobal.deadCount, "deadIncr", dataGlobal.deadIncr);
    8 r8 t+ V- f* T/ ^$ J

  62. - D. z% _( z5 \. ?
  63.                 } else return 1;
    & k1 ?: G. l# o! ^5 U
  64. 4 B2 ~5 @6 p# {' \6 R/ w( _9 o) e8 @$ S
  65.                 /* 毫秒级时间戳转字符串 */5 Z" P7 }& g9 i# y! \# w. v
  66.                 updateTime = (time_t )(cJSON_GetObjectItem(result, "updateTime")->valuedouble / 1000);5 k; o+ j9 f8 v$ T( d' p8 N
  67.                 updateTime += 8 * 60 * 60; /* UTC8校正 */, r% `* F" M4 H- x& D; k3 j" K$ Z
  68.                 time = localtime(&updateTime);
    * L% |/ ?' i% u8 M1 t
  69.                 /* 格式化时间 */5 X( T, i; y" M) y' y8 W  M
  70.                 strftime(dataChina.updateTime, 20, "%m-%d %H:%M", time);5 T7 B$ P: E+ H* A. I
  71.                 rt_kprintf("update: %s\r\n", dataChina.updateTime);/* 06-24 11:21 */% @9 x5 N+ ^: z5 k# K) c2 ?
  72.                 //数据在LCD显示( k; h7 z2 o- `  D/ p
  73.                 //gui_show_ncov_data(dataChina, dataGlobal);0 X3 Z" O9 S& F+ {* z
  74.             } else return 1;5 O7 R; \" W! }; Q
  75.         } else return 1;
    - i& m  F: i* B* Z$ H
  76.         rt_kprintf("\nparse complete \n");1 @& y- ]. b  v* `
  77.     }
    3 Y  a+ g4 [; \- V8 g2 U# v4 Q
  78.     else
    , O' ^( t  |# ~7 H3 G  f
  79.     {+ o; u$ Y5 Q. o) J
  80.         rt_kprintf("JSON format error:%s\n", cJSON_GetErrorPtr()); //输出json格式错误信息: ?( h% J# g3 Q% U' V' b
  81.         return 1;
    4 v5 }1 T& {' `% J1 f) N) N
  82.     }( u4 f% R( N; ~
  83.     cJSON_Delete(root);; j3 q- E$ c3 R1 i0 r
  84. 9 w9 _3 d7 \; s6 N
  85.     return ret;
    1 W3 f! D  p" ^1 E: m5 u* k
  86. }
复制代码
在数据接收完成之后,对JSON数据进行解析。
: i0 k$ {; W" Q/ C) R. B& h8 }
37.png
解析结果
7.疫情数据的显示
, b( r7 F9 N: _8 L8 U2 |% d5 F数据解析出来之后,剩下的就简单了,把上一篇文章中9341的驱动文件移植过来就好了。4 `6 F/ s# f' c5 ^8 }& O4 \8 h
* V+ H6 a* k, T5 l) B# l; E

/ V5 h" @& |$ \4 i9 g& W5 r$ g液晶屏使用的是3.2寸 LCD,IL9341驱动芯片,320*240分辨率,16位并口。由于屏幕分辨率比较低,可显示的内容有限,所以只是显示了最基本的几个疫情数据。为了减小程序大小,GUI只实现了基本的画点,画线函数,字符的显示,采用的是部分字符取模,只对程序中用到的汉字和字符进行取模。为了增强可移植性,程序中并没有使用外置SPI Flash存储整个字库。6 y/ U! |7 i' J2 B+ D
; t7 F7 \. O8 ]

+ h4 u2 C3 U9 r5 t5 T+ f由于RT-Thread Studio使用的HAL库,所以LCD的GPIO初始化函数需要修改一下:4 s$ A* D: i% z& v5 \: r1 V
  1. void lcd_gpio_init(void)
      j- u3 R, C8 F: ?: ]+ C% m% v
  2. {
    $ n" X' ?) w( w6 \3 R' x4 o" n
  3.     GPIO_InitTypeDef GPIO_InitStructure;7 f* D1 I6 [1 v/ X6 O4 B

  4. 2 N1 G7 w+ q1 D3 r& g, @
  5.     __HAL_RCC_GPIOA_CLK_ENABLE();
    ' c- x7 e  {8 [* m' _' B
  6.     __HAL_RCC_GPIOC_CLK_ENABLE();
    6 e3 g4 R9 I3 y1 y& y' L
  7.     __HAL_RCC_GPIOB_CLK_ENABLE();& X% Y1 S/ c# C2 f& X! h" f- V8 g9 H
  8.     __HAL_RCC_AFIO_CLK_ENABLE();
    . F5 {8 `' o- I' c+ Q
  9.     __HAL_AFIO_REMAP_SWJ_NOJTAG();
    7 z5 p5 V: u0 z  D. S! {
  10. , I. }; l) t7 {
  11.     GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    , p: O2 r( q6 k- Y4 b) k1 m
  12.     GPIO_InitStructure.Pull = GPIO_PULLUP;
    9 p8 {! }$ u) d8 ^
  13.     GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
    4 H% o6 o, D# w: e: k4 }
  14.     GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6;
    + s. u3 l2 |2 e8 K( K
  15.     HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOC: s& S. [: K. U0 S1 Y* \# V

  16. 1 d+ `. w8 q3 T/ o/ \
  17.     GPIO_InitStructure.Pin = GPIO_PIN_8;    //背光引脚PA8
    ' J6 E4 h! i  E  R- E5 W7 X
  18.     HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); //GPIOC
    * c& f$ }+ Y  `7 }6 j9 P
  19. " z. S/ X. Y% w) f) y' M
  20.     GPIO_InitStructure.Pin = GPIO_PIN_All;
    / e0 F7 U' B9 ]! D  N+ |+ U
  21.     HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);; k8 t- w) ]- _8 `
  22. + B) N! y  w$ L! K& @5 u3 V
  23.     HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6, GPIO_PIN_SET);
    8 t5 l6 H9 s! I
  24.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);( f  g$ P: o: u. U: g, a- q- R
  25.     HAL_GPIO_WritePin(GPIOB, GPIO_PIN_All, GPIO_PIN_SET);) [6 C9 t/ _3 f
  26. }
复制代码
延时函数换成:
! ?6 _2 F3 V# @2 x- B* K& [+ g
  1. rt_thread_mdelay(nms);
复制代码
还有一点,在Keil中,文字编码选择GBK编码,1个汉字占用2个字节,而RT-Thread Studio为UTF-8编码,1个汉字占用3个字节,汉字显示函数需要调整:% }( b1 x7 d1 `# X6 z+ l: ~% Q
  1. void gui_show_chn(uint16_t x0, uint16_t y0, char *chn). |, }  X/ @+ Z! f, A
  2. {
    : x# a/ U4 c; h0 I5 K
  3.     uint8_t idx = 0;
    $ A4 R' x2 L  ^: [7 s# w/ x
  4.     uint8_t* code[3]; //UTF-8:国=E59BBD' A  c- ?# U1 k

  5. ; x( j7 Z. |9 I5 V" p
  6.     uint8_t size = sizeof(FONT_16X16_TABLE) / sizeof(FONT_16X16_TABLE[0]);" o3 F) r; y. V7 s9 b2 q
  7.     /* 遍历汉字,获取索引 */5 n% U4 |) J8 u& o  _
  8.     for(idx = 0; idx < size; idx++)
    2 Y0 \7 n3 S) J* L
  9.     {
    ) f  }  ~3 v0 r# k' \2 a! Q3 ]
  10.         code[0] = FONT_16X16_TABLE[idx].chn;
    , q; h: j7 Y" |  {) Y+ i
  11.         code[1] = FONT_16X16_TABLE[idx].chn + 1;- i, s1 [  Z" S: K) g8 C
  12.         code[2] = FONT_16X16_TABLE[idx].chn + 2;- `/ |/ Y8 i) B2 R3 E; z* y4 a
  13.         //汉字内码一致
    : X/ p: J/ \& W& B: t% W, V/ i1 G
  14.         if(!(strcmp(code[0], chn) || strcmp(code[1], chn+1) || strcmp(code[2], chn+2)))
    & ~& t: t4 y: z8 {, k
  15.         {# \; P! U" x8 M" [6 I
  16.             gui_show_F16X16_Char(x0, y0, idx, WHITE);! E# a( ^$ `7 V# t* B! U% `
  17.             return;
    ' @# |1 Q1 y; C2 h
  18. //            break;
    ) t9 n1 S: n* e: j$ v* [
  19.         }7 V5 |1 t7 ~. B4 X
  20.     }
    ! e; a, I* ?. }& ?3 C
  21. }
复制代码
疫情数据显示函数:
+ Q! Q0 `' ]( u1 u  A
  1. void gui_show_ncov_data(struct NCOV_DATA china, struct NCOV_DATA global)3 b, l7 n6 U, o
  2. {
      [6 @* ~' F6 K) c1 ?
  3.     uint8_t y0 = 20;
    4 [: ^0 J7 s4 m% J3 {
  4. & W* k% S8 `3 ?' h6 E7 R
  5.     lcd_clear(BLACK);
    7 b& r7 H) d4 U# P
  6.     gui_show_bar();9 t7 `7 o# v8 \. C
  7. " l# d- o0 ^3 s- h
  8.     gui_drawLine(0, 18, 320, DIR_X, WHITE);8 o! y6 O6 e+ H- o1 ^8 Z/ C
  9.     gui_drawLine(0, 38, 320, DIR_X, WHITE);) E: p0 l3 B. z8 R# r+ L
  10.     gui_drawLine(0, 138, 320, DIR_X, WHITE);1 V( ^/ b" G/ ]. k& U, P( D  t
  11.     gui_drawLine(0, 158, 320, DIR_X, WHITE);, p4 k; I* t) T; t2 D9 s9 _
  12.     gui_drawLine(0, 220, 320, DIR_X, WHITE);
    : S; \7 A0 i0 `& }; C
  13. 8 I% h  `; P! f) B' A' b: x5 Z
  14.     /* "国内疫情" */
    9 f" s" n5 e& G* ^# ~
  15.     gui_show_chn_string(128, y0, "国内疫情");
    9 s; `+ S/ ~4 w! }" L% a
  16.     gui_show_line_data(40, "现存确诊:", china.currentConfirmedCount, "较昨日:", china.currentConfirmedIncr);- d3 m0 Q8 |2 g  L% |+ s3 l
  17.     gui_show_line_data(60, "累计确诊:", china.confirmedCount, "较昨日:", china.confirmedIncr);. V/ k* q7 v) W- `: ~+ a5 w
  18.     gui_show_line_data(80, "累计治愈:", china.curedCount, "较昨日:", china.curedIncr);
    4 t7 }/ y  s: z# _8 |& z/ z$ O" d
  19.     gui_show_line_data(100, "现存重症:", china.seriousCount, "较昨日:", china.seriousIncr);
    $ R% j1 [5 ~( o8 r# H" A9 I  F
  20.     gui_show_line_data(120, "累计死亡:", china.deadCount, "较昨日:", china.deadIncr);; [5 E" m$ m' a! K/ n0 a4 D

  21. : @( U1 Q$ ~, V% E6 S" w+ _
  22.     /* 全球疫情 */& P5 L' C8 T4 W! I( |; U; |
  23.     gui_show_chn_string(128, 140, "全球疫情");! }, Y5 m4 t  S
  24.     gui_show_line_data(160, "现存确诊:", global.currentConfirmedCount, "较昨日:", global.currentConfirmedIncr);# ~  j4 Z4 W% s7 ~
  25.     gui_show_line_data(180, "累计治愈:", global.curedCount, "较昨日:", global.curedIncr);
    & P' G" S# N5 c. y! }
  26.     gui_show_line_data(200, "累计死亡:", global.deadCount, "较昨日:", global.deadIncr);
    ! C. D* t1 N2 f% b
  27. + l$ ^2 _/ u* c! L% z2 A; p6 @" ]
  28.     gui_show_chn_string(160, 222, "更新于:");
    ) m. ?0 E" F0 U: x  f
  29.     gui_show_F8X16_String(230, 222, china.updateTime, GREEN);
复制代码
最终显示效果
) o# S( z1 D2 `: S9 d
38.png
最终效果
开源地址
6 s7 X6 B& h& T代码已经开源,地址在文末,欢迎大家参与,丰富这个小项目的功能!
; Y; W$ X% `- Y& S3 U, B3 f$ v( D" J9 w

+ d* i1 J% ^2 z* s' I7 u; r  ]9 V基于STM32+RT-Thread的疫情监控平台/ k5 Z! V* B5 d
http://github.com/whik/rtt_2019_ncov
% |6 ?( {$ N9 ^, @) B. R4 t# ~! z# }4 e

( [: h9 r. Z. h基于STM32F103的疫情监控平台(裸机版)1 r$ J) Z1 R0 O% `9 }
http://github.com/whik/stm32_2019_ncov
8 ^' s" E5 E- [9 Z  M2 l5 y3 S- \9 l
4 l! _* }9 ]+ ~

& V) b$ {' r( m& B$ Q4 c% |文章出处:[color=var(--weui-LINK)][url=]RTThread物联网操作系统[/url]
) t* F7 b4 @0 X2 M+ K7 u8 S$ a+ m2 m1 Y5 t* K0 u2 P
收藏 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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版