请选择 进入手机版 | 继续访问电脑版

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

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

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

4 J8 v. f- \: H9 R' O
4 Y5 x+ s0 J- L0 g, n! j
上周末加班,这周末休息,有时间整理一篇之前做的基于RT-Thread的疫情监控平台。上一篇文章我们使用STM32F103 MCU裸机开发的方式实现了疫情监控平台。这次我们玩点高端的,使用RT-Thread Studio来实现同样的功能,一起来看看吧!& j4 H2 i- `$ U4 d
- ?- J9 f# ^* l2 b/ A& c  e7 P) U

, ^; N8 P1 c8 T7 j2 K( Y% H·文章目录
! a' x$ l: ^% o9 Q·使用到的软件包
; X! @+ g1 T3 L' D9 q$ O·0.RT-Thread Studio的下载和安装4 r! Y/ B$ b0 y$ U4 n  w; U

9 ~$ e7 p, l; ?( z! k: }9 h
8 y/ n' ~9 ?, k% o
1.硬件准备
) }- c# J' s. n0 _' x9 ?2.新建工程
0 d( L" y1 l" w% L: ^, ]3.添加LED闪烁功能0 p/ m# M: Z( ^% ~- O
4.添加ESP8266软件包# f9 m3 H7 C: T" I
5.疫情数据的获取
, y; `+ C% R; k6.疫情数据的解析& G+ c& n) v+ N0 l& @3 D8 s6 U
7.疫情数据的显示: g. X6 y; G0 [) V0 ]$ [
开源地址
; y+ n: _' E) e9 X( h/ `
: J0 M' f3 J+ r  ~7 u9 ~1 X0 r

" v% O5 ^# x2 L( a  e+ B最终的显示效果:
5 b1 E& ^0 D& R# b6 s
1.png
显示效果
有效文件就这9个,其他的就全是图形化配置:
: z' h$ p/ s5 p" h& C8 m
2.png
有效文件
整个流程下来,如果顺利的话,可以在2个小时内完成。! N) k( \) g- p% A+ w9 Z  J* ?
! b% K! k* @5 l0 J
使用到的软件包: ]7 e. m+ J: d0 B% B& P" J( |6 r
·at device:用于ESP8266配网
5 b& Z2 h. t1 S; P6 k: f* S. a·webclient:用于发送HTTPS请求# A( |: q1 S& u- g! v. C; K
·mbdetls:用于HTTPS加密
0 S# V' _% _% ^$ [* |8 w·cJSON:用于JSON数据解析
; }- u$ S4 {! w, q, [, ~* v* R4 b% m4 h6 |5 j8 [
0.RT-Thread Studio的下载和安装  B) U+ Y5 l8 E3 Z! E" [
一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效
3.png
RT-Thread Studio
·支持多种芯片,STM32全系列
- E  v% l! h8 G·支持创建裸机工程、RT-Thread Nano和Master工程3 T1 I9 Y9 b6 ^2 E& j
·强大的代码编辑功能,基于Eclipse框架0 U3 ?/ S- Q. u! |3 p$ Y
·免费无版权限制,基于开源Eclipse和ARM-GCC编译器。' @, {* C5 _7 G, W
·支持多种仿真器,J-Link,ST-link等,支持在线调试,变量观察。
% ?2 _$ r7 m, j·SDK管理器,图形化配置RT-Thread软件包,同步RT-Thread最新版本。
1 D0 }' @! V- a- r" t·集成Putty串口终端工具
* F# v" Y5 B! z4 k, ]1 j$ x) ]( O( I. E& a: j6 P6 r
9 X+ l% s/ Y' t% d3 @% P
更多的使用教程:
9 u$ u$ X4 }" vhttp://www.rt-thread.org/page/studio.html
3 Q. ]* n8 G7 q- z! v  j9 S% T% E1 g5 Y/ |
; v: R: X' n- u
目前最新版本为1.1.3版本,支持3种下载方式,我们选择最后一个下载方式,从RT-Thread 官网服务器上下载。
, l- n$ O5 U+ a! R$ |: E* i7 P$ P- Q8 P) A# w

: I+ O. p$ L) [* h4 Z2 L. v+ h+ U0 @下载地址:. G( h* e* K1 p" M( R

) q4 J5 Y3 k+ P9 {" _# k/ d) X
. o# r1 {( i" f
http://117.143.63.254:9012/www/studio/download/RT-Thread%20Studio-v1.1.3-setup-x86_64_20200731-2100.exe
2 v: F. C3 L# A
$ A. a8 ^( ]! K1 N2 q
6 q  l; L. B" h8 B
4.png
下载链接
安装过程和常用的软件安装方法一样,选择安装路径,然后Next就行了。% r! ~1 g5 t) B, \, q% S  ~
6 e# F) b- t3 Z4 q! M6 G$ n

( i9 e$ G$ K8 G; o2 E& I  e' m! ^+ |1.硬件准备) F' a6 M2 M0 y, B  R+ |8 X
开发板用的是我在大四时自己设计的STM32开发板——NiceDay,基于STM32F103RET主控。 这是我设计的第二块板子(第一块是毕业设计两轮平衡车主板),是在大四快毕业时,毕设实物和论文完成之后还有点时间,就设计了这款板子,最开始是准备做桌面天气时钟的。% ?( S0 G! n- o& `

# I, ^  _, m* d4 l

- [- i! `; w5 @7 O5 w7 _4 p3 v8 d
5.png
开发板
2.新建工程
4 b6 d* \8 F: H  L  T$ Q  RRT-Thread Studio支持创建裸机工程、包含RT-Thread Nano版本的工程和包含Master版本的工程。这里,我们选择创建RT-Thread 项目,即包含完整版RT-Thread的工程。% y" p6 }8 _+ ]9 A( g7 w
6.png
新建项目
工程支持基于芯片创建工程,或者基于已有的BSP创建,这里使用的是我自己设计的开发板,所以选择基于芯片,选择芯片型号:STM32F103RE,调试串口选择串口1,调试器选择J-Link,SWD接口。4 u5 Q: Y+ A4 Z: P0 f- y
7.png
新建项目
创建完成之后,直接按Ctrl+B编译整个工程,第一次编译时间会长一点,如果修改很少,下次再进行编译就会很快了,可以看到无警告无错误。4 r1 G1 T& f% T" c
8.png
编译结果
使用SWD接口连接JLink调试器和开发板,开发板上电,直接点击下载按钮,也可以使用快捷键Ctrl+Alt+D下载" s4 a2 j* X7 `0 l7 t# G
9.png
下载程序
底部可以看到下载信息,从LOG来看,下载的程序文件是Bin文件,比较,擦除,编程,验证,复位整个流程耗时13s左右。
& R# E9 x4 T) a4 f
10.png
下载LOG
RT-Thread Studio是自带Putty串口终端的,点击终端图标:
3 b: P& _+ O* f+ E
11.png
终端按钮
选择串口号、波特率、文字编码方式等。5 ^6 t5 _. d' ?- x0 P# S
12.png
配置终端
底部切换到终端窗口,可以看到串口终端输出信息:
% f6 W4 X6 f! A3 v
13.png
串口终端
这样,不到5分钟,一个基于STM32F103RET6的工程模板就创建好了,包含RT-Thread完整版操作系统,整个过程不需要写一行代码,完全图形化配置。
- a8 _, ^: G  E* ~  B$ N4 L+ G" O" a  e8 B5 {$ j

6 k2 J( P/ H9 v( n8 |1 t8 n; ?) _8 X3.添加LED闪烁功能: v& S7 o3 e( c! \  x8 M
作为单片机点灯小能手,RT-Thread下如何点灯是必须掌握的。打开RT-Thread组件图形化配置界面,可以看到默认开启了PIN和串口设备驱动的。
, Q8 ]# o6 b" {
14.png
图形化配置界面
在main.c文件中添加LED闪烁功能。包含头文件和添加宏定义
2 \3 B$ s; z8 F% U$ c5 K0 V- X
  1. #include <board.h>
    $ w: q5 x5 I* ~$ I: l% w
  2. #include <rtdevice.h>
    : C8 b% @; E) H

  3. + g4 _0 h/ x  ?$ X2 m9 z
  4. #define LED_RED_PIN     GET_PIN(A, 7): J* A) k9 U( D7 V& [5 m9 o3 ?
  5. #define LED_BLUE_PIN    GET_PIN(A,6)
    * p1 N+ `+ s3 O: i; j
  6. 0 c0 A# ^! t3 F8 p* m! Z5 g% m
  7. int main(void)- T4 K$ f0 X- M" ~% H: y2 ]
  8. {
    ! E+ S1 N" N- {4 S; F- a' ~
  9.     int count = 1;' L& Q/ i5 J1 h0 P! W3 n4 D. O
  10.     rt_pin_mode(LED_RED_PIN, PIN_MODE_OUTPUT);7 {8 \) a" f' X) L2 x
  11.     rt_pin_mode(LED_BLUE_PIN, PIN_MODE_OUTPUT);
    2 A" h% \% @9 p2 b# @$ M( I
  12. & l, y5 K& i/ ]  Q2 c) r7 Z, l8 [
  13.     while (count++)
    ; }% }, o, o% @' H; H3 k# J7 _
  14.     {$ G0 O$ v+ y' K& s4 p
  15.         rt_pin_write(LED_BLUE_PIN, PIN_LOW);
    5 U" h) v0 j1 X0 F
  16.         rt_pin_write(LED_RED_PIN, PIN_LOW);
    3 n7 U# k- O/ d
  17.         rt_thread_mdelay(100);
    3 N9 M3 F/ R8 P
  18.         rt_pin_write(LED_BLUE_PIN, PIN_HIGH);4 F! M9 m5 Z$ h5 S$ [3 x
  19.         rt_pin_write(LED_RED_PIN, PIN_HIGH);
    ) u6 U3 [1 U2 k3 @6 ~
  20.         rt_thread_mdelay(100);
    ' s& k6 x8 J9 Q+ f% L
  21.     }
    ( Y: w5 b2 m6 }4 z' \! P
  22.     return RT_EOK;- L1 B3 Z6 r% b5 N, p
  23. }
复制代码
重新编译,下载。可以看到LED闪烁起来了。工程默认是使用内部RC作为输入时钟,所以无论你的板子是8M还是12M,都可以正常闪烁。我的开发板是8M晶体,这里我们配置使用外部HSE作为输入时钟。  `3 T2 C& h% _
3 T* d+ W; u/ w& V" g* J
7 l% D5 |) j. @
打开drivers->stm32f1xx_hal_conf.h文件,修改HSE_VALUE宏定义为8M。3 I2 m! g; m. q) o# w
15.png
晶体频率修改
打开drivers->drv_clk.c文件:
0 Q: _: M& m& {
16.png
时钟源修改
配置PLL时钟源为HSE,并设置倍频系数为9。& @7 o/ @! H( O6 I
17.png
时钟源修改
18.png
倍频系数
这里根据实际板子晶体频率来设置,如果是12M晶体,倍频系数应该设置为6,如果是16M,需要参考时钟树,先2倍分频,然后9倍倍频。; d* u; z' X$ z- l* B, t
  1. #include <rtdbg.h>
    ! p% e  U+ r5 Z5 I0 b4 |

  2. 9 O7 W  ?1 R. }, L( a- a7 y' A
  3. void system_clock_config(int target_freq_Mhz)  w( @+ n, [& a* Z
  4. {1 R9 N6 n8 ~& g- e: M  i& Q! m
  5.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};- n; z/ |: o8 i% X# _* g' U

  6. ' J; _& L5 K: y' g! b# [' Q  A
  7.     /** Initializes the CPU, AHB and APB busses clocks1 W# r7 E$ l. q. O4 s2 d
  8.     */
    + G9 |+ b' |( |3 e! y2 q; P
  9.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    5 A/ P0 e2 ^; j) D* w: j
  10.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;9 A7 K5 \' `3 O$ k! Q6 |% v1 O
  11.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    / }' O( c& B* m& f. k( {% N
  12.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;: w! m9 Q) T! K3 `: [. @
  13.      ........   
    9 d: k1 ]0 W  n; @2 H9 H& G
  14.     //9倍频
    ; P* @# D2 k. K; V6 q5 o+ {
  15.     RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;   //8*9=72M8 f, @& Z8 J* E5 Z
  16.     ........- ~0 l8 E8 h8 n
  17. }
复制代码
这样就修改为外部8M晶体作为PLL时钟源,再次编译下载,和之前的现象是一样的。7 z, o  A7 n0 z, x

' T8 A- m( D. r  {6 }
7 X) Y3 @* ?' R# x* J
4.添加ESP8266软件包
% T3 F2 u8 q" x' V0 `& u  K1 f联网设备,我们选择的是ESP8266-01S,如果看过上一篇疫情监控三部曲——在STM32F103 MCU上实现(裸机版),里面介绍了如何配置ESP8266 GET HTTPS请求, 配置工作模式 > 连接WiFi > 与服务器建立SSL连接 > 发送GET请求获取数据等等,整个流程固定而繁琐,那么能不能封装成一个模块,直接拿来使用呢?2 f) a6 S0 d  G, v  t4 C% Y
19.png
esp8266
这里就要介绍RT-Thread的AT Device软件包了,, z% P5 S4 h. q" w
- y- x4 P* J  d/ v% D) I% l& X: {
- ?6 @) w! g2 V' Z7 G) v( n
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 命令章节 。: T' M+ V- _" i
http://www.rt-thread.org/document/site/programming-manual/at/at/
, E  k4 \( k, X* Y! l+ q: \) K8 x& l/ O" R; x1 l

; i1 e/ I* g+ g* B1 j简单的说,就是我只需要调用这个软件包,然后修改WiFi账号和密码,就可以直接配置ESP8266联网了。
- N+ A/ @) E" [9 G: c. V7 E
& h8 M% b8 K$ H2 ]; @" M# U: x

; u6 Z3 I) e1 u5 [" @2 ~由于AT Device依赖于libc组件,所以在添加AT Device软件包之前,先开启libc。$ A" b; G0 s6 I/ P' ^
9 \9 l0 a. L5 r; j3 e2 g! K9 j
* c3 f5 Z! p9 O& l' F0 T
在RT-Thread Settings中点击libc灰色图标,变成彩色说明已经开启。
" O6 ~6 u( @, L. v8 f0 _
20.png
组件配置
添加AT Device软件包,点击立即添加6 F# `- M, P/ v) A/ A) A/ X
21.png
软件包
在弹出的软件包中心,搜索at_device,然后点击添加,添加到当前工程。
4 j" f* W1 t6 r3 L! V
22.png
软件包
在at_device软件包上右键,选择详细配置:2 p7 @: J9 I0 o" [$ \8 i+ W9 I
23.png
软件包
在弹出的页面,选择我们使用的WiFi模块类型,乐鑫的ESP8266系列,并配置WiFi账号和密码,WiFi模块所连接的串口号。
/ E: v# [" R$ J4 w/ i
24.png
WiFi配置
点击保存之后,工程会重新进行配置,添加相应的软件包文件到当前工程,重新生成Makefile文件,rtconfig文件等等。: [% B2 T$ b1 b% O6 F4 g7 i8 \  p# Q& y
6 ^! U# C& K; n

3 U9 h  G& V' |2 r2 o5 j: p+ l虽然我们在at_device配置中选择了uart2作为at_device设备连接的串口。但此时串口2并没有开启,还需要我们手动使能。
* V, I" t" }0 o/ \5 ~3 ]9 }* T
, D* h( @3 z: L% N9 [: K

2 ]( ]4 L/ L5 M打开drivers->board.h文件,通过宏定义的方式使能串口2。; H1 z5 z- E( n+ f8 j% c
  1. #define BSP_USING_UART2
    * ?! o7 ]. I5 A0 j, m9 n
  2. #define BSP_UART2_TX_PIN       "PA2"
    2 P1 o2 ?$ P  F3 I7 _: f8 f1 |
  3. #define BSP_UART2_RX_PIN       "PA3"
复制代码
这样就开启了UART2的片上外设,Ctrl + B重新进行编译,时间会有些长,编译完成之后,可以看到flash文件大小明显比之前大了。- W, t: a, \6 U) F, h7 I- o
25.png
编译结果
Ctrl + Alt + D重新下载运行,打开串口终端:
, E# W3 e/ ?( {7 s
26.png
终端
可以看到,UART2初始化成功,WiFi连接成功。说明我们的串口模块已经可以正常工作了。提示[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!失败信息,是因为当前ESP8266的固件版本不支持AT+CIPDNS_CUR?这条命令,把固件升级到最新版本就好了。这个不影响后面的操作,所以就不用在意这个了。
5 Z* t. k/ ]  [  E
4 P+ R# @# P: R

0 \, r2 Z- S; }$ t; r  E测试一下ifconfig和ping命令,都是正常的。
) q6 N5 u; o2 ]7 \# J8 R
27.png
终端
在RT-Thread Studio中配置ESP8266模块联网,整个流程只写了3行代码,可以说是非常的快速方便。
/ @9 Z$ q: j0 H; T' c* N  j. A, S& ]0 t: M2 w0 ]( Y

% i% F) Z" j' n  A; g) v5.疫情数据的获取
9 m2 F4 h4 L% E' w4 B+ f$ sWiFi模块连接上互联网之后,就可以连接GET疫情数据的API接口http://lab.isaaclin.cn/nCoV/api/overall,然后读取返回的疫情数据。在上一篇的裸机工程中,是通过先和服务器建立SSL连接,然后发送GET HTTPS请求,获取到的返回数据,那RT-Thread有没有这样功能的软件包呢?这里就需要添加另一个软件包webclient。' P# R# t3 T" ~

( Y  C- A4 ^0 j5 a% m

' U! L& t% `4 U1 H# A% K. kWebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。, i3 Z( A: ]1 T( X# v4 p8 ~6 D/ y
WebClient 软件包功能特点如下:' b3 n# P& z8 u( V9 B5 S  t# y+ }1 r

( i% h3 V- w1 @' ]% \

4 t5 Q: W0 \% x( v8 X·支持 IPV4/IPV6 地址;
; q, i1 L3 G& [9 q" G( p3 c·支持 GET/POST 请求方法;% k# P! {: G' R, {$ v0 z. E
·支持文件的上传和下载功能;( t7 ]; E* N, b" g0 p  P) z
·支持 HTTPS 加密传输;( j0 i: r! U0 A% @$ X1 M+ \# }
·完善的头部数据添加和处理方式。# C( d8 g7 {$ R8 x7 G: A

0 ~$ t$ b" ^; S5 |) r
6 o( s4 u3 P2 v* l: ?3 M
和添加at_device一样,在软件包中心中搜索webclient,0 J' s! c! W$ ?7 _. a7 r, C
28.png
软件包
然后添加到当前工程,右键进行配置,由于我们的http://lab.isaaclin.cn/nCoV/api/overall这个疫情数据接口是HTTPS类型的,根据软件包使用手册,我们需要选择TLS模式中的 MbedTLS。勾选添加GET和POST示例。
3 Y) a+ p, v* p" y% }
29.png
软件包配置
保存配置,看一下当前已经添加了哪些功能,可以看到有一些组件我们并没有去打开,但是已经被开启了,这是因为有些软件包是会依赖一些组件的,当使能软件包时,一些依赖的组件也被同时使能。" i2 F- c6 `9 Z( o
30.png
软件包
Ctrl + B编译,Ctrl + Alt + D下载运行。在终端输入web_get_test测试GET请求功能。& R  y( k: d8 u# e  p9 X5 a, V
31.png
GET示例
可以看到,执行get命令之后,会返回一个字符串,那么GET的是哪个地址呢?打开packages->webclient-v2.1.2->samples->webclient_get_sample.c文件,% t" d# P4 x: R9 p% E
32.png
示例代码
可以看到GET的是这个地址:http://www.rt-thread.com/service/rt-thread.txt,我们用电脑上的浏览器访问一下:
  v  Q$ T& y" v; S2 Q6 r
33.png
浏览器访问
经过实际测试发现,GET HTTPS请求,还需要使能软件模拟RTC这个组件,否则会报assertion failed at function:gettimeofday, line number:19错误。. Z: W( S6 W9 E* k7 J0 t% v- d
34.png
使能RTC
我们重新写一个获取疫情数据的函数,并导出到MSH。6 R) w' A  [+ g- J  f) U8 B

: e, ?$ O; T: \% f0 @! R! m  n) R3 K
4 l4 |! H  M& U! Q* R% X; i
usr_ncov.c文件内容! i! S6 a- O. }/ m
  1. //usr_ncov.c
    5 }' n4 x5 m! W* G
  2. #include "usr_ncov.h"
    : H) _: ?6 t/ `4 Z" }6 x( O: |

  3. $ x& ~1 m. n* _
  4. int get_NCOV_Data(void)
    ; x( v% r  |! m5 N- a9 L; k
  5. {3 C7 M; H) p' ~7 g6 h3 N
  6.     char *uri = RT_NULL;
    " ?4 g7 }( d" J: B' y: i
  7.     struct webclient_session* session = RT_NULL;
    ( T$ b/ `4 ?- D1 a' f, b6 O( r- p
  8.     uint8_t *buffer = RT_NULL;( B9 x! @! D8 w, V/ m2 Q1 x
  9.     int index, ret = 0;4 F1 ~2 Z3 X7 J6 c; d+ J
  10.     int bytes_read, resp_status;
    * |7 I) Y3 Q+ V5 _; z( A8 F% _
  11.     int content_length = -1;, H& d. V5 ]% T$ c% J/ n
  12.     int buffer_size = 1600;
    " H) u3 u  Q) V* R: b
  13.     uri = web_strdup(API_NCOV);) ^( h5 d3 \) h, s; h
  14.     rt_kprintf("start get api: %s\r\n", API_NCOV);
    % x3 n" W: [! m6 J; v
  15.     if(uri != RT_NULL). a! V3 C4 U, O# M7 u, K* i
  16.     {8 d2 Z: Z0 e/ x
  17.         buffer = (unsigned char *) web_malloc(buffer_size);
    / t" a# p# T  {6 ?2 H
  18.         if (buffer == RT_NULL), g+ J8 `2 M5 R4 d2 g0 w& F! q6 J
  19.         {
    6 A6 K& n2 {2 d; y
  20.             rt_kprintf("no memory for receive buffer.\n");
    0 ]. ^8 v9 c' @: n
  21.             ret = -RT_ENOMEM;
    4 U( d! a2 N: B4 O8 x8 o' }* |$ J
  22.             goto __exit;
    , D& V8 w+ }9 ]/ z# X$ C
  23.         }
    ; i+ W" ?& v, x

  24. 7 F. t: \% y7 S1 l/ s7 N
  25.         /* create webclient session and set header response size */
    ( S/ y% }( ~  I
  26.         session = webclient_session_create(buffer_size);
    ; ~& K; y! u6 d5 y( o% c, m
  27.         if (session == RT_NULL)4 v7 ^( M7 G* h4 D6 B2 T6 t
  28.         {6 g' P9 S9 E' L/ V& d
  29.             ret = -RT_ENOMEM;
    3 N9 `" A& I$ v1 Q6 L" T8 `$ V
  30.             goto __exit;
    . @( y5 y' y0 a% o6 N3 H
  31.         }
      f7 M( I' a( e( R

  32. " N. b- U; }( s7 C) z5 ~
  33.         /* send GET request by default header */3 L, h8 H+ b" W) j( a" c
  34.         if ((resp_status = webclient_get(session, uri)) != 200)2 ~  [* Y. W: o' ^$ X
  35.         {
    : T- Q$ v) z5 m
  36.             rt_kprintf("webclient GET request failed, response(%d) error.\n", resp_status);. \7 B8 E5 @! t7 Q* o
  37.             ret = -RT_ERROR;
    8 t. v7 D( e. A( N- c
  38.             goto __exit;
    7 R- r: V; ]" [) r) ^$ c' g7 y+ a
  39.         }
    : B; m; f! ~! E- g! p0 X
  40. : S* ]! D! u% r' [- O) ?6 m! U! l
  41.         rt_kprintf("webclient get response data: \n");
      `/ ^# Z$ v7 F0 l- Y

  42. 8 V0 D' U; a+ i1 @* S3 i$ o' N3 n! {
  43.         content_length = webclient_content_length_get(session);
    6 d- q6 H+ [: f) G" P
  44.         if (content_length < 0)/ b/ f+ _/ M, O& d0 u! F8 F
  45.         {
    0 u+ `1 }1 H* o2 P+ Q
  46.             rt_kprintf("webclient GET request type is chunked.\n");
    , ]' @0 S9 A& E
  47. # a  q! H- U0 y# h# \) M2 [6 V- \
  48.             do3 N! K# w- M* `8 K8 ^  Q: ?# I
  49.             {1 P5 Y  g7 p* O: A
  50.                 bytes_read = webclient_read(session, buffer, buffer_size);+ s8 {. w* G! O( l
  51.                 if (bytes_read <= 0)4 ?/ x$ v$ R. x0 z
  52.                     break;2 J+ T4 ]" V  h8 g$ |8 ]
  53. # f- L" R+ V) D, E$ r
  54.                 for (index = 0; index < bytes_read; index++)& ?9 C4 j, N9 _$ N+ k
  55.                 {" R, y& r# V! X* }' R# c; }
  56.                     rt_kprintf("%c", buffer[index]);+ h; @7 \: Q7 E2 X# }
  57.                 }) D" X; J' s( t& P
  58.             } while (1);
    " z. Q# W) i1 Z/ b4 Y) I6 V3 N: V
  59. , s% J$ B" ~: T
  60.             rt_kprintf("\n");
    ; Y7 a$ d  J. K! Y8 D1 ]# J8 h' K
  61.         }
    # w$ X1 r4 @8 i6 `( U5 @
  62.         else4 b8 g; w- n* @
  63.         {: D+ d$ P0 ]0 @( \1 q- G
  64.             /* 读取服务器响应的数据 *// v1 y3 D- P5 ?
  65.             bytes_read = webclient_read(session, buffer, content_length);; I- `4 \" C# C% D2 V5 Y
  66.             rt_kprintf("data length:%d\n", bytes_read);
    : _  S6 ~  E! V$ D; s( V7 N
  67. ) |7 m  W' `! q( T5 y; {* g2 e! U) m9 v; g
  68.             buffer[bytes_read] = '\0';
    $ r. X2 `& e- B" O2 ~- \
  69.             rt_kprintf("\n\n %s \n\n", buffer);
    8 I6 E1 {/ C4 K5 `9 @
  70. //            rt_kprintf("parse data\r\n");
    8 |' b9 e: |3 h+ o
  71.             // parseData(buffer);        //解析函数1 ]8 ~8 O! c' h* {
  72.             rt_kprintf("\n");5 z( X6 A( }, B
  73.         }/ v( N. g: t) j

  74. ! E$ l1 o: O/ U- n
  75.         __exit:
    2 H( Z5 S+ h& X9 W6 d
  76.         if (session)
    ) H# `9 G7 }3 o; c% N
  77.             webclient_close(session);4 K) l; F. r7 c; `

  78. ( L9 n( p# X% C$ n. q
  79.         if (buffer): h) R2 z+ w$ C6 B! ?1 O) G
  80.             web_free(buffer);
    6 ~/ {& {! j: y- w* |6 l& G# e
  81.     }5 n0 G5 n  K1 p7 U* u
  82.     else
    , F0 `: y* ]2 [* O8 N
  83.         rt_kprintf("api error: %s\n", API_NCOV);# c( T( M' _6 ^) _+ X8 v
  84. % A8 {( d( V$ z2 P: e
  85.     return ret;( y1 |" F8 _/ k  D: I2 ~, _
  86. }- u, [  ]: v9 c6 P% P
  87. MSH_CMD_EXPORT(get_NCOV_Data, get api ncov);
复制代码
usr_ncov.h文件内容( i8 a8 S/ S4 @' I, Z1 ?
  1. #ifndef APPLICATIONS_USR_NCOV_H_
    $ u' ?& O+ N4 C* y+ u
  2. #define APPLICATIONS_USR_NCOV_H_5 G  R8 h8 H7 a1 b; Y" S
  3. #include <webclient.h>8 u% s" l! ?8 r
  4. #include <rtdevice.h>
    4 n  B  ~: l6 P
  5. #include <rtthread.h>
    7 f; u& T1 _* S
  6. #define API_NCOV     "http://lab.isaaclin.cn/nCoV/api/overall"
    4 B  ^# @- X( u: B" i  u$ @% P0 `/ a! R
  7. int get_NCOV_Data(void);) |9 b. k0 w4 P
  8. #endif /* APPLICATIONS_USR_NCOV_H_
复制代码
重新编译,下载,运行。在终端运行这个命令:
( u. x6 P! x1 J1 w' `) m
35.png
命令获取疫情数据
可以看到获取到了返回的数据,长度1366个字节。下一步就是对这个JSON数据进行解析,获取到我们想要的疫情数据。' [5 l4 j# j9 r; Y- T4 b

% Q3 k9 }; o8 A% }6 g6 _0 v
/ X; |1 g# l; c
6.疫情数据的解析  w. q/ r  [% C
API返回的数据是JSON格式的,关于JSON的介绍和解析,可以查看使用cJSON库解析和构建JSON字符串。数据的解析使用的开源小巧的cJSON解析库,我们可以在软件包管理中心直接添加:# ]" G5 Q/ s2 }. A. r
36.png
添加cJSON
在进行解析之前,先来分析一下JSON原始数据的格式:results键的值是一个数组,数组只有一个JSON对象,获取这个对象对应键的值可以获取到国内现存和新增确诊人数、累计和新增死亡人数,累计和新增治愈人数等数据。8 t* Z2 Y4 a1 U4 L9 X7 A# D
. N/ c' }1 e- x7 Q- C. u  T1 w5 g

) o- Q) X; R' l& ?2 D( q$ }9 D全球疫情数据保存在globalStatistics键里,它的值是一个JSON对象,对象仅包含简单的键值对,这些键的值,就是全球疫情数据,其中updateTime键的值是更新时间,这是毫秒级UNIX时间戳,可以转换为标准北京时间。
, [5 v) T5 U. n4 L$ R
  1. {9 w- S  x5 ^- A. J4 k& V
  2.     "results": [{
    , C3 Q2 G0 @' n% L+ [! K/ ]
  3.         "currentConfirmedCount": 509,
    # k0 E9 K" `3 D8 d: Q
  4.         "currentConfirmedIncr": 16,/ Q" }! g  Y% q" f+ W2 z4 u
  5.         "confirmedCount": 85172,
    $ m! V; g8 U& y3 K& e9 ?! d( k& r
  6.         "confirmedIncr": 24,
    ' P! g  v: j, Z+ k
  7.         "suspectedCount": 1899,
    - f3 t1 `" k2 w
  8.         "suspectedIncr": 4," o2 D) V8 y5 @2 x* [& q
  9.         "curedCount": 80015,
    ! e6 m% A1 N8 E1 R" G- A
  10.         "curedIncr": 8,9 W  l( P4 `  B2 e1 T* E' O
  11.         "deadCount": 4648,
    0 P9 k8 \" ]9 E1 k4 c
  12.         "deadIncr": 0,' \4 |& p4 A3 e4 ~3 h& q( L
  13.         "seriousCount": 106,
    ' O4 g6 |5 h3 `: M( ?. x
  14.         "seriousIncr": 9,6 W3 g( h% L4 L5 M* }0 s
  15.         "globalStatistics": {5 z& W1 N" M. S3 S
  16.             "currentConfirmedCount": 4589839,2 ^- o. d3 n1 C1 H
  17.             "confirmedCount": 9746927,
    ! i7 V( j' n. w2 M4 i7 \: v
  18.             "curedCount": 4663778,
    8 S1 d  C1 H9 f4 Z' b
  19.             "deadCount": 493310,1 B4 Z3 T3 c4 A8 p
  20.             "currentConfirmedIncr": 281,- o: ]- o& E1 Z5 k9 `0 `8 s
  21.             "confirmedIncr": 711,7 k5 \6 t0 J( c1 h% @7 H6 m, S' H
  22.             "curedIncr": 424,7 A6 s; y4 |: z
  23.             "deadIncr": 6% ~5 E6 e' h1 b+ K
  24.         },
    + x  L. b+ X/ u2 k" |
  25.         "updateTime": 1593227489355: e( m! m* y4 p2 G6 G4 {, F; H# R
  26.     }],
    ; v4 E/ ^+ `* z+ R$ H
  27.     "success": true2 v% @- g% k) h
  28. }
复制代码
先定义了结构体NCOV_DATA,用于存储国内和全球疫情数据:8 B5 a9 O6 M) Z& `" ?
  1. struct NCOV_DATA{/ z" x0 _1 S- Q9 @/ |2 t3 {
  2.     int currentConfirmedCount;6 p' Z1 f. x' R: O# S/ M
  3.     int currentConfirmedIncr;' H/ [* r9 L, X8 \
  4.     int confirmedCount;* q( x; ~) F% D  |: a9 J
  5.     int confirmedIncr;
    . I1 m' B& b8 P- u5 g! T7 J5 `
  6.     int curedCount;
    3 W& I1 ], F6 e8 }7 e. }: m
  7.     int curedIncr;
    ; W8 o! J- u' _! _0 [4 v
  8.     int deadCount;
    4 x3 o. u! Z4 s3 \# N
  9.     int deadIncr;
    1 K: c2 b4 ?9 L. G2 ?9 j" r$ @# m7 G
  10.     int seriousCount;
    - u9 b. Q5 Y3 ^9 z" ~6 {. ^" k
  11.     int seriousIncr;
    7 N# `! h' Q5 }0 w) y6 o$ [7 ?7 u

  12. 7 p8 J. N! r& D/ R  p6 V2 M3 R! N5 k
  13.     char updateTime[20];! g7 K- h3 o$ X' Y# p
  14. };
复制代码
对应的解析函数:9 p% M, h; U( y) a- T3 v
  1. #include <cJSON.h>
    + a( [8 I5 @1 s# L3 S

  2. 8 W; d, b7 m2 U9 z* m3 z
  3. struct NCOV_DATA dataChina = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "06-13 16:22"};;0 u* K. G1 T& M- X8 a; `
  4. struct NCOV_DATA dataGlobal = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
    8 i1 X, p* B5 J$ ]

  5. 6 i' s6 Q4 H7 _. [
  6. int parseData(uint8_t *str)1 _& o; s9 [5 i7 }
  7. {
    8 c$ G3 G( V* t7 P5 p
  8.     int ret = 0;  Q, n0 ~3 r' d$ u+ {7 H
  9.     cJSON *root, *result_arr;4 n, Z8 z, \5 T2 U7 f# L: c- r" F
  10.     cJSON *result, *global;
    ; U6 }' i3 g% Z: ^/ r) A" x4 h
  11.     time_t updateTime;  F; A3 l* L: ^9 a1 V
  12.     struct tm *time;
    % o! n! ]0 [7 x- @, d( ?5 Y3 ~3 q

  13. / P% u2 ]5 B( k- p3 U
  14.     root = cJSON_Parse((const char *)str);   //创建JSON解析对象,返回JSON格式是否正确# ^9 Z& ^5 ?# U8 {3 s! E
  15. 8 r# S7 ^. V) e8 ]$ I
  16.     if (root != 0)/ O* w; z, n- u! X, i5 d. n
  17.     {
    / R* D; L% m0 l6 c2 {
  18.         rt_kprintf("JSON format ok, start parse!!!\n");2 N0 K8 X, `& p$ i9 M; [
  19.         result_arr = cJSON_GetObjectItem(root, "results");' _- U, X- l# z8 }
  20.         if(result_arr->type == cJSON_Array)
    - g  O9 m/ `. N
  21.         {, V, f! Y5 Q; c+ n7 p0 Q
  22. //            rt_kprintf("result is array\n");7 Y1 i1 I) P0 O0 i; l; v! F% B
  23.             result = cJSON_GetArrayItem(result_arr, 0);
    ( z2 s3 r4 _/ R8 v
  24.             if(result->type == cJSON_Object)# y' `' n' y& q- ^* ^  G4 s6 T  ^4 ?: u" h
  25.             {) Z6 F3 T7 y6 F! X: S) [; \, f  z
  26. //                rt_kprintf("result_arr[0] is object\n");
    ( ~( H8 e0 I- u; e% {- D9 @
  27. 2 X/ J( U. c5 f0 t4 V7 r% g
  28.                 /* china data parse */
    % p& ^# F% r1 u2 n- b
  29.                 dataChina.currentConfirmedCount = cJSON_GetObjectItem(result, "currentConfirmedCount")->valueint;
    9 s+ C4 P3 l& `! N! s
  30.                 dataChina.currentConfirmedIncr = cJSON_GetObjectItem(result, "currentConfirmedIncr")->valueint;
    1 O5 J2 L7 v" T) ^+ ^5 \; X
  31.                 dataChina.confirmedCount = cJSON_GetObjectItem(result, "confirmedCount")->valueint;1 ?1 T1 M6 Y( M) B1 `2 b6 r
  32.                 dataChina.confirmedIncr = cJSON_GetObjectItem(result, "confirmedIncr")->valueint;7 t2 Q+ n9 W1 K2 s4 U
  33.                 dataChina.curedCount = cJSON_GetObjectItem(result, "curedCount")->valueint;
    # s: X/ |; @2 f! U
  34.                 dataChina.curedIncr = cJSON_GetObjectItem(result, "curedIncr")->valueint;
    * G8 Q7 `, J2 f" m5 Y; E
  35.                 dataChina.deadCount = cJSON_GetObjectItem(result, "deadCount")->valueint;
    0 ?+ u% d: u% T  u7 w
  36.                 dataChina.deadIncr = cJSON_GetObjectItem(result, "deadIncr")->valueint;
    ; I! d" ~0 N; r+ g0 {% R* R
  37. ; m! ]" }1 [& T1 R$ u- m, C
  38.                 rt_kprintf("**********china ncov data**********\n");
    9 L3 D0 R0 @, H" R0 B
  39.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataChina.currentConfirmedCount, "currentConfirmedIncr", dataChina.currentConfirmedIncr);
    ) N7 {* `2 u: V* b
  40.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataChina.confirmedCount, "confirmedIncr", dataChina.confirmedIncr);" T( H- n4 z0 H! ^
  41.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataChina.curedCount, "curedIncr", dataChina.curedIncr);/ {, H% E/ p; `" z, P. i
  42.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataChina.deadCount, "deadIncr", dataChina.deadIncr);: o+ b0 f8 Y2 B5 M3 y: w

  43. 8 d7 U+ n, v- F6 R
  44.                 /* global data parse */4 K! y4 Y4 I; \* N0 ?
  45.                 global = cJSON_GetObjectItem(result, "globalStatistics");6 u8 T: I8 w8 C0 c( u7 e4 w
  46.                 if(global->type == cJSON_Object)
    . b, t# i$ G0 k8 U' o- X
  47.                 {5 ^( L; ^+ o. @$ n  t, O
  48.                     dataGlobal.currentConfirmedCount = cJSON_GetObjectItem(global, "currentConfirmedCount")->valueint;
    # J1 I5 G, X* p6 I4 D& O
  49.                     dataGlobal.currentConfirmedIncr = cJSON_GetObjectItem(global, "currentConfirmedIncr")->valueint;! L( w: b9 K6 Y. A) T, U
  50.                     dataGlobal.confirmedCount = cJSON_GetObjectItem(global, "confirmedCount")->valueint;
    9 ?+ @( A0 V. V. L2 P' F/ C
  51.                     dataGlobal.confirmedIncr = cJSON_GetObjectItem(global, "confirmedIncr")->valueint;
    ; x( t6 l9 F0 h7 ^" j' R" ]! u
  52.                     dataGlobal.curedCount = cJSON_GetObjectItem(global, "curedCount")->valueint;
    2 z% X( B5 c4 |( G8 e
  53.                     dataGlobal.curedIncr = cJSON_GetObjectItem(global, "curedIncr")->valueint;
    ) g8 }) [% s4 }/ _9 p( y% P
  54.                     dataGlobal.deadCount = cJSON_GetObjectItem(global, "deadCount")->valueint;
    ! A, n! y/ a# H% |' @( X
  55.                     dataGlobal.deadIncr = cJSON_GetObjectItem(global, "deadIncr")->valueint;
    % c) F; C9 R: ^1 k* A: S
  56. , {* O1 |) E- o/ Z7 S# x& |4 t9 l0 }
  57.                     rt_kprintf("\n**********global ncov data**********\n");
    ) L+ N6 F& W' V0 t# O2 ?- D* ]
  58.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataGlobal.currentConfirmedCount, "currentConfirmedIncr", dataGlobal.currentConfirmedIncr);
    ! M9 E6 n- B. _* c3 L
  59.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataGlobal.confirmedCount, "confirmedIncr", dataGlobal.confirmedIncr);
    8 U1 f9 O# T! F9 {- C4 r' u
  60.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataGlobal.curedCount, "curedIncr", dataGlobal.curedIncr);
    5 E3 }, y8 \5 H. A5 z
  61.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataGlobal.deadCount, "deadIncr", dataGlobal.deadIncr);# ]2 R& T9 S1 c: o) ?* W
  62. 6 j% b+ S; c! ]& t. s
  63.                 } else return 1;/ \+ H) g9 r0 n3 O9 W- y' {

  64. + g6 S. L7 L5 Y4 M2 p
  65.                 /* 毫秒级时间戳转字符串 */  m% V9 |5 B) }) A, R# x/ Q
  66.                 updateTime = (time_t )(cJSON_GetObjectItem(result, "updateTime")->valuedouble / 1000);
    , p. j2 s2 S" j3 P4 `1 x
  67.                 updateTime += 8 * 60 * 60; /* UTC8校正 */
    7 h6 _4 j7 r' x0 K" K. m4 p, L
  68.                 time = localtime(&updateTime);
    8 l: J0 Y; m# M8 C
  69.                 /* 格式化时间 */8 N1 o+ q8 t+ n# c  i
  70.                 strftime(dataChina.updateTime, 20, "%m-%d %H:%M", time);0 \6 C% B. K5 C" n) R9 _3 E
  71.                 rt_kprintf("update: %s\r\n", dataChina.updateTime);/* 06-24 11:21 */$ u4 J: V. g, N7 x0 y
  72.                 //数据在LCD显示) i4 U: Q9 ?* U% ]3 g
  73.                 //gui_show_ncov_data(dataChina, dataGlobal);/ N1 ?5 {; J) N$ O8 T5 H% j
  74.             } else return 1;" k! `* A2 Y" R4 U9 W: g7 [0 W/ {
  75.         } else return 1;+ J; I- n( q5 Q$ ?4 _
  76.         rt_kprintf("\nparse complete \n");' O0 O; b+ ~% [8 b
  77.     }6 z# u: Q  p7 M
  78.     else
    9 z" f1 n4 y5 T: M" l, V
  79.     {) w1 |: ~* a0 c
  80.         rt_kprintf("JSON format error:%s\n", cJSON_GetErrorPtr()); //输出json格式错误信息
    + T. x5 H1 z: x- g
  81.         return 1;
    ' d$ b. x) d* X
  82.     }6 \: _9 L7 n" r
  83.     cJSON_Delete(root);
    7 _5 H* F- s: M
  84. 2 _2 r6 `8 b* N: J6 _
  85.     return ret;
    * t. g- o: E; I' D
  86. }
复制代码
在数据接收完成之后,对JSON数据进行解析。
/ N( Q9 P( ]0 l1 h; J
37.png
解析结果
7.疫情数据的显示
# `; E- g# h) m3 V2 u9 k! b数据解析出来之后,剩下的就简单了,把上一篇文章中9341的驱动文件移植过来就好了。+ b! \/ f  K, d$ n% e
: i2 [9 W. M7 C' M$ ^6 h7 a
. Z2 w1 `! T1 n  }# a: }$ ?/ o: ]
液晶屏使用的是3.2寸 LCD,IL9341驱动芯片,320*240分辨率,16位并口。由于屏幕分辨率比较低,可显示的内容有限,所以只是显示了最基本的几个疫情数据。为了减小程序大小,GUI只实现了基本的画点,画线函数,字符的显示,采用的是部分字符取模,只对程序中用到的汉字和字符进行取模。为了增强可移植性,程序中并没有使用外置SPI Flash存储整个字库。8 I+ k! r0 ]7 N5 I2 L% {
7 }+ g' w/ a1 i/ S
2 h; k8 ~% Q" L6 i. E
由于RT-Thread Studio使用的HAL库,所以LCD的GPIO初始化函数需要修改一下:
5 b6 s# ]( w- M% ^- r1 ~
  1. void lcd_gpio_init(void)# x, D, Z  D# {- M4 _0 r6 n+ e: M
  2. {3 i+ J* X  m; _! O! a
  3.     GPIO_InitTypeDef GPIO_InitStructure;
    7 W" G! x  P" p- B( N& q# c9 a
  4. 8 v7 h6 e, K7 d  T
  5.     __HAL_RCC_GPIOA_CLK_ENABLE();0 t/ Q" C- p$ R
  6.     __HAL_RCC_GPIOC_CLK_ENABLE();; V9 y9 X; L7 Y8 @
  7.     __HAL_RCC_GPIOB_CLK_ENABLE();6 P4 U% O' U& [
  8.     __HAL_RCC_AFIO_CLK_ENABLE();% k; s" d9 }% \0 `8 U
  9.     __HAL_AFIO_REMAP_SWJ_NOJTAG();/ b/ G8 m  K* R
  10. 0 b* N. m6 D2 W% m; ]; t* P
  11.     GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;1 O4 S0 J* U, A  F. E9 S8 _5 Z
  12.     GPIO_InitStructure.Pull = GPIO_PULLUP;0 j1 _/ {1 U: F
  13.     GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;* S: \  A0 E# |  T
  14.     GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6;
    . Y4 Z4 |6 x( K  U- w2 ]# n
  15.     HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOC0 f& H$ \+ t0 C

  16. # u/ t% k) O( i# m$ S
  17.     GPIO_InitStructure.Pin = GPIO_PIN_8;    //背光引脚PA8# {. ^* S8 r, g
  18.     HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); //GPIOC
    ! w! L0 U( ^; B; U
  19. " U' p1 }3 K; l
  20.     GPIO_InitStructure.Pin = GPIO_PIN_All;
    / ^( t  ^* F6 c3 N8 a( H1 M
  21.     HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);4 d; p0 i2 x: E% H5 u- b
  22. ; A5 J" o" o+ K7 \$ w' X
  23.     HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6, GPIO_PIN_SET);/ w5 C2 j, B9 C  W
  24.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);: A+ _' e" p- C4 \
  25.     HAL_GPIO_WritePin(GPIOB, GPIO_PIN_All, GPIO_PIN_SET);
    # b! C+ `" V4 T" P" h& m) m
  26. }
复制代码
延时函数换成:
$ x% _! g6 Z, d4 D  p: ^
  1. rt_thread_mdelay(nms);
复制代码
还有一点,在Keil中,文字编码选择GBK编码,1个汉字占用2个字节,而RT-Thread Studio为UTF-8编码,1个汉字占用3个字节,汉字显示函数需要调整:
3 k- H# H& b7 `$ j" C" f
  1. void gui_show_chn(uint16_t x0, uint16_t y0, char *chn)
    # ]$ E+ Z2 g4 u  e
  2. {9 D0 K  K( J1 y: m% X; w! R4 a
  3.     uint8_t idx = 0;' g0 R3 k/ {  r, I  S. n& \' `
  4.     uint8_t* code[3]; //UTF-8:国=E59BBD9 g1 k* d6 n( t1 Z+ x1 t

  5. 8 k7 s  y" C! p) _7 L. J2 B( k
  6.     uint8_t size = sizeof(FONT_16X16_TABLE) / sizeof(FONT_16X16_TABLE[0]);
    + O& x% v2 [6 }: W7 N( X7 p$ e# D8 s& b
  7.     /* 遍历汉字,获取索引 */) S  o7 m% x# J/ ^# H6 w* q  W; G; A
  8.     for(idx = 0; idx < size; idx++)! R! A* F1 x$ d+ ]! H( f
  9.     {& C& H5 r" L8 d( \$ c) G2 A2 u7 |# t
  10.         code[0] = FONT_16X16_TABLE[idx].chn;4 O: B: a; _: v4 \! ^
  11.         code[1] = FONT_16X16_TABLE[idx].chn + 1;
      L& I1 A& Q* J+ \9 g. e  k, V" X
  12.         code[2] = FONT_16X16_TABLE[idx].chn + 2;
    : s2 M- p, k  u1 f) M$ n
  13.         //汉字内码一致  C- T3 H& i- m" B' n' y
  14.         if(!(strcmp(code[0], chn) || strcmp(code[1], chn+1) || strcmp(code[2], chn+2)))
      f) h6 k4 k7 l$ ~
  15.         {
    5 n9 O1 p$ b7 w; I% s& V5 G
  16.             gui_show_F16X16_Char(x0, y0, idx, WHITE);- J/ J0 Q! ?+ A, C
  17.             return;
    2 a! o. O% A+ @5 R  {
  18. //            break;
    2 G" Y5 m) Z) v; d) m$ z
  19.         }8 w( K# {' O, Y. m
  20.     }
    . v. D% {2 ], y9 m# ^9 m' q
  21. }
复制代码
疫情数据显示函数:3 ]$ j& x0 s" j9 c0 D
  1. void gui_show_ncov_data(struct NCOV_DATA china, struct NCOV_DATA global)! P$ d- D! w6 ?& X7 E0 a
  2. {
    " ?( K& p- Y1 u* u
  3.     uint8_t y0 = 20;+ F% y! n' f7 Q4 P9 O' Y8 L

  4. 1 d# k$ }' F% i1 ^5 \2 C0 B
  5.     lcd_clear(BLACK);
    1 c( j" j& M+ s; M4 ^5 G
  6.     gui_show_bar();' ^; J  @: E; H+ v  ^. F

  7. ' J) K) Z8 d! E2 E) Z# I5 v2 r
  8.     gui_drawLine(0, 18, 320, DIR_X, WHITE);
    6 g- ]/ s; K  \0 V" V
  9.     gui_drawLine(0, 38, 320, DIR_X, WHITE);
    * E( o: W4 e+ V' C* v( g
  10.     gui_drawLine(0, 138, 320, DIR_X, WHITE);
    ' H, T: d8 J" @0 Q! Q
  11.     gui_drawLine(0, 158, 320, DIR_X, WHITE);7 M6 R4 R/ w( R2 [* i' [
  12.     gui_drawLine(0, 220, 320, DIR_X, WHITE);) u& Q4 M. s4 l$ Y
  13. % o9 v. c. k' t8 ~
  14.     /* "国内疫情" */
    % c: [* Z# z- w# [8 U/ y) h. m
  15.     gui_show_chn_string(128, y0, "国内疫情");
    ! }# X3 ?6 e% a- _3 h# U
  16.     gui_show_line_data(40, "现存确诊:", china.currentConfirmedCount, "较昨日:", china.currentConfirmedIncr);
    2 Z3 L# l3 o# |* |
  17.     gui_show_line_data(60, "累计确诊:", china.confirmedCount, "较昨日:", china.confirmedIncr);6 o) }$ ]1 q+ K$ R; W2 w- A
  18.     gui_show_line_data(80, "累计治愈:", china.curedCount, "较昨日:", china.curedIncr);
    9 _; B6 Q) U$ w4 J7 J
  19.     gui_show_line_data(100, "现存重症:", china.seriousCount, "较昨日:", china.seriousIncr);2 J$ B3 [! U. W2 W, c! d% M: T
  20.     gui_show_line_data(120, "累计死亡:", china.deadCount, "较昨日:", china.deadIncr);
    / [( m' S* ]% H. v2 C: Q
  21. 0 J" ^0 q  d2 J* U) r) z
  22.     /* 全球疫情 */
    ) w9 h) Q6 x& Q9 ~
  23.     gui_show_chn_string(128, 140, "全球疫情");
    ; m: q$ v2 h+ D& e: x/ }6 z
  24.     gui_show_line_data(160, "现存确诊:", global.currentConfirmedCount, "较昨日:", global.currentConfirmedIncr);7 Q  y# I) a$ P5 P: g
  25.     gui_show_line_data(180, "累计治愈:", global.curedCount, "较昨日:", global.curedIncr);$ H+ ?8 E1 I; l4 {
  26.     gui_show_line_data(200, "累计死亡:", global.deadCount, "较昨日:", global.deadIncr);$ U# @/ k8 r6 e& X2 X+ Q

  27. 1 E: K9 b1 ]/ @# _% ?( o$ z% Z
  28.     gui_show_chn_string(160, 222, "更新于:");
    4 ^$ S4 w+ \9 b, T; c) u
  29.     gui_show_F8X16_String(230, 222, china.updateTime, GREEN);
复制代码
最终显示效果0 V9 U& q; x; I7 t
38.png
最终效果
开源地址
$ @9 L% w- {% H! a1 x代码已经开源,地址在文末,欢迎大家参与,丰富这个小项目的功能!
' z, X% J& C$ u$ g7 Q4 C$ Y" R( U1 p  g- {2 C% k+ z
" V! _  p, U( s/ D* g7 I6 R
基于STM32+RT-Thread的疫情监控平台; m0 I3 o2 q- \3 K
http://github.com/whik/rtt_2019_ncov8 U: ]. ~9 o9 l( B; g  O

4 Y2 R! O5 d+ F1 p6 V% L2 x

. }! ]. l+ S- l+ |/ F基于STM32F103的疫情监控平台(裸机版)
5 C; |2 A% l4 S* @http://github.com/whik/stm32_2019_ncov
2 S' o: w% |( b$ A/ J/ B
) l6 C8 y' j9 ]5 U+ E  V
' E% N# v1 c1 u* i2 M% Z/ I* J! w

/ G  F! e4 Y  _文章出处:[color=var(--weui-LINK)][url=]RTThread物联网操作系统[/url]
1 |9 P0 L/ W9 B$ ]: O' Y6 Z8 T/ I" w! {! t3 w6 e$ T( O2 z  H" \
收藏 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 手机版