RTduino为RT-Thread的Arduino生态兼容层,是RT-Thread社区的下属子社区,旨在兼容Arduino社区生态(如上千种分门别类的Arduino库,以及Arduino社区优秀的开源项目),来丰富RT-Thread社区软件包生态,并降低RT-Thread操作系统以及与RT-Thread适配的芯片的学习门槛。可以让用户通过Arduino的函数和编程方法,轻松地将RT-Thread以及特定的芯片使用起来。用户也可以直接使用Arduino社区的库(例如I2C传感器驱动库、算法库等)直接用在RT-Thread工程中,极大地补充了RT-Thread社区生态。该项目由RT-Thread社区核心开发和维护者满鉴霆发起。
本软件包可以运行在RT-Thread Studio IDE和Keil编译环境下,因为Arduino的库都是基于GCC环境开发的,因此强烈推荐使用RT-Thread Studio IDE运行。
BSP名称 | 备注 |
---|---|
STM32L475潘多拉 | 引脚异构布局,但外设丰富 |
STM32F072 Nucleo | 标准Arduino UNO引脚布局 |
STM32F401 Nucleo | 标准Arduino UNO引脚布局 |
STM32F469 Discovery | 标准Arduino UNO引脚布局 |
STM32F103 BluePill | 引脚异构布局 |
ES32F3696 | 引脚异构布局 |
注:RTduino也可以无需适配特定BSP,直接运行在任意RT-Thread BSP上,请参考第5章-RTduino精简模式。
本软件包需要对特定的BSP进行适配之后才可以使用,适配方法很简单请参考。本节以STM32L475潘多拉开发板和RT-Studio开发环境为例,来讲解如何使用本兼容层。
2022年RT-Thread全球开发者大会报告视频
请到RT-Thread Github官方仓库,下载最新的源码。对于部分用户下载Github源码慢的问题,可以百度或者到B站搜索“Github加速”等关键字来解决,此处不再赘述。
下载好之后请解压,打开RT-Studio IDE,选择文件(File) -> 导入(Import),并选择RT-Thread BSP Project into Workspace,也就是将BSP工程导入到Studio的选项。
路径选择,你刚刚下载解压好的RT-Thread源码,以STM32L475潘多拉板为例:rt-thread\bsp\stm32\stm32l475-atk-pandora
。工程名字随便起一个就好,比如STM32
:
点击完成(Finish),稍等片刻即可完成工程导入。
导入成功之后,双击RT-Thread Settings,进入到RT-Thread工程配置界面,点击<<按钮,进入到详细配置页面:
点击Hardware,选择Support Arduino,只需要点一下即可,其他依赖项会自动处理。然后点击小锤子按钮进行编译,RT-Thread Studio会自动保存你当前的配置并下载RTduino软件包以及依赖项软件包,并将这些软件包加入到工程中,最后自动编译整个工程。
总的来讲,你只需要选择Support Arduino,并点一下小锤子按钮,就坐等编译成功即可。
至此,RTduino软件包安装完成,此BSP工程已经具备了兼容Arduino生态的能力。
RTduino软件包包含有两个主要的文件夹:core和libraries。
core文件夹主要是提供Arduino内置的所有的API函数,例如analogWrite、analogRead函数等等,这些函数可以在Arduino官方找到详细的介绍。
libraries文件夹是Arduino库所在文件夹。其中:
buildin文件夹下存放着Arduino内置的一些库,例如Servo舵机驱动库,Wire I2C驱动库等等;
user文件夹是用户文件夹,这是对用户来说很重要的一个文件夹,里边默认是空的,用户可以把下载好的Arduino库拖入到此文件夹中来,在下个章节会细说这个操作。
对于Arduino,最经典的莫过于setup和loop函数。这两个函数位于BSP目录下的applications文件夹内arduino_main.cpp文件中。以潘多拉板为例,这两个函数位于:
bsp/stm32/stm32l475-atk-pandora/applications/arduino_main.cpp
文件中,在开启RTduino软件包后,你可以直接在工程的Applications组中找到它。
- 1#include
- 2
- 3void setup(void)
- 4{
- 5 // put your setup code here, to run once:
- 6 pinMode(LED_BUILTIN, OUTPUT);
- 7}
- 8
- 9void loop(void)
- 10{
- 11 // put your main code here, to run repeatedly:
- 12 digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
- 13 Serial.println("Hello Arduino!");
- 14 delay(100);
- 15}
可以看到,板载的LED灯已经开始闪烁,串口开始输出了。
注意:
由于RT-Thread的main.c文件内,也会默认闪烁一个LED灯,如果板子上只有一个LED灯的话,两个线程会发生干涉。但是你会发现这个LED的闪烁速度明显变快了。因为main.c那边的闪烁周期是1000ms,上面这个例程是200ms。
如果你用潘多拉板,main.c闪烁的是红灯,RTduino兼容层的Arduino程序默认闪烁的是绿色的灯,二者不会发生干扰。
由于每个BSP的板子设计、以及芯片型号等,引脚分布是有区别的,因此需要到指定BSP的applications/arduino
文件夹下的README.md文件查看详细信息。例如:
STM32L475潘多拉板的Arduino引脚布局的详细说明 | STM32F072 Nucleo板的Arduino引脚布局的详细说明
软件包:英文为 software package,是指RT-Thread社区所属维护的第三方扩展,是RT-Thread原生生态一部分。
库:英文为library,是指Arduino社区所属维护的第三方扩展,是Arduino原生生态一部分。
库和软件包其实是一个意思,只不过两个社区叫法不一样。
目前RTduino兼容层可以实现对Arduino纯软件类(例如算法类、数据处理类等)、串口相关、I2C传感器相关的库做到100%兼容。
支持的详细情况和计划,请查看:
https://github.com/RTduino/RTduino/discussions/26
首先,你需要到Arduino官方的软件包分类中心去查找你想要的库,或者直接在Github上搜索你想要的库,一般都是C++类型的。比如,我想要一个驱动AHT10温湿度传感器的库,可以在此处下载。此处以潘多拉板为例,因为潘多拉板板载了AHT10传感器。
下载好之后,直接将zip压缩包拖进RTduino文件夹下的libraries\user
这个目录下即可。选择当前工程右键选择Sync Sconscript to project也就是让RT-Studio重新扫描并组织一遍工程目录,在扫描的过程中,RT-Studio会自动将zip压缩包解压,并按照Arduino IDE的文件添加逻辑(也就是忽略examples文件夹,并将其他文件夹的.c文件和.h路径添加到工程),将Arduino库添加到RT-Thread工程中来。
然后再点一下小锤子按钮来重新编译一下工程。
工程编译通过之后,你可以将这个AHT10 Arduino库的例程(位于该库文件夹下的examples文件夹)直接复制到arduino_main.cpp文件下运行,你可以看到,串口会输出当前的温湿度,Arduino的例程是直接可以在RT-Thread上运行起来的。
在RTduino中内置了两类库,方便用户直接使用。
一类是在Arduino中原生内建(buildin)的库,存放于 libraries/buildin
文件夹内。具体如下表所示:
需要在某个BSP的applications文件夹下创建如下文件、文件夹:
参考示例BSP:STM32F072 Nucleo板applications文件夹 | STM32L475 潘多拉板applications文件夹
该文件是Arduino的编程入口,提供setup和loop函数。在loop函数默认以200ms为周期,闪烁Arduino内建LED灯(LED_BUILTIN)。如果该BSP默认支持SPI功能且为UNO引脚布局,由于SPI和LED_BUILTIN可能存在冲突(D13),可以在loop函数内以 Serial.println("Hello Arduino\n");
代替频闪LED(例如STM32F401 Nucleo板)。
需要在applications文件夹下创建arduino_pinout文件夹,这个文件夹主要包含 arduino_pinout.c
和 arduino_pinout.h
两个关键的文件,这两个文件是对接的关键。用户只需要做好这两个文件,即可完成与RTduino的对接。
同时,这个文件夹内也需要SConscript脚本文件,以及提供Arduino引脚布局的README说明文档。请参照上面的示例BSP来完成对这两个文件的编写。
arduino_pinout.c
内需要完成一个IO编号和功能的映射表。由于Arduino的习惯是采用1-13 (D0-D13) 以及 A0-A5的引脚编号,而正规的MCU的引脚编号一般都是PA1之类,因此需要将MCU真正的引脚编号与Arduino引脚编号映射起来。
以下段代码来举例讲解:
- 1/*
- 2 {Arduino Pin, RT-Thread Pin [, Device Name(PWM or ADC), Channel]}
- 3 [] means optional
- 4 Digital pins must NOT give the device name and channel.
- 5 Analog pins MUST give the device name and channel(ADC, PWM or DAC).
- 6 Arduino Pin must keep in sequence.
- 7*/
- 8/* 按照先数字引脚后模拟引脚的顺序从0开始,一定要按序排列 */
- 9/* 可以按照板卡实际IO情况,灵活调整功能,不一定非得按照Arduino UNO的引脚功能布局,但是建议按此布局设计 */
- 10const pin_map_t pin_map_table[]=
- 11{
- 12 {D0}, /* RX */
- 13 {D1}, /* TX */
- 14 {D2, GET_PIN(A,10)},
- 15 {D3, GET_PIN(B,3), "pwm2", 2}, /* PWM */
- 16 {D4, GET_PIN(B,5)},
- 17 {D5, GET_PIN(B,4), "pwm3", 1}, /* PWM */
- 18 {D6, GET_PIN(B,10), "pwm2", 3}, /* PWM */
- 19 {D7, GET_PIN(A,8)},
- 20 {D8, GET_PIN(A,9)},
- 21 {D9, GET_PIN(C,7), "pwm3", 2}, /* PWM */
- 22 {D10, GET_PIN(B,6), "pwm16", 1}, /* PWM */
- 23 {D11, GET_PIN(A,7), "pwm17", 1}, /* PWM */
- 24 {D12, GET_PIN(A,6)},
- 25 {D13, GET_PIN(A,5)},
- 26 {D14}, /* I2C1-SDA */
- 27 {D15}, /* I2C1-SCL */
- 28 {D16, GET_PIN(C,13)}, /* user button */
- 29 {A0, GET_PIN(A,0), "adc1", 0}, /* ADC */
- 30 {A1, GET_PIN(A,1), "adc1", 1}, /* ADC */
- 31 {A2, GET_PIN(A,4), "adc1", 4}, /* ADC */
- 32 {A3, GET_PIN(B,0), "adc1", 8}, /* ADC */
- 33 {A4, GET_PIN(C,1), "adc1", 11}, /* ADC */
- 34 {A5, GET_PIN(C,0), "adc1", 10}, /* ADC */
- 35}
如上截取展示了IO编号和功能映射表,每一行用花括号包裹(实际是一个结构体)来建议一个IO的映射关系:
1{Arduino引脚编号, RT-Thread引脚编号(通过GET_PIN宏获取), 复用功能的设备名(PWM、ADC或DAC), 该复用功能设备的通道号}
其中,Arduino引脚编号,即是第一个参数,是必填的,D0 - Dx 或者是 A0 - Ax。注意一定要按先数字引脚后模拟引脚照顺序来填写。
RT-Thread引脚编号,即第二个参数,rt_pin_write中引脚编号填什么,这里就填什么,一般使用 GET_PIN
宏来获取。注意:D0、D1以及I2C、SPI IO需要将此参数略过。
后两个参数是复用功能IO才需要填写的,普通引脚只需要略过即可。
参考示例BSP:STM32L475 潘多拉板applications文件夹
该文件主要负责定义各种宏,包括:
D0、A0等引脚的数字宏,该宏一定要按照先数字引脚后模拟引脚的顺序进行排号。
定义默认开启的一些硬件功能,如下表所示:
- 1/* pins alias. Must keep in sequence */
- 2/* 按照先数字引脚后模拟引脚的顺序从0开始,一定要按序排列 */
- 3/* 可以按照板卡实际IO情况,灵活调整功能,不一定非得按照Arduino UNO的引脚功能布局,但是建议按此布局设计 */
- 4#define D0 (0)
- 5#define D1 (1)
- 6#define D2 (2)
- 7#define D3 (3)
- 8#define D4 (4)
- 9#define D5 (5)
- 10#define D6 (6)
- 11#define D7 (7)
- 12#define D8 (8)
- 13#define D9 (9)
- 14#define D10 (10)
- 15#define D11 (11)
- 16#define D12 (12)
- 17#define D13 (13)
- 18#define D14 (14)
- 19#define D15 (15)
- 20#define D16 (16)
- 21#define D17 (17)
- 22#define D18 (18)
- 23#define D19 (19)
- 24#define D20 (20)
- 25#define D21 (21)
- 26#define D22 (22)
- 27#define D23 (23)
- 28#define D24 (24)
- 29#define D25 (25)
- 30#define D26 (26)
- 31#define D27 (27)
- 32#define D28 (28)
- 33#define D29 (29)
- 34#define D30 (30)
- 35#define D31 (31)
- 36#define D32 (32)
- 37#define A0 (33)
- 38#define A1 (34)
- 39#define A2 (35)
- 40#define A3 (36)
- 41#define DAC0 (37)
- 42
- 43#define F_CPU 80000000L /* CPU: 80MHz,定义CPU的主频 */
- 44#define LED_BUILTIN D22 /* Default Built-in LED,定义Arduino内置LED的引脚编号 */
- 45
- 46/* 定义I2C设备名称,在使用Wire库时会直接调用。可选,如果没有I2C功能,不需要定义该宏 */
- 47#define RTDUINO_DEFAULT_IIC_BUS_NAME "i2c4"
- 48
- 49/* 定义SPI设备名称,在使用SPI库时会直接调用。可选,如果没有SPI功能,不需要定义该宏 */
- 50#define RTDUINO_DEFAULT_SPI_BUS_NAME "spi2"
- 51
- 52/*
- 53 定义高精度定时器设备名称,该宏主要是提供us时基信号使用。
- 54 所有Cortex-M核MCU均不需要定义此宏,RTduino会自动调用systick来计算us级时间戳。
- 55 非Cortex-M核的MCU需要提供一个硬件定时器来提供us级时间戳。
- 56 */
- 57#define RTDUINO_DEFAULT_HWTIMER_DEVICE_NAME "timer7"
- 58
- 59/* 如果有串口2、串口3可以定义串口2、3的设备名称,若没有可直接不定义此宏 */
- 60#define RTDUINO_SERIAL2_DEVICE_NAME "uart2"
Kconfig文件位于BSP的board文件夹下:
参考示例BSP:
STM32F072 Nucleo板Kconfig | STM32L475 潘多拉板Kconfig
- 1menu "Onboard Peripheral Drivers"
- 2 config BSP_USING_STLINK_TO_USART
- 3 bool "Enable STLINK TO USART (uart2)"
- 4 select BSP_USING_UART
- 5 select BSP_USING_UART2
- 6 default y
- 7
- 8 #增加 BSP_USING_ARDUINO 配置选项
- 9 config BSP_USING_ARDUINO
- 10 bool "Support Arduino"
- 11 select PKG_USING_RTDUINO
- 12 select BSP_USING_STLINK_TO_USART
- 13 select BSP_USING_GPIO
- 14 select BSP_USING_ADC
- 15 select BSP_USING_ADC1
- 16 select BSP_USING_PWM
- 17 select BSP_USING_PWM2
- 18 select BSP_USING_PWM2_CH2
- 19 select BSP_USING_PWM2_CH3
- 20 select BSP_USING_PWM3
- 21 select BSP_USING_PWM3_CH1
- 22 select BSP_USING_PWM3_CH2
- 23 select BSP_USING_PWM16
- 24 select BSP_USING_PWM16_CH1
- 25 select BSP_USING_PWM17
- 26 select BSP_USING_PWM17_CH1
- 27 select BSP_USING_I2C
- 28 select BSP_USING_I2C1
- 29 imply RTDUINO_USING_SERVO
- 30 imply RTDUINO_USING_WIRE
- 31 imply RTDUINO_USING_ADAFRUIT
- 32 default n
- 33
- 34endmenu
需要在Onboard Peripheral Drivers
栏下增加 BSP_USING_ARDUINO
配置选项,并依赖相应的PWM、ADC、UART、I2C以及SPI等设备框架,满足一键化开启RTduino的能力。
示例:STM32F072 Nucleo的Arduino引脚布局说明文档 | STM32L475潘多拉的Arduino引脚布局说明文档
该文档需位于applications/arduino_pinout/README.md
,主要介绍该BSP下的Arduino引脚编号和引脚功能,以及注意事项等。
setup()
和 loop()
函数是Arduino编程中非常经典的函数,当你在Arduino IDE中新建一个文件时,默认就提供了这两个函数。这两个函数RTduino是完全支持的(参见第4章),但是一些较为复杂或庞大的业务逻辑如果放在setup-loop函数中就会受到一些束缚。因此,可以在Env或者RT-Thread Studio的RT-Thread Settings中取消setup-loop编程模型:
- 1RT-Thread online packages --->
- 2 system packages --->
- 3 [*] RTduino: Arduino Ecological Compatibility Layer --->
- 4 [*] Don't use setup-loop structure --->
选择此选项后,用户可以直接在 core/arduino_thread.c
中的 arduino_entry
线程函数中直接编程,或者在任意.cpp文件中调用Arduino API(不局限于Arduino线程,只要是.cpp文件下调用即可)。
通过上文,我们可以看到,RTduino软件包并不是直接可以用的,需要RT-Thread BSP方面提供一些配套的支持,如引脚映射表(arduino_pinout)等。但是,如果用户不想使用Arduino引脚(IO)相关的API(如analogRead等),只想借助RTduino软件包,来直接兼容运行I2C芯片驱动库、纯软件算法库等和IO无关的函数和库,如何快速的使用起来呢?
用户可以直接在Env或者RT-Thread Studio的RT-Thread Settings中选择精简模式 (Enable tiny mode)。在选择精简模式后,用户就无需定义引脚映射表,直接就可以使用Arduino库中的非IO相关的函数和库了。开启精简模式后,会自动开启 5.1 章节所介绍的不使用setup-loop编程模型选项,用户可以在任意.cpp文件下使用Arduino API。
- 1RT-Thread online packages --->
- 2 system packages --->
- 3 [*] RTduino: Arduino Ecological Compatibility Layer --->
- 4 -*- Don't use setup-loop structure
- 5 [*] Enable tiny mode --->
下表列举了在两种不同模式下,RTduino对Arduino API的兼容情况:
注:SPI库目前还在施工中,暂不开放使用。
调用到Arduino相关函数和宏的源文件,请包含Arduino.h头文件,否则可能会报错:
如果使用Keil AC5环境,需要勾选GNU extension。AC6不需要。
- 1void setup() {
- 2 //Declaring LED pin as output
- 3 //pinMode(led_pin, OUTPUT); //不能设置为OUTPUT,否则PWM会失效
- 4}
- 5void loop() {
- 6 //Fading the LED
- 7 for(int i=0; i<255; i++){
- 8 analogWrite(led_pin, i);
- 9 delay(5);
- 10 }
- 11 for(int i=255; i>0; i--){
- 12 analogWrite(led_pin, i);
- 13 delay(5);
- 14 }
- 15}
因为底层已经将对应的PWM、ADC或DAC的IO设置为模拟输入或者复用推挽,调用pinMode之后把IO模式改成了纯输入输出,原有的PWM、ADC或DAC功能将无法使用。该问题无要修正,只需要知道调用analogRead和analogWrite的时候不需要设置pinMode即可。一旦调用pinMode,该引脚将丧失analogWrite或者analogRead功能,后续只能当做普通IO使用。
Arduino 官方文档也是这么建议的:
- 1You do not need to call pinMode() to set the pin as an output before calling analogWrite().
- 2The analogWrite function has nothing to do with the analog pins or the analogRead function.
用户如果对PWM、ADC或DAC引脚使用pinMode函数,在终端也会给出警告:
当然,如果用户已经知道这样做的后果,但是故意需要将PWM、ADC或DAC引脚通过pinMode函数转为普通IO也是完全可以的。
在很多Arduino例程中,都喜欢使用如下语句来初始化串口:
1 Serial.begin(9600);
这句话将串口默认初始化成波特率为9600. 但是在RT-Thread中,串口的初始化实际是有RT-Thread驱动框架负责的,并且默认波特率为115200. 因此如果调用Serial.begin(9600)
函数后,串口的波特率将会从默认的115200调整为9600。如果你的终端或者串口助手还保持在115200的波特率,那么接收数据将出现乱码。
因此建议:
使用Serial.begin()
代替Serial.begin(9600)
。Serial.begin()
无参数方法是RTduino的扩充方法,其表示跟随使用RT-Thread串口配置,不重新配置串口。
https://github.com/RTduino/RTduino
https://gitee.com/rtduino/RTduino
查看全部名单请上GitHub
END
爱我就给我点在看
点击 “阅读原文”进入本项目