可能不少接触过STM32 和Arduino的小伙伴看到标题觉得很神奇,STM32上怎么能跑Arduino 的程序呢?,我第一次Arduino的2000人交流群里发这个项目时候,大家也觉得很神奇。
# i8 f" |0 G" v+ m
6 W/ g5 ?2 F9 F可能传统学习路线 ( 51 -> STM32 -> 各种Arm核心MCU ) 的单片机工程师并不了解什么是Arduino,Arduino是一个以avr单片机作为硬件(现在不光支持avr了,还有STM32、ESP8266等等),拥有数以千计的开源项目的电子设计平台。比如你玩pid ,你不懂pid 可以直接Arduino IDE的库管理中下载别人封装好的PID库,比如你想玩个MQTT远程控制,Json解析/正则表达式,NTP服务 ... 不需关心MQTT的底层原理,Json怎么解析。虽然传统单片机上也可以移植cJSON库进行解析,但是ArduinoJSON可以使用更优雅的方式解析json。
, O( P& N/ P# K- F
5 ?/ P% Y) r" I9 H4 UcJSON解析( f7 ?( ?$ X; V. L4 a4 ?" }7 U6 f5 Y
! |" ?) ~% q) @, e* V
- #include <stdio.h>
2 i$ e( s6 O# L& i7 [ - #include "cJSON.h"! ~/ j/ {* Q4 Z9 |& X& A
! o% ~6 _5 [7 H* d# `- int main(void)
8 Y+ U3 Y4 E2 q1 E- V9 U f - {8 [7 f8 ~8 E% I# @, X5 c
- cJSON* cjson_test = NULL;( v2 b3 U9 Q) G. K8 O
- cJSON* cjson_address = NULL;
. U5 {) X+ @! c3 q0 Y; t - cJSON* cjson_skill = NULL;( f+ @' k% K! B5 _# ~. S5 X
- char* str = NULL;+ k: s6 W5 ~: L* {
6 C+ o% n7 X" W; A1 q8 X' a8 u- /* 创建一个JSON数据对象(链表头结点) */3 N3 }& M) P" @! C5 v9 t
- cjson_test = cJSON_CreateObject();
* r) b3 T$ r2 w9 ^$ T+ H" z. t
" N3 O G$ V* M: ]# a; z! X- /* 添加一条字符串类型的JSON数据(添加一个链表节点) */4 V) F2 z9 e4 {; V/ L9 X* [
- cJSON_AddStringToObject(cjson_test, "name", "mculover666");
& k& R2 u% E5 Y - - a7 K; i/ U; g% E' Z& Q R
- /* 添加一条整数类型的JSON数据(添加一个链表节点) */
: c A5 }2 F( O# B* [* I - cJSON_AddNumberToObject(cjson_test, "age", 22);
! j" p7 i) {' j6 \
: J; n) I2 Y# E: f! P- /* 添加一条浮点类型的JSON数据(添加一个链表节点) */
! |1 E G3 o( U2 \ - cJSON_AddNumberToObject(cjson_test, "weight", 55.5);
9 t v0 \2 ]4 o' Z% M' X
* l3 ]& I0 x0 k! ~# X- /* 添加一个嵌套的JSON数据(添加一个链表节点) */# B3 |0 D( [8 v# a5 r
- cjson_address = cJSON_CreateObject();
, }' c g. Q2 ~& R# t: Y H/ { - cJSON_AddStringToObject(cjson_address, "country", "China");6 e& j/ @9 U- t' L- d: J
- cJSON_AddNumberToObject(cjson_address, "zip-code", 111111);# ^+ f9 x; p5 e
- cJSON_AddItemToObject(cjson_test, "address", cjson_address);
. g4 ?' ~+ O. v - % _5 N" ~+ V# d: P" S
- /* 添加一个数组类型的JSON数据(添加一个链表节点) */
5 t# ^8 ?1 ?- Z# ~) E3 v$ j2 l; Y - cjson_skill = cJSON_CreateArray(); {: \7 g! F0 y: Z
- cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));
. @; \3 G+ `2 v- v$ V. X6 C$ r - cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java" ));$ S8 k' i' p4 m: O
- cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));+ M2 w- T* s$ `& j4 a5 @
- cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);3 w$ e$ P( W+ z
" s0 M3 i& q# h0 L- /* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */
! s& n/ D( H8 w) ^ - cJSON_AddFalseToObject(cjson_test, "student");. R& J2 V+ {% J- r8 \4 Q
- . {, t" J7 q4 o) r% a& x
- /* 打印JSON对象(整条链表)的所有数据 */
$ J6 I; G4 M ~# j! j - str = cJSON_Print(cjson_test);
* d* k- m u1 l1 Q - printf("%s\n", str);+ W4 _6 ?% L5 t3 b7 Q/ E: x
- 3 a- B1 m) Z0 l/ Y# y' T4 A# V
- return 0;
r n4 ?4 z# I. T, g0 ] - }
复制代码 6 y4 Y: Q; h* t3 b% p
ArduinoJSON
; g6 c" f4 B& \7 B9 Z# i& j$ d( J1 ]. ?: q) T* M+ |7 h6 U0 c: k
- DynamicJsonDocument doc(1024);3 t6 x% B5 O+ F( k3 R' I2 b8 }; | }
! t2 | Y: |* t7 G- doc["sensor"] = "gps";! F& W w* L+ n- q$ _ J
- doc["time"] = 1351824120;
' Y1 x2 c+ V9 L6 T& Y+ \$ K - doc["data"][0] = 48.756080;
S% O# f" @" d2 a9 v - doc["data"][1] = 2.302038;) H0 l/ i2 d q* s f
- . U Q+ j1 W) Z, n- {* P
- serializeJson(doc, Serial);
! t1 y/ ]8 H) F - // This prints:$ J6 k3 [. }# l7 ^1 ^' D
- // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
复制代码
" B" Y }7 ]+ t6 s: q看到了有木有,得益于C++的特性、我们可以使用更优雅、高效、安全的方式解析JSON。
' M" p, |6 Y) g+ l- Q$ W
* q8 x% M3 @( E0 J' v4 Y6 mArduino平台有很多类似于ArduinoJSON这样优秀的开源库。如果能为我们所用就好了。
9 x( @7 d- w: ?! Y! a) r( m& y1 G7 {6 r
于是我将移植了Arduino 核心API到STM32上。这样Arduino能用的库STM32只要下载下来,并将源代码添加进工程,包含头文件目录就可以使用了。# O% q2 N0 z- y7 o) M8 E
) T/ k- \, b( F4 d7 X9 q" M) ?
为了更好的支持C++目前项目采用AC6编译器,不包含第三方库的话AC5也是可以 0 warning, 0 Errors编译的。不过不推荐使用AC5,因为第三方库基本上都大量使用了C++11 甚至是C++14的特性编写。只要AC6可以完美支持。 y! ~ |7 c+ ~3 d
1 \) T8 S1 b2 ?/ k+ x; \: F8 ]
6 B7 y3 Q( l, _6 @) o7 `系统框架说明
. e+ ^* X! A0 {+ R8 z# y
' r6 l: ]/ ?4 ?+ T% _" U
: }: b* |3 o0 C8 Z
d C+ N# k' G大家下载下来工程后,可以看到有很多文件夹。其中DOC是文档目录,里面存放了说明文档。大家可以看看。
, L7 c9 a5 V% F+ f, D# e7 v6 P9 B+ L. h' b
Drivers 是驱动包,这个是由MCU厂商提供的,比如我们用的是STM32F103,这里面存放的就是STM32F103的标准库源码,如果我们用的是F4、H7 这里面放的就是HAL库源码。我们用华大的HC32就用的是华大提供的驱动库源码。MDK文件夹里放的是MDK工程文件,以后还可能多个IAR文件夹,顾名思义是IAR的工程文件目录。USER里只有个main.cpp 是我们用户编写的代码。
, s+ o9 I$ o) {% a8 q
. u4 i/ z/ g0 D
* ~ q$ d$ t8 `+ E/ G- q. l
, s1 Q7 w* e, _
/ {0 {1 e% A2 V, P6 G1 m- W/ v T3 x
Frame文件夹下就是框架源码了。ArduinoAPI顾名思义是Arduino 的核心API(用法请参照Arduino 官网)。LLA_Drivers是底层抽象驱动层,顾名思义是对Drivers 驱动包的进一步抽象(封装),对其它MCU的移植也基本上就是移植这里面的文件。4 Y# [$ x* i8 O# u8 U
/ E# ^ B+ i8 `: A0 ^: Z' l4 q7 q; N, F% u+ b6 D* f6 j
如何添加第三方库:
5 H7 U( `% ?/ V
$ {- B5 `0 y- D( a
2 t3 o" S& I0 c8 k# D- ?
/ C M& o6 P+ y0 r& F( s P添加一个新的分组,明明为库的名称。然后把库文件夹下的所有cpp和.c文件全部添加进来。
4 ]% R- }, A2 ~
* M6 X. H G" H8 ?
& T3 Q. u! K& B5 k
" X0 O: d3 k5 e0 Z3 z! h0 d2 |
然后将库文件夹下的头文件路径包含进来。运行编译,然后解决一下编译错误(部分库因为用了寄存器级别的代码所以肯定编译不过的)。
2 l5 V* V; m5 _: p
- J- O+ X) F" f. ]默认工程里跑了个MQTT的demo,需要是STM32F103ZET6 + W5500 以太网芯片(连接在SPI2 //SCK-PB13 MISO-PB14 MOSI-PB15 上),所以Arduino API 的 SPI其实是STM32的SPI2,如果你需要修改为SPI1的话打开ArduinoAPI的SPI.cpp文件拉到最底下更改下注释就行了
' A; }' Y% U) H) s4 n' W2 \& G5 @; r) ~8 p2 J! [
- //SPIClass SPI(LLA_SPI0);//SCK-PA5 MISO-PA6 MOSI-PA7
?0 T M# x0 g6 U; q% a - //SPIClass SPI_2(LLA_SPI1);//SCK-PB13 MISO-PB14 MOSI-PB15
# I y9 e, Y0 l6 X
3 J2 E# A& Q& `; P- SPIClass SPI(LLA_SPI1);//SCK-PB13 MISO-PB14 MOSI-PB15
复制代码
( P0 k6 i7 r0 t0 M8 L6 o这是我跑的MQTT例程,通常情况下第三方库都会有个examples文件夹,里面的demo程序直接复制粘贴进main.cpp 就可以运行。+ {& z2 c4 U5 P- k) M' [
* j, f* v2 ]. Z% U6 \
3 V" O5 z- ^% D$ ^% i! c
# N/ U$ s- f- M5 `/ g" N- I' P! I0 }. U
LLA层串口断言: H' R' @! V# t K
* E9 g. {" j' N8 Z% ]你填错了函数参数,会定位出错位置并且会有提示。( ?% |, f6 D. j9 L: s; v2 w
# |1 W! \& o' Q$ ^+ R# y
Z4 k6 Y7 [3 Y5 h7 N 作者:捉住一只皮皮虾# b: |! o0 n7 K+ K* `# C; {
( G4 K6 Y3 z2 ~ k7 f' \* L
+ ^9 N# e+ k0 q9 z$ U T; [. P" @& W |