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

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

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

+ z' P5 h# U2 Q+ |+ W2 T( j

, g' @3 M5 S  H" P上周末加班,这周末休息,有时间整理一篇之前做的基于RT-Thread的疫情监控平台。上一篇文章我们使用STM32F103 MCU裸机开发的方式实现了疫情监控平台。这次我们玩点高端的,使用RT-Thread Studio来实现同样的功能,一起来看看吧!, w* B1 d! Q7 ^: ~2 H% B. n3 @
7 \5 `  j) f' `3 I6 S3 k7 N. `
, r/ b- L, D3 I1 z% a, g. K
·文章目录
3 S6 ^% I0 |5 O3 |( y2 f& k) T·使用到的软件包: C: B; }* z5 q7 \' `9 Q
·0.RT-Thread Studio的下载和安装
  X6 b3 M, S8 d& _
2 b$ \; J! k" `( U0 S* J
3 e- ]2 Q. Z0 I
1.硬件准备
# S/ x6 R8 {# \( |- W( d4 S+ K2.新建工程
& I" p5 K( N' e' E3.添加LED闪烁功能
* W( J+ S- S0 E' i4 J! P4.添加ESP8266软件包
, V# Y8 R% y' g5.疫情数据的获取5 `$ B- y' b0 w) r
6.疫情数据的解析8 f4 G+ z* N, D2 L, v9 v* r* {3 _
7.疫情数据的显示
5 `4 {6 e% P' i! r  A& o. u7 B3 o开源地址
' I9 `6 [# c3 d) X* f- d' y# J1 D) Q# O4 C6 t

% Z# W, d: [5 [, ?9 C9 U6 v6 Z最终的显示效果:% a8 f/ C5 |: W5 i
1.png
显示效果
有效文件就这9个,其他的就全是图形化配置:
% Q) r( q/ k" l* B0 q+ U4 J
2.png
有效文件
整个流程下来,如果顺利的话,可以在2个小时内完成。  @3 l- q8 p# m. ^1 X  i
0 @! i4 _1 b1 k! ?5 r
使用到的软件包4 b  \2 J( A  h: N/ p7 f: ]/ P
·at device:用于ESP8266配网0 I) @5 n6 y) a3 d
·webclient:用于发送HTTPS请求( {0 O: t# b/ ~1 T$ ]8 S; [. b% n
·mbdetls:用于HTTPS加密  i$ Q3 D1 L3 z. u. f7 c
·cJSON:用于JSON数据解析
3 R; z* h" V; n; @: A5 i9 }3 m0 n+ U) v+ ]. c7 Y- U
0.RT-Thread Studio的下载和安装3 r& k) N/ ~. I4 k8 ~! g7 r
一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效
3.png
RT-Thread Studio
·支持多种芯片,STM32全系列
0 T& B. A' C& ]) V3 n7 F9 H·支持创建裸机工程、RT-Thread Nano和Master工程' X8 `" {$ n- }; Q4 e6 e8 L
·强大的代码编辑功能,基于Eclipse框架
( J; x0 S8 i, c6 i·免费无版权限制,基于开源Eclipse和ARM-GCC编译器。" l2 y1 h6 z- z( N/ m& ^
·支持多种仿真器,J-Link,ST-link等,支持在线调试,变量观察。
: F$ z  x1 J8 K& |- Y2 K·SDK管理器,图形化配置RT-Thread软件包,同步RT-Thread最新版本。
$ c" I1 B( [3 |1 N" ]! ~4 ?·集成Putty串口终端工具( U. Y" s  Q* A
4 ?6 t) Q9 }# B# I% G; \

/ V9 e! J0 u: o8 g( g. \更多的使用教程:* R" v5 ?7 R; K# @( @
http://www.rt-thread.org/page/studio.html/ k! R  D+ }3 g; e  ^/ d
: E% K5 U9 e3 L4 F8 w

; }, S7 e" G) c0 B. D+ Q# b目前最新版本为1.1.3版本,支持3种下载方式,我们选择最后一个下载方式,从RT-Thread 官网服务器上下载。
: h3 o' p. u  G' g. F6 S7 r
, c# G& b3 {% N+ W

* n0 P3 p. m" b4 \/ \4 M4 Q; V! r下载地址:/ l: i! p* H; r7 S  F
: r5 M: _+ o  U( F: S
1 F  }, d" B1 f* C& G
http://117.143.63.254:9012/www/studio/download/RT-Thread%20Studio-v1.1.3-setup-x86_64_20200731-2100.exe
4 e; y1 |3 r4 U+ h6 }: z! q" p! t
2 i; @' J2 q# R2 I

- d2 x2 ~+ a6 I$ i# \8 T
4.png
下载链接
安装过程和常用的软件安装方法一样,选择安装路径,然后Next就行了。
/ @. t0 Z5 Y$ B* v# t- O
- c4 F0 Q* a6 g0 m# l% }; S

; C3 |1 n5 M% M1.硬件准备  a! D8 M9 o4 i: W+ P
开发板用的是我在大四时自己设计的STM32开发板——NiceDay,基于STM32F103RET主控。 这是我设计的第二块板子(第一块是毕业设计两轮平衡车主板),是在大四快毕业时,毕设实物和论文完成之后还有点时间,就设计了这款板子,最开始是准备做桌面天气时钟的。( K6 w! A: U* j" ~4 w0 V
; x5 Q' |/ F5 r

* A0 E9 o$ X$ b, z$ D0 A
5.png
开发板
2.新建工程
. c* c( T8 J, f' Z. nRT-Thread Studio支持创建裸机工程、包含RT-Thread Nano版本的工程和包含Master版本的工程。这里,我们选择创建RT-Thread 项目,即包含完整版RT-Thread的工程。# D' F8 e/ v% p6 _) O
6.png
新建项目
工程支持基于芯片创建工程,或者基于已有的BSP创建,这里使用的是我自己设计的开发板,所以选择基于芯片,选择芯片型号:STM32F103RE,调试串口选择串口1,调试器选择J-Link,SWD接口。, [* q; T. M/ l9 d5 Z
7.png
新建项目
创建完成之后,直接按Ctrl+B编译整个工程,第一次编译时间会长一点,如果修改很少,下次再进行编译就会很快了,可以看到无警告无错误。
0 u; e6 V  A3 X) n. d
8.png
编译结果
使用SWD接口连接JLink调试器和开发板,开发板上电,直接点击下载按钮,也可以使用快捷键Ctrl+Alt+D下载: L$ |# A0 X$ l* m; p7 `: m
9.png
下载程序
底部可以看到下载信息,从LOG来看,下载的程序文件是Bin文件,比较,擦除,编程,验证,复位整个流程耗时13s左右。$ `. N2 ~) B5 n/ o" x1 I4 A, O& x
10.png
下载LOG
RT-Thread Studio是自带Putty串口终端的,点击终端图标:& O, Q$ p- N) ]1 g
11.png
终端按钮
选择串口号、波特率、文字编码方式等。
7 @  w$ O3 p6 X& a3 _
12.png
配置终端
底部切换到终端窗口,可以看到串口终端输出信息:
" z: G! }3 b- ~' z1 N
13.png
串口终端
这样,不到5分钟,一个基于STM32F103RET6的工程模板就创建好了,包含RT-Thread完整版操作系统,整个过程不需要写一行代码,完全图形化配置。7 ]) r" M% X: }) x9 l2 s

' |) n  b8 Y! ]. l3 b2 ~! K9 V
' y2 s' d0 X! U6 R
3.添加LED闪烁功能
9 P1 a  h3 J6 U' D1 _) G作为单片机点灯小能手,RT-Thread下如何点灯是必须掌握的。打开RT-Thread组件图形化配置界面,可以看到默认开启了PIN和串口设备驱动的。
* W' ~" d2 C3 h  G3 n; g" K# R8 ]
14.png
图形化配置界面
在main.c文件中添加LED闪烁功能。包含头文件和添加宏定义
% ]* l5 f! t  S4 P/ Z6 i  F6 n
  1. #include <board.h>4 ~0 j6 m0 O: n1 o" j& {
  2. #include <rtdevice.h>
      r, ^) q8 V5 m2 r
  3. / o0 n$ c5 L$ B. g7 l% E% O, M  h
  4. #define LED_RED_PIN     GET_PIN(A, 7)1 a( `( I. J8 d5 U6 u
  5. #define LED_BLUE_PIN    GET_PIN(A,6)' Q( D) t# N6 v; Z. D

  6. ( s4 J" b. J, }! c4 ]* C
  7. int main(void)
    2 T" t1 T$ d( d; w0 y+ \5 M7 r
  8. {! b! U! C2 s6 J" S# A- n
  9.     int count = 1;/ Z& {; y5 X  q& ~& I9 K: M
  10.     rt_pin_mode(LED_RED_PIN, PIN_MODE_OUTPUT);
    + g9 ^- @. B  Z: I' B( J2 K+ g; @. e
  11.     rt_pin_mode(LED_BLUE_PIN, PIN_MODE_OUTPUT);
    3 Z9 z8 m: [+ @4 ^9 X' {' x7 c

  12. , E$ p9 B4 e" b- T
  13.     while (count++)8 [- `* Q! W9 K7 l9 y8 b
  14.     {% b0 [( I3 I8 l3 m% z& R* s
  15.         rt_pin_write(LED_BLUE_PIN, PIN_LOW);
    ! x9 S+ \+ M: s/ I
  16.         rt_pin_write(LED_RED_PIN, PIN_LOW);
    ( i- Y; e0 I& h5 h0 u0 V+ I2 ?
  17.         rt_thread_mdelay(100);
      y1 b3 P( }# w( z7 x# F
  18.         rt_pin_write(LED_BLUE_PIN, PIN_HIGH);
    3 {1 _9 U1 ~: Y" Z
  19.         rt_pin_write(LED_RED_PIN, PIN_HIGH);
    5 x! {9 X* {  R) R, Z8 H6 R& R
  20.         rt_thread_mdelay(100);
    2 U# o8 _2 l3 Z! Y% Y! H* i1 L
  21.     }
    ) M9 @! T) I' C) S. G2 D
  22.     return RT_EOK;
    . v1 }9 |! q' _) E+ E3 o. ^9 y
  23. }
复制代码
重新编译,下载。可以看到LED闪烁起来了。工程默认是使用内部RC作为输入时钟,所以无论你的板子是8M还是12M,都可以正常闪烁。我的开发板是8M晶体,这里我们配置使用外部HSE作为输入时钟。
9 n- U4 ?- L1 A0 M
/ z& X8 R) o0 |6 G: x- ^( m

3 C9 A- z; [$ [  P打开drivers->stm32f1xx_hal_conf.h文件,修改HSE_VALUE宏定义为8M。! U  v8 n* w1 c% n0 N* a
15.png
晶体频率修改
打开drivers->drv_clk.c文件:; h1 i$ e4 |* o: Y: ^
16.png
时钟源修改
配置PLL时钟源为HSE,并设置倍频系数为9。
5 o5 h& \$ E# G8 I1 K% n: S/ s
17.png
时钟源修改
18.png
倍频系数
这里根据实际板子晶体频率来设置,如果是12M晶体,倍频系数应该设置为6,如果是16M,需要参考时钟树,先2倍分频,然后9倍倍频。8 T9 f  p& @! k6 ^9 o$ O% _
  1. #include <rtdbg.h>( K2 J4 _# b8 N: _
  2. 1 M3 N& {. w" P. i' @
  3. void system_clock_config(int target_freq_Mhz)
    + B8 y+ O" T, h/ F1 |3 Q- [
  4. {3 s' Q; x$ S8 E" K
  5.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    + _; Z4 D# C3 t

  6. 5 j% Y0 R- c$ j% ]/ z! _
  7.     /** Initializes the CPU, AHB and APB busses clocks; J4 t& @9 b9 S9 q" n' V
  8.     */
    8 s* i/ c' M! @1 q4 C( _) l
  9.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;1 p# |$ d, |( l. h( W2 t8 F& P
  10.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    & S* Y6 w5 F0 i9 u* k. @1 X+ C
  11.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;1 B" l0 m* _& l; }+ S& u
  12.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    . h' h2 h  A% ]: J2 M7 g/ u
  13.      ........   . B+ }8 U/ h6 a5 Y, {! f% d
  14.     //9倍频0 T& n2 U9 n4 w: ^+ ~9 R/ k  [
  15.     RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;   //8*9=72M
    0 N- A( L8 L% Y* [; i- R2 _/ j1 E
  16.     ........
    . @; ]; f" Q2 T& i4 x8 T
  17. }
复制代码
这样就修改为外部8M晶体作为PLL时钟源,再次编译下载,和之前的现象是一样的。
8 a/ g8 I4 y% n1 Z0 a
# ^, m0 |" F/ C$ _
, P- d. X4 L$ s! d
4.添加ESP8266软件包
' P/ H# n2 b# @# ]联网设备,我们选择的是ESP8266-01S,如果看过上一篇疫情监控三部曲——在STM32F103 MCU上实现(裸机版),里面介绍了如何配置ESP8266 GET HTTPS请求, 配置工作模式 > 连接WiFi > 与服务器建立SSL连接 > 发送GET请求获取数据等等,整个流程固定而繁琐,那么能不能封装成一个模块,直接拿来使用呢?
# k% N4 J/ x2 c. F& x" }4 @/ D
19.png
esp8266
这里就要介绍RT-Thread的AT Device软件包了,! p, O) ^, m0 C# {$ ^/ @' B

+ H5 S$ x4 q: Z, g1 ~! n7 A
: P; w3 t5 M+ N; ?, Z+ k
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 命令章节 。
$ I! T7 j2 j0 o! a7 }http://www.rt-thread.org/document/site/programming-manual/at/at/4 p) h6 K- p% F- u

4 w$ Y) j- p+ Z
8 i, g+ o7 l7 o
简单的说,就是我只需要调用这个软件包,然后修改WiFi账号和密码,就可以直接配置ESP8266联网了。
: G. ~. n& R5 F3 V
; _; z/ ]9 U' ^& _+ @1 ^

" Y' B6 @. x6 D0 X+ c由于AT Device依赖于libc组件,所以在添加AT Device软件包之前,先开启libc。
! _8 l3 F: I6 l, A# K. j$ i- h7 n, @( V( Y7 ~4 T9 O: V3 ~" E5 K
- V; n; [6 x! J, F2 a
在RT-Thread Settings中点击libc灰色图标,变成彩色说明已经开启。
4 G" g  S5 F/ l. H- \$ X. N
20.png
组件配置
添加AT Device软件包,点击立即添加8 C5 u$ v! |0 d: z% a4 a
21.png
软件包
在弹出的软件包中心,搜索at_device,然后点击添加,添加到当前工程。6 j( P. e( T0 ^' P* E7 \
22.png
软件包
在at_device软件包上右键,选择详细配置:) g- P$ _0 l9 r. ^6 P7 M0 ~8 s
23.png
软件包
在弹出的页面,选择我们使用的WiFi模块类型,乐鑫的ESP8266系列,并配置WiFi账号和密码,WiFi模块所连接的串口号。$ O$ {& l% A" w9 u
24.png
WiFi配置
点击保存之后,工程会重新进行配置,添加相应的软件包文件到当前工程,重新生成Makefile文件,rtconfig文件等等。) i: U0 K( x! I1 c$ S6 v8 G) e
8 u* w+ |5 O, ~- d( a1 a& _0 \
7 Z4 u! ~/ l4 _: b9 B7 b
虽然我们在at_device配置中选择了uart2作为at_device设备连接的串口。但此时串口2并没有开启,还需要我们手动使能。
; G1 X# X% v) J
. ], O) m, K7 m4 ^3 O' S( i

1 I  n2 @  ~# D2 S$ G/ @1 G1 p打开drivers->board.h文件,通过宏定义的方式使能串口2。
8 U7 g5 N5 R1 O. I# q9 k
  1. #define BSP_USING_UART2/ d4 G8 C" q1 d$ f
  2. #define BSP_UART2_TX_PIN       "PA2"4 p* C1 t! T) G6 B4 _
  3. #define BSP_UART2_RX_PIN       "PA3"
复制代码
这样就开启了UART2的片上外设,Ctrl + B重新进行编译,时间会有些长,编译完成之后,可以看到flash文件大小明显比之前大了。
" Y4 m% V, A7 D
25.png
编译结果
Ctrl + Alt + D重新下载运行,打开串口终端:5 ~* |+ o5 H& M4 U
26.png
终端
可以看到,UART2初始化成功,WiFi连接成功。说明我们的串口模块已经可以正常工作了。提示[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!失败信息,是因为当前ESP8266的固件版本不支持AT+CIPDNS_CUR?这条命令,把固件升级到最新版本就好了。这个不影响后面的操作,所以就不用在意这个了。% a& f4 p- J* E, |  ]+ M6 y
6 u6 W9 Y. E, |: N: u1 b5 q  f

4 M( a# C( d7 c. \测试一下ifconfig和ping命令,都是正常的。
* m  u) k1 q% V. }: J& Y7 u
27.png
终端
在RT-Thread Studio中配置ESP8266模块联网,整个流程只写了3行代码,可以说是非常的快速方便。% `) B6 s# F# L7 E. c+ O& X

  I6 W% J5 Z4 g; _0 z$ W1 ]
: p# ]' @5 i" P/ I" u! ^) k) L" Y
5.疫情数据的获取2 W: F! G0 a7 X! P5 s  L
WiFi模块连接上互联网之后,就可以连接GET疫情数据的API接口http://lab.isaaclin.cn/nCoV/api/overall,然后读取返回的疫情数据。在上一篇的裸机工程中,是通过先和服务器建立SSL连接,然后发送GET HTTPS请求,获取到的返回数据,那RT-Thread有没有这样功能的软件包呢?这里就需要添加另一个软件包webclient。( E: @  a; J& k
5 c9 ?+ x! s; ^6 n8 K
' [5 ^1 j; p0 \; U
WebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。9 P7 Y. ?: b& S2 s  [
WebClient 软件包功能特点如下:' N8 K9 I' H& m" _

. `/ ]$ V4 ^6 `) N8 w
  o' r5 Y6 H( W( m3 ^
·支持 IPV4/IPV6 地址;7 X7 L' P* B, j" s/ ?
·支持 GET/POST 请求方法;
/ M7 m, J, {) ~8 y·支持文件的上传和下载功能;
  S6 \; e8 N8 ?% [# J. a·支持 HTTPS 加密传输;9 f' E& X" s& F# M
·完善的头部数据添加和处理方式。
  U6 I% K+ I* Z) ~3 D  f; b  N) r! h$ V9 {1 H$ g

/ m5 c# C! o+ P( R  T和添加at_device一样,在软件包中心中搜索webclient,
& a. |% h+ ?7 [/ s& r
28.png
软件包
然后添加到当前工程,右键进行配置,由于我们的http://lab.isaaclin.cn/nCoV/api/overall这个疫情数据接口是HTTPS类型的,根据软件包使用手册,我们需要选择TLS模式中的 MbedTLS。勾选添加GET和POST示例。
. S. o/ Z$ W8 S4 P& M" W
29.png
软件包配置
保存配置,看一下当前已经添加了哪些功能,可以看到有一些组件我们并没有去打开,但是已经被开启了,这是因为有些软件包是会依赖一些组件的,当使能软件包时,一些依赖的组件也被同时使能。
4 H2 `3 B/ M+ a9 s
30.png
软件包
Ctrl + B编译,Ctrl + Alt + D下载运行。在终端输入web_get_test测试GET请求功能。8 Q* C1 B& n" h7 H
31.png
GET示例
可以看到,执行get命令之后,会返回一个字符串,那么GET的是哪个地址呢?打开packages->webclient-v2.1.2->samples->webclient_get_sample.c文件,
( e1 G* o4 m& V; d
32.png
示例代码
可以看到GET的是这个地址:http://www.rt-thread.com/service/rt-thread.txt,我们用电脑上的浏览器访问一下:
* a! e( Q5 q/ B$ @. Y- i
33.png
浏览器访问
经过实际测试发现,GET HTTPS请求,还需要使能软件模拟RTC这个组件,否则会报assertion failed at function:gettimeofday, line number:19错误。$ T, Z% R9 y9 o# V% d, z1 v9 d
34.png
使能RTC
我们重新写一个获取疫情数据的函数,并导出到MSH。
6 T. _, H, R. P! N, n% l
6 u4 L3 X, T# U4 d

1 {+ m! l  @! v( i  c1 x4 m, X! Ousr_ncov.c文件内容' I; F- l: ?/ z& v; i* H0 l
  1. //usr_ncov.c, x6 l  V. v% B1 P5 m3 K% x0 o
  2. #include "usr_ncov.h"9 {. }; J8 G) F, y
  3. 5 D  W# a' H% v  z9 H
  4. int get_NCOV_Data(void)# w$ [- z7 }. [- r9 P6 }- w
  5. {1 {" b7 m. ]' b! c& B  J
  6.     char *uri = RT_NULL;
    $ x+ N# E$ y4 K3 S0 I3 E
  7.     struct webclient_session* session = RT_NULL;
    ( Y5 _; f; x) e$ c& k
  8.     uint8_t *buffer = RT_NULL;; e0 `; m( [% V' H8 Y: Z* Z& H
  9.     int index, ret = 0;' p% d9 ^. g- j6 p, S
  10.     int bytes_read, resp_status;+ v$ Z1 a9 z! E7 Y
  11.     int content_length = -1;
    8 j" E, e% w5 y' ?# [0 n# b2 h
  12.     int buffer_size = 1600;
    ! L! b9 W- i8 M
  13.     uri = web_strdup(API_NCOV);$ ?: y% T, g7 v
  14.     rt_kprintf("start get api: %s\r\n", API_NCOV);
    4 Z; z. U( r: t2 |4 v( B0 D( H
  15.     if(uri != RT_NULL)! Q" g$ M8 M" {" ?, B
  16.     {
    $ p# m0 d0 k" i8 Z" [3 w
  17.         buffer = (unsigned char *) web_malloc(buffer_size);
    . ^: c' v( a) q
  18.         if (buffer == RT_NULL)$ l( M" R' a- @; t  d+ ^
  19.         {
    % m+ U' A; K- M1 Y( U
  20.             rt_kprintf("no memory for receive buffer.\n");: V/ O+ \) a" M1 o* q
  21.             ret = -RT_ENOMEM;
    . }' `2 C) D- }0 T" I3 s" D
  22.             goto __exit;, U8 g# n" O5 J- T9 X
  23.         }
    , |3 ^8 ]' ~, {7 K/ z
  24. ) d1 Y4 s) J! }$ t; |) u( p
  25.         /* create webclient session and set header response size */" u( B2 E- v% k8 I
  26.         session = webclient_session_create(buffer_size);% |" F; K: u# O6 h
  27.         if (session == RT_NULL)
    . j% H+ W- V7 D  [# I3 Y( q
  28.         {
    0 ^; X$ ]2 |3 P0 O# y1 V
  29.             ret = -RT_ENOMEM;
    ) @9 K6 S1 O6 a6 f: z  `- a9 m
  30.             goto __exit;2 o4 g. e- ^4 p+ c
  31.         }
    & N" [7 i& e- U0 r9 X

  32. & h3 X& o% Q- J: l6 @
  33.         /* send GET request by default header */
    : e! N' S3 O9 |9 v' g4 m
  34.         if ((resp_status = webclient_get(session, uri)) != 200)5 j' A$ f- n( \" D2 A9 N  V
  35.         {% p; I+ s; B5 k& _8 F5 f' V
  36.             rt_kprintf("webclient GET request failed, response(%d) error.\n", resp_status);
    * @! W& l- ~1 k# E4 j0 |/ Y$ t
  37.             ret = -RT_ERROR;5 W) g$ Q' L! v3 a0 G' A  X1 N# L6 M
  38.             goto __exit;
    * i; W  ^9 y9 I8 C
  39.         }
    / m" a/ ]2 r7 A4 t0 x

  40. # Q9 i: k0 w$ D2 h  i, _
  41.         rt_kprintf("webclient get response data: \n");
    , P+ u7 W6 n6 Q( |) j- T
  42.   k9 O8 A: G1 F
  43.         content_length = webclient_content_length_get(session);( d1 L. O. s! p* ]* s
  44.         if (content_length < 0)7 s# j- K2 m/ i# M+ ^
  45.         {7 i  g/ B" @9 t! f& ]% `* N
  46.             rt_kprintf("webclient GET request type is chunked.\n");
    , Y" t+ K& s3 g; l7 H. ]$ L/ F
  47. 1 q# R+ B3 p% l  k7 V) n
  48.             do
    ; d  S: Z% i) a2 L* Y, H$ \
  49.             {5 O- ~7 \( q8 D4 p) x4 t( J
  50.                 bytes_read = webclient_read(session, buffer, buffer_size);
    $ R6 e8 U. @5 V1 q% g0 O( _
  51.                 if (bytes_read <= 0)
    $ I; d% c2 R2 @& i4 Q: G) @& t
  52.                     break;% C3 t7 G0 W3 J: ?3 F! I+ a

  53. : |8 f2 y8 h$ N
  54.                 for (index = 0; index < bytes_read; index++), i; `* t8 U6 D7 Z
  55.                 {) y5 f) k4 l1 ]5 [# [
  56.                     rt_kprintf("%c", buffer[index]);. `) z; e- x9 j  K' ~0 l- C
  57.                 }
    4 B- w" V- n- f6 [8 s$ j7 B
  58.             } while (1);; h$ }, i4 z; D, I  V: s% ?
  59. 7 @3 C2 n8 f4 [9 d( I5 P( P- Y
  60.             rt_kprintf("\n");
    ' c( v0 }8 ?6 J  j
  61.         }& }5 X& R" X: t0 O- ]) X  ~) {9 I) Q
  62.         else
    2 c) R4 K5 V1 q) f! h7 P
  63.         {
    5 Q2 b: M- J# ]2 ?2 F
  64.             /* 读取服务器响应的数据 */) i8 K" L$ E, U- X! b  P! `1 e5 B9 c/ {5 l
  65.             bytes_read = webclient_read(session, buffer, content_length);+ c0 `6 _# P. i( d0 F& m" o
  66.             rt_kprintf("data length:%d\n", bytes_read);  x8 s# Y; f! u

  67. 0 {3 b  w% H, x. q" Y
  68.             buffer[bytes_read] = '\0';
    ; L3 q# L4 A' D8 @  I
  69.             rt_kprintf("\n\n %s \n\n", buffer);
    1 i% I/ ^/ D, Z/ |3 |4 i, Y5 z2 g
  70. //            rt_kprintf("parse data\r\n");( {) F8 H9 A* ]
  71.             // parseData(buffer);        //解析函数
    3 v2 `" j5 e! U* W" \6 F
  72.             rt_kprintf("\n");
    / `. x! d+ i, [9 `3 y
  73.         }
    8 m5 G; p2 u. C; \0 R; v5 I

  74. ) m0 T# s4 }7 t% l
  75.         __exit:
    $ G" s3 w) S0 i+ |5 p
  76.         if (session)
    0 V2 Y  J4 m8 X
  77.             webclient_close(session);
    ' u1 X# Z; G  [6 f/ n  ^5 P1 l
  78. 0 p- D- f5 C" Y7 d8 X7 I5 w
  79.         if (buffer)
    9 F8 ]: _) x1 g4 @% b4 w* H) L2 J
  80.             web_free(buffer);
    " Q7 z1 U' o& T  ]) \, A7 i
  81.     }
    7 c1 b& D5 `) {( A! @8 `9 L
  82.     else
    : u4 k0 M) a, k/ f" W3 l
  83.         rt_kprintf("api error: %s\n", API_NCOV);# g% b, @0 Z6 {. A* R% c
  84. 4 y- z- o& d$ R5 @4 Q9 y) t
  85.     return ret;
    / U( r6 z# P' \( t9 L! |
  86. }
    8 S) C; o: |1 s! P. ^; A' J0 I
  87. MSH_CMD_EXPORT(get_NCOV_Data, get api ncov);
复制代码
usr_ncov.h文件内容" s6 y8 O! H/ F" _6 o" O- i
  1. #ifndef APPLICATIONS_USR_NCOV_H_' j3 _& i3 C- G7 O7 `
  2. #define APPLICATIONS_USR_NCOV_H_6 t6 O. ?, V! y7 S
  3. #include <webclient.h>
    . }5 g4 D5 }; W: C) V
  4. #include <rtdevice.h>
    4 m4 X! j5 L( Z9 P4 Z2 C: M( d
  5. #include <rtthread.h>
    9 P: V9 O' c" S6 h; S0 v
  6. #define API_NCOV     "http://lab.isaaclin.cn/nCoV/api/overall"" s$ j4 d9 O$ I: G( ]
  7. int get_NCOV_Data(void);
    ( I% C2 A; N7 d
  8. #endif /* APPLICATIONS_USR_NCOV_H_
复制代码
重新编译,下载,运行。在终端运行这个命令:! {# c; K& r; j
35.png
命令获取疫情数据
可以看到获取到了返回的数据,长度1366个字节。下一步就是对这个JSON数据进行解析,获取到我们想要的疫情数据。
) D# W3 I( j% d2 m! K) ?' t# c2 W+ t9 X; m; S1 x* ~
* a& w: T% {1 C$ [
6.疫情数据的解析
0 R2 w) d3 K- W5 p- `8 j: NAPI返回的数据是JSON格式的,关于JSON的介绍和解析,可以查看使用cJSON库解析和构建JSON字符串。数据的解析使用的开源小巧的cJSON解析库,我们可以在软件包管理中心直接添加:7 i& y& s3 G$ q3 c4 d+ C; q& c
36.png
添加cJSON
在进行解析之前,先来分析一下JSON原始数据的格式:results键的值是一个数组,数组只有一个JSON对象,获取这个对象对应键的值可以获取到国内现存和新增确诊人数、累计和新增死亡人数,累计和新增治愈人数等数据。9 L" C4 Q& l  `# ~  L. J4 O

  @# f1 W: t0 W
! ~( v6 B& {# L/ B: |3 a  h! v
全球疫情数据保存在globalStatistics键里,它的值是一个JSON对象,对象仅包含简单的键值对,这些键的值,就是全球疫情数据,其中updateTime键的值是更新时间,这是毫秒级UNIX时间戳,可以转换为标准北京时间。$ X7 \# W2 A: R, V; l& s5 R: V
  1. {
    ! ^! b5 p* {$ M1 R' b: N
  2.     "results": [{
    4 P1 ^3 \% T$ F
  3.         "currentConfirmedCount": 509,8 q3 [% y7 L6 C) ?8 J6 Y8 E4 ]
  4.         "currentConfirmedIncr": 16," g5 f/ z! G9 c6 R( K& q8 c
  5.         "confirmedCount": 85172,
    , H5 x  ^: M6 {- [1 Y
  6.         "confirmedIncr": 24,+ L% d- e& Q+ e2 a3 p$ ]
  7.         "suspectedCount": 1899,
    4 p7 ?+ S$ V) E/ P- X
  8.         "suspectedIncr": 4,
    7 g3 K+ f7 L5 A
  9.         "curedCount": 80015,
    + |! i  r; J& T
  10.         "curedIncr": 8,, n; V3 [  e! i$ B
  11.         "deadCount": 4648,
    , m! g9 i, t8 D( _2 W
  12.         "deadIncr": 0,' L' C( B- d; `4 i$ ~- I
  13.         "seriousCount": 106,! p0 E4 I+ p/ E! i- q
  14.         "seriousIncr": 9,
    6 Z" x. c' a4 ^' n9 p1 m
  15.         "globalStatistics": {
    8 I5 A) m( j4 `  g) w; F
  16.             "currentConfirmedCount": 4589839,- d* T& p/ w. _, j0 ~. ?
  17.             "confirmedCount": 9746927,
    % \* H& E- P. {# F3 U% O
  18.             "curedCount": 4663778,
    : y) F6 m( i* ~0 a# f1 O
  19.             "deadCount": 493310,5 l  r1 B- ~7 ], @. s
  20.             "currentConfirmedIncr": 281,
    , A" J: K9 g) y1 N  L/ U$ k/ k. K, i
  21.             "confirmedIncr": 711,
    / ^% ?# Y4 @. D! o$ t* t' Z
  22.             "curedIncr": 424,  v9 t2 V3 ?& ^9 o. P
  23.             "deadIncr": 6
    , B6 l% U1 V! Q, e9 p* q
  24.         },# ]9 v5 u7 u' o3 {4 ?& p
  25.         "updateTime": 15932274893551 l$ j  M) A0 _: x9 D
  26.     }],) s/ c. I; G6 b: V+ W8 K; t! g
  27.     "success": true
    - B; a* ^0 G; ?  f, ?6 r# l
  28. }
复制代码
先定义了结构体NCOV_DATA,用于存储国内和全球疫情数据:( p% D+ H0 ^) p, X6 X$ l* g. |& M6 _
  1. struct NCOV_DATA{
    $ v0 i& }0 N% E; B8 g
  2.     int currentConfirmedCount;, R1 l9 l5 a  p9 k3 e
  3.     int currentConfirmedIncr;% b0 u7 \5 v! j2 I6 F. s$ t
  4.     int confirmedCount;1 r( q/ j( r3 O4 b/ T
  5.     int confirmedIncr;$ N6 |$ o/ }% |  ^
  6.     int curedCount;
    8 @' c' P1 a8 m$ c3 u2 O3 X
  7.     int curedIncr;
    3 N0 C7 \. `4 B  H" y
  8.     int deadCount;0 L4 d( x$ ?3 A2 s
  9.     int deadIncr;
    : M. g  m, ~0 d% O  l7 R
  10.     int seriousCount;
    ! p: E! P$ d& Z* H0 j
  11.     int seriousIncr;
      @4 f' L" F+ B+ F9 h  [
  12. . R$ q0 P) P( f6 h- L
  13.     char updateTime[20];
    / z0 ^1 B9 z, @: W/ `: k) W1 i
  14. };
复制代码
对应的解析函数:
/ }( |, m; R) e8 |; J2 X
  1. #include <cJSON.h>
    , \/ o  C! c9 _6 h- L3 O
  2. ( k6 D5 e' g# l% \* k8 \2 ~  f
  3. struct NCOV_DATA dataChina = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "06-13 16:22"};;
    2 N0 ]9 [" y% F. t) R5 k
  4. struct NCOV_DATA dataGlobal = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
    ; P: _$ o* C4 M- I, T- K

  5. ( O" J" j9 u5 x1 K* g% j
  6. int parseData(uint8_t *str); D! n/ K6 w1 H5 m: H! I
  7. {: @( g7 H/ i1 J, Y2 U
  8.     int ret = 0;. E4 @* n" [! y
  9.     cJSON *root, *result_arr;  ~/ F+ t1 b9 k! Z
  10.     cJSON *result, *global;
    ! n- ~0 C& T+ [+ G7 T# r$ m
  11.     time_t updateTime;
    : U3 y, w* H7 _' i% {3 ?
  12.     struct tm *time;
    + P0 n$ X, x) m

  13. : }! P& C1 i  N4 X' r
  14.     root = cJSON_Parse((const char *)str);   //创建JSON解析对象,返回JSON格式是否正确
    : j5 z' V' `# S; C  A7 @

  15. + s# Q, _$ l& c, c5 F5 Z4 M
  16.     if (root != 0)8 ^* f/ }2 i  O
  17.     {  d) D* v0 `0 p4 {& X9 a: |
  18.         rt_kprintf("JSON format ok, start parse!!!\n");
    . B* f% C- u* R: w. ?+ l
  19.         result_arr = cJSON_GetObjectItem(root, "results");4 F8 m$ c( r& Z6 O# u; a8 d
  20.         if(result_arr->type == cJSON_Array)
    7 {" Z# W7 n& w) k: j) {( u* A  p
  21.         {
    6 y: g0 {7 b, H  j- ]! S4 l1 k' f, W4 W
  22. //            rt_kprintf("result is array\n");5 [  x  ]" T, h: B; i- o+ f: y0 m
  23.             result = cJSON_GetArrayItem(result_arr, 0);7 A; {" A; j# M8 {. y* t  B! w/ A
  24.             if(result->type == cJSON_Object), d( _2 ^4 T; H/ `6 f
  25.             {
    + `! t$ Q2 Z7 U8 e9 U2 A% v
  26. //                rt_kprintf("result_arr[0] is object\n");# E: w6 b! r! y2 v+ {/ z( f
  27. . n' }% i  L8 {7 a2 e1 z
  28.                 /* china data parse */* I( p2 V& i* R6 }
  29.                 dataChina.currentConfirmedCount = cJSON_GetObjectItem(result, "currentConfirmedCount")->valueint;
    : `7 G, P& U# i
  30.                 dataChina.currentConfirmedIncr = cJSON_GetObjectItem(result, "currentConfirmedIncr")->valueint;1 D5 N6 z, ]) R3 `6 K
  31.                 dataChina.confirmedCount = cJSON_GetObjectItem(result, "confirmedCount")->valueint;& Y' B; F4 K7 a5 A, y5 h0 ~4 T
  32.                 dataChina.confirmedIncr = cJSON_GetObjectItem(result, "confirmedIncr")->valueint;
    ) M5 T' k/ P  ~3 B  P
  33.                 dataChina.curedCount = cJSON_GetObjectItem(result, "curedCount")->valueint;. L" v% m! f7 J, n) F9 l8 w
  34.                 dataChina.curedIncr = cJSON_GetObjectItem(result, "curedIncr")->valueint;; n2 t# v0 [' y5 i/ w6 b" V
  35.                 dataChina.deadCount = cJSON_GetObjectItem(result, "deadCount")->valueint;0 }3 \( O5 y& n! y$ Z: Z) G5 B
  36.                 dataChina.deadIncr = cJSON_GetObjectItem(result, "deadIncr")->valueint;' m* F+ G& @' V/ e& o

  37. & M2 l2 S+ E4 b. v' q+ z  ]
  38.                 rt_kprintf("**********china ncov data**********\n");
    . X0 z, F' `: X' }' c
  39.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataChina.currentConfirmedCount, "currentConfirmedIncr", dataChina.currentConfirmedIncr);
    1 W. x5 |: S" X( G% [9 T0 A! o+ H
  40.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataChina.confirmedCount, "confirmedIncr", dataChina.confirmedIncr);8 a- x( z" s" Q3 S: h9 `
  41.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataChina.curedCount, "curedIncr", dataChina.curedIncr);
      q3 w4 T  s; ?3 u4 k
  42.                 rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataChina.deadCount, "deadIncr", dataChina.deadIncr);
    8 }, {( I$ o5 B. M3 `5 [& X

  43. 5 F, B, S% o+ {* |+ O
  44.                 /* global data parse */
    . K; `# i% f8 q& _
  45.                 global = cJSON_GetObjectItem(result, "globalStatistics");, V& X2 @1 w1 X' O% L1 z- p# X/ v% L" B3 ?
  46.                 if(global->type == cJSON_Object)5 W) W( ?0 `: S
  47.                 {
    + I7 Q9 O/ B6 X: {
  48.                     dataGlobal.currentConfirmedCount = cJSON_GetObjectItem(global, "currentConfirmedCount")->valueint;
    3 U3 n- x( D0 {1 H- u( ?# @* R, a: {
  49.                     dataGlobal.currentConfirmedIncr = cJSON_GetObjectItem(global, "currentConfirmedIncr")->valueint;2 L. I4 u8 [5 o* j5 u* E
  50.                     dataGlobal.confirmedCount = cJSON_GetObjectItem(global, "confirmedCount")->valueint;( l: n5 q' E5 N
  51.                     dataGlobal.confirmedIncr = cJSON_GetObjectItem(global, "confirmedIncr")->valueint;
    # j8 l8 a9 f  t, n
  52.                     dataGlobal.curedCount = cJSON_GetObjectItem(global, "curedCount")->valueint;
    0 H% \* b. I6 V" `; v
  53.                     dataGlobal.curedIncr = cJSON_GetObjectItem(global, "curedIncr")->valueint;7 X0 w- Z1 v/ `0 K1 C1 Z* k4 s
  54.                     dataGlobal.deadCount = cJSON_GetObjectItem(global, "deadCount")->valueint;$ f0 H+ z/ r  x# D- B
  55.                     dataGlobal.deadIncr = cJSON_GetObjectItem(global, "deadIncr")->valueint;
    # U7 C  K+ |2 M# u, q& |
  56. 2 m- f9 d; f3 L! N
  57.                     rt_kprintf("\n**********global ncov data**********\n");' @5 v7 _, b7 S/ u+ F& S8 }, f
  58.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataGlobal.currentConfirmedCount, "currentConfirmedIncr", dataGlobal.currentConfirmedIncr);
    2 ~4 e8 @, e; M
  59.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataGlobal.confirmedCount, "confirmedIncr", dataGlobal.confirmedIncr);
    0 ]- Z2 `" P" ]5 p
  60.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataGlobal.curedCount, "curedIncr", dataGlobal.curedIncr);% M4 Y5 s- B6 G( }* L
  61.                     rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataGlobal.deadCount, "deadIncr", dataGlobal.deadIncr);
    - l0 L( R: r& E  g6 u3 g/ H! |% c
  62. 4 |1 q9 h: x5 a
  63.                 } else return 1;
    % s3 p" i% }0 Y2 l3 b  h( S
  64. 4 e# y* A& i6 e& T# R
  65.                 /* 毫秒级时间戳转字符串 */4 f$ |5 S& C- |! L7 m
  66.                 updateTime = (time_t )(cJSON_GetObjectItem(result, "updateTime")->valuedouble / 1000);0 e; x, {, ^. V, A6 |* ?& j4 O9 s
  67.                 updateTime += 8 * 60 * 60; /* UTC8校正 */
    ; h6 q5 B; P# P' }$ Y: Z
  68.                 time = localtime(&updateTime);  m6 i% h& W4 y  ]
  69.                 /* 格式化时间 */5 h: o" f7 i9 e% y
  70.                 strftime(dataChina.updateTime, 20, "%m-%d %H:%M", time);# ^' D6 Y* }  _. L" V( B
  71.                 rt_kprintf("update: %s\r\n", dataChina.updateTime);/* 06-24 11:21 */
    # t1 h) O& n; G
  72.                 //数据在LCD显示
    8 u5 q, l! b  ^, Z
  73.                 //gui_show_ncov_data(dataChina, dataGlobal);4 s2 y, P. ~2 G6 S9 r; @8 s
  74.             } else return 1;7 p# H' L1 n$ ^; H7 t7 L
  75.         } else return 1;# C0 k' k" k8 i: O$ `
  76.         rt_kprintf("\nparse complete \n");
    ) F, L5 b. T% N5 M5 P
  77.     }
    . |6 o4 g; C$ z) s
  78.     else, ~+ b8 Z% e) m/ C* `7 i+ p5 Z
  79.     {, v3 x% E2 O/ @5 p3 _6 A* W% g
  80.         rt_kprintf("JSON format error:%s\n", cJSON_GetErrorPtr()); //输出json格式错误信息: p1 E! z7 a, [4 |: w8 ?; x& X$ |; k
  81.         return 1;
    0 F, M# K+ Z0 v& ?  d* B' u
  82.     }9 ?0 c2 B7 x8 P: z6 x
  83.     cJSON_Delete(root);8 d0 h) k* h# P' `  R2 k6 _

  84. 4 h: g9 V! f( w: q4 B! g/ N
  85.     return ret;7 R! z2 f( }. K! ]
  86. }
复制代码
在数据接收完成之后,对JSON数据进行解析。5 }1 h- C: b4 k2 @: w
37.png
解析结果
7.疫情数据的显示
8 ^, c3 q) F, _- c: i, s' r% W数据解析出来之后,剩下的就简单了,把上一篇文章中9341的驱动文件移植过来就好了。
& \$ D3 x4 v2 g% Z
. X* R( T, _7 m& z! k

: m5 L; C: ~' t8 @2 ~$ i液晶屏使用的是3.2寸 LCD,IL9341驱动芯片,320*240分辨率,16位并口。由于屏幕分辨率比较低,可显示的内容有限,所以只是显示了最基本的几个疫情数据。为了减小程序大小,GUI只实现了基本的画点,画线函数,字符的显示,采用的是部分字符取模,只对程序中用到的汉字和字符进行取模。为了增强可移植性,程序中并没有使用外置SPI Flash存储整个字库。4 h  `$ K' Z( L0 b5 f
% h% o, t$ m3 g) X* E
5 j# c8 p; s3 [3 z, K
由于RT-Thread Studio使用的HAL库,所以LCD的GPIO初始化函数需要修改一下:' p8 P' I* r7 x' j& Z2 P8 D
  1. void lcd_gpio_init(void)
    4 ]; e# m7 E' x8 j/ A2 Y8 i" T
  2. {
    & T) O7 {8 @% ~) r* J+ C0 t
  3.     GPIO_InitTypeDef GPIO_InitStructure;# K# E( E3 _; d7 i  ~$ }$ @/ q
  4. * T2 A( N* V% ^$ M& U! k
  5.     __HAL_RCC_GPIOA_CLK_ENABLE();) H( q- P6 |7 m4 g3 Y/ }' x
  6.     __HAL_RCC_GPIOC_CLK_ENABLE();9 m4 ?/ {( y+ F' x) q2 H! u
  7.     __HAL_RCC_GPIOB_CLK_ENABLE();7 }1 O2 ^* [2 l- i5 F# h# V: Z2 P
  8.     __HAL_RCC_AFIO_CLK_ENABLE();, M- \: {7 N& g! f' w- c: S( E' H: p
  9.     __HAL_AFIO_REMAP_SWJ_NOJTAG();, Y$ z' ~0 O, I7 K4 \
  10. " J9 M) @! `# l: r4 _4 m6 C, n
  11.     GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    ) ~! `) E' r# D; @* ?+ h+ P
  12.     GPIO_InitStructure.Pull = GPIO_PULLUP;  G* h4 X8 ?2 n8 x: E9 w( A/ q: y
  13.     GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;  _- |* W) l: n6 c6 x
  14.     GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6;
    $ j  J6 o# V3 P& z, S' {. P
  15.     HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOC0 }8 y9 g: B  Z  P& Z# \& I6 z
  16. : y! ?" t! O3 o/ e
  17.     GPIO_InitStructure.Pin = GPIO_PIN_8;    //背光引脚PA8
    0 ]% i$ Y8 b% @
  18.     HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); //GPIOC, @7 {; C' N- _7 {3 e; m! F) t/ L
  19. 3 B% Y0 S; i7 Z. A  X7 j. M, @$ y, K
  20.     GPIO_InitStructure.Pin = GPIO_PIN_All;
    ' a- T9 {; ?' Z# }9 {) R) i/ o
  21.     HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
    2 M7 Y+ m$ J- X9 U3 D: }: U
  22. 6 l3 O# M  V' n0 ^7 s3 D+ \
  23.     HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6, GPIO_PIN_SET);% C% `; J7 k# Z. x8 n
  24.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);% n1 |, s9 \" z% |
  25.     HAL_GPIO_WritePin(GPIOB, GPIO_PIN_All, GPIO_PIN_SET);
    8 W4 B( P& F+ G
  26. }
复制代码
延时函数换成:) d/ W! |8 r6 A& M, N
  1. rt_thread_mdelay(nms);
复制代码
还有一点,在Keil中,文字编码选择GBK编码,1个汉字占用2个字节,而RT-Thread Studio为UTF-8编码,1个汉字占用3个字节,汉字显示函数需要调整:
- Y. J) n( u5 B, w
  1. void gui_show_chn(uint16_t x0, uint16_t y0, char *chn)4 h8 K% U. A, w/ i0 f3 [7 [
  2. {7 D6 I2 P$ o+ A0 y: q3 @9 O& a3 T
  3.     uint8_t idx = 0;
    ! v3 @8 C/ B* O6 j  D
  4.     uint8_t* code[3]; //UTF-8:国=E59BBD. z2 z) R+ M) o# w9 W
  5. # v6 [/ a5 T9 P+ o
  6.     uint8_t size = sizeof(FONT_16X16_TABLE) / sizeof(FONT_16X16_TABLE[0]);
    + z9 e; J  _' ~1 H( b
  7.     /* 遍历汉字,获取索引 */
    5 {9 @: _, s' y
  8.     for(idx = 0; idx < size; idx++)
    ! t2 A' _6 j- ]) c- {
  9.     {
    2 Z! |+ x# T, V/ R. T0 d
  10.         code[0] = FONT_16X16_TABLE[idx].chn;2 \- S6 ?* g% f
  11.         code[1] = FONT_16X16_TABLE[idx].chn + 1;
    ; P7 ]2 U$ H, |
  12.         code[2] = FONT_16X16_TABLE[idx].chn + 2;/ u: `( f: |: O+ f" k0 l3 K
  13.         //汉字内码一致+ N0 B: t7 W* g' t# d' \
  14.         if(!(strcmp(code[0], chn) || strcmp(code[1], chn+1) || strcmp(code[2], chn+2)))2 x5 x4 I+ J7 m& |& Y! H7 [" j6 y
  15.         {+ a7 V# e: n  J* r
  16.             gui_show_F16X16_Char(x0, y0, idx, WHITE);
    & a, m9 e9 f' O
  17.             return;
    # r' g: h6 U! \3 d
  18. //            break;
    ' H3 k7 j, N- T
  19.         }
    9 B# n3 g  A. {& b4 T4 O0 {9 b
  20.     }& G/ H7 |7 y4 F) d9 O$ a+ }) n2 o8 |
  21. }
复制代码
疫情数据显示函数:$ R/ K2 R2 @. x& i8 `) z
  1. void gui_show_ncov_data(struct NCOV_DATA china, struct NCOV_DATA global)
      U# l$ d" V3 x& [9 N' h
  2. {
      d* _! u( v9 N$ C
  3.     uint8_t y0 = 20;
    4 F% c* d9 M5 @# `# i6 R

  4. ' W! v3 z, T# c. v8 D
  5.     lcd_clear(BLACK);
    6 e0 K/ K% N* E- K' {7 n- G4 n
  6.     gui_show_bar();
    ! A3 Y9 B  M% D( c# d+ N9 t6 ~
  7. ' ?  {. K  E* b3 {
  8.     gui_drawLine(0, 18, 320, DIR_X, WHITE);" R1 a2 j& {5 L: X" L
  9.     gui_drawLine(0, 38, 320, DIR_X, WHITE);
    + c( c. K- m+ v- i, C) ]- _
  10.     gui_drawLine(0, 138, 320, DIR_X, WHITE);6 T  F2 l+ R* {  N8 B& V
  11.     gui_drawLine(0, 158, 320, DIR_X, WHITE);# g/ ^) o" o# k1 M. J% c5 r
  12.     gui_drawLine(0, 220, 320, DIR_X, WHITE);( p9 m( C; @1 l$ q

  13. # g4 Z' \% f5 U% n- F; m
  14.     /* "国内疫情" */. I# n+ v3 ?# a! F1 p# W4 g
  15.     gui_show_chn_string(128, y0, "国内疫情");; D+ c5 B$ T4 j9 x" L* e. Z2 q4 h  I
  16.     gui_show_line_data(40, "现存确诊:", china.currentConfirmedCount, "较昨日:", china.currentConfirmedIncr);( F7 H( S2 }7 ~1 ]5 H/ C
  17.     gui_show_line_data(60, "累计确诊:", china.confirmedCount, "较昨日:", china.confirmedIncr);  O6 Y- C- `! v" o; ?( ~
  18.     gui_show_line_data(80, "累计治愈:", china.curedCount, "较昨日:", china.curedIncr);
    : ?. j% [4 H4 [! A4 K& T  W
  19.     gui_show_line_data(100, "现存重症:", china.seriousCount, "较昨日:", china.seriousIncr);1 n: e! p7 M% f: ]
  20.     gui_show_line_data(120, "累计死亡:", china.deadCount, "较昨日:", china.deadIncr);
    , B& F% @- \! L& }

  21. 3 D) C5 J" k, n0 A6 x* w. O
  22.     /* 全球疫情 */  p5 M0 @3 m8 Q+ J  l' I
  23.     gui_show_chn_string(128, 140, "全球疫情");
    6 \# N$ `# U+ V# r
  24.     gui_show_line_data(160, "现存确诊:", global.currentConfirmedCount, "较昨日:", global.currentConfirmedIncr);
    7 ?3 g+ `0 s0 v3 Z# M( E$ @/ d" M
  25.     gui_show_line_data(180, "累计治愈:", global.curedCount, "较昨日:", global.curedIncr);+ t  c5 \- ?0 [, y
  26.     gui_show_line_data(200, "累计死亡:", global.deadCount, "较昨日:", global.deadIncr);+ O+ r5 I; |3 m9 W# X3 _% u

  27. : \* C" M6 w6 r6 }2 \# n
  28.     gui_show_chn_string(160, 222, "更新于:");5 V1 n5 g7 e! [" K# q4 d& V$ P8 p
  29.     gui_show_F8X16_String(230, 222, china.updateTime, GREEN);
复制代码
最终显示效果
$ Q& v2 V& E2 S' w( j
38.png
最终效果
开源地址! e8 G# p9 ~- a: l  F
代码已经开源,地址在文末,欢迎大家参与,丰富这个小项目的功能!
; x& K+ Y; K5 R4 F' K2 Q2 M. W, L3 o: {; z

! H3 A, R* d. T. B* R6 Y8 P基于STM32+RT-Thread的疫情监控平台
0 d; ?- p/ J+ P; |; u* xhttp://github.com/whik/rtt_2019_ncov
% I# _  [  S1 f
0 R; [  v9 Z  @- }- {: t
- j4 e1 r& E9 l9 [4 w: w
基于STM32F103的疫情监控平台(裸机版)
: P- B* `- H2 Q4 C/ L0 `7 l& W8 l' xhttp://github.com/whik/stm32_2019_ncov
4 _2 D6 q$ w& q' R, t/ I
! C: K- N! g% ?* B& p4 Y; ]+ j3 J& n. p  _7 k4 Q, V
# c2 N8 R# `1 q9 b5 Q/ T- ~
文章出处:[color=var(--weui-LINK)][url=]RTThread物联网操作系统[/url]
' {4 e, S- {( t. a3 P: c# _4 n! D! X
收藏 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 手机版