综述
WRTnode2R最大的特征便是双核双系统:MTK7688+STM32F103T8U6以及它们对应的操作系统OpenWrt+RT-Thread。
我们在OpenWrt之外引入MCU主要有两个考虑:
- 完善OpenWrt的可用资源,如:模拟采集、定时器的捕获、PWM的输入,以及更多的PWM输出,更多的GPIO和其他输出
- 具备可靠的“硬”实时能力,我们知道在Linux中无法承载毫秒级的精确延时,而MCU则正好擅长此任务
由此,我们希望嫁接OpenWrt所带来的Linux广泛的现有软件资源和多媒体、人工智能应用的能力,同时STM32核心提供了更底层的开发能力和更加可靠的物理交互能力,我们希望两者的结合能够在类机器人应用领域提供更广泛的应用场景。
在OpenWrt中调用Arduino函数:对STM32系统的定制
而引入双核双系统特征后,对STM32资源和API的组织,变成了一个新课题。
基于我们对Linux+MCU构架的开发体会,我们设计了如下的构架:
在WRTnode2R核心板上,STM32和7688之间通过SPI总线连接,占用7688的CS1引脚。在WRTnode2R标准底板上,引出STM32的JTAG调试引脚、串口、I2C以及其他未使用的所有GPIO引脚。具体引脚布局图,详见WRTnode2R针脚定义和底板针脚定义
使用方法
在OpenWrt中调用STM32
WRTnode2R的STM32上运行着RT-Thread实时操作系统,受益于RTT中友好的msh命令行调试组件,我们在出厂固件中实现了RTT的spi
slave设备驱动,并且将msh的输入输出端口都映射到了spi总线上。同时我们提供了在7688上与STM32通过SPI总线通信的软件(包括spiS0设备驱动、spi-bridge通信软件和flash-stm32固件升级程序)。
体验stm32上的msh:
-
登录到WRTnode2R上。(登录的方式有串口、telnet、ssh)
- 升级stm32固件
本文档主要是针对最新版本的stm32固件做的一些阐述,所以建议大家更新stm32固件。
$ wget http://d.wrtnode.com/2R-stm32/flash-stm32 -O /usr/bin/flash-stm32 $ flash-stm32
注:如果flash-stm32后面不跟参数,会自动下载并请求升级最新的stm32固件;flash-stm32加上指定版本的固件(服务器上的或本地的版本),请求升级指定版的stm32固件。升级完以后stm32会自动重启。
root@OpenWrt:~# flash-stm32 --help --------------------------------------------------------------------------- Usage: flash-stm32 Example #1: Updated to the latest version flash-stm32 Example #2: Update to the specified version(server or local) flash-stm32 http://d.wrtnode.com/2R-stm32/$Stm32Firmware or flash-stm32 /tmp/$Stm32Firmware --------------------------------------------------------------------------- root@OpenWrt:~# flash-stm32 Download the latest version form http://d.wrtnode.com/2R-stm32/update Connecting to d.wrtnode.com (66.33.205.160:80) - 100% |********************************************************************************************| 5 0:00:00 ETA Connecting to d.wrtnode.com (66.33.205.160:80) WRTnode2r_STM32_rtth 100% |********************************************************************************************| 30404 0:00:00 ETA The latest version is 0.7 No gets to the current STM32 version. Ple input "Y" to continue to upgrade or input "N" to cancel upgrade! input Y or N:Y write ... ###############################################################################################write end! check ... ###############################################################################################check end! restart ... restart end root@OpenWrt:~# |
-
输入spi-bridge即可打开和stm32的msh命令行终端,先按下tab键再按下enter键列出msh支持的所有命令。
$ spi-bridge
-
若要执行某一条命令,输入命令后按下enter即可执行。如图:
-
exit退出msh。
如果要单命令方式执行,亦可以通过spi-bridge来实现,仅需要输入spi-bridge write [cmd]即可。如果stm32正在执行之前的指令,导致写缓存满的话,此命令将阻塞,直至可以执行。
如果要读取命令执行的结果,输入spi-bridge read即可。如果stm32读数据缓存为空,则命令会阻塞,直至有数据可读。
[注]由于7688芯片上SPI总线的特殊性,现阶段暂时未能给用户提供标准的SPI CS1的操作接口,所以用户暂时只能通过spi-bridge进行通信。
$ spi-bridge write list_timer $ spi-bridge read
Arduino函数的兼容:maple
WRTnode2R的STM32上已经移植好了Maple固件库。Maple固件库为使用stm32的用户提供了兼容Arduino代码的接口。用户如果要移植Arduino上的代码到WRTnode2R的stm32上,需要做一点简单的移植工作即可。
[注]因为Arduino的应用工程均为C++代码,但是WRTnode2R的stm32上运行的rtt是在c编译器下编译执行的,所以在移植工程时必然需要做一点移植工作。我们现有的工程中,已经将maple库的所有接口增加了一层c语言的封装,让用户可以在c代码中直接调用,所以用户可以选择将Arduino的应用代码用C语言重构,或者将应用代码增加一层C语言的封装,供RTT调用。
通过spi-bridge对STM32的IO口进行控制
当前版本已经为用户提供了msh调用Maple的GPIO库的命令,这里为大家提供了一个参考例程。
控制IO语句的使用十分简单,与arduino相仿,目前可以使用的语句包括pinMode/digitalRead/digitalWrite/togglePin/analogRead/pmwWrite
本文主要演示pinMode、digitalWrite及pwmWrite的用法。
先看针脚的定义:
extern const stm32_pin_info PIN_MAP[BOARD_NR_GPIO_PINS] = { PMAP_ROW(GPIOA, 0, TIMER2, 1, ADC1, 0), /* D0/PA0 */ PMAP_ROW(GPIOA, 1, TIMER2, 2, ADC1, 1), /* D1/PA1 */ PMAP_ROW(GPIOA, 8, TIMER1, 1, NULL, ADCx), /* D2/PA8 */ PMAP_ROW(GPIOB, 2, NULL, 0, NULL, ADCx), /* D3/PB2 */ PMAP_ROW(GPIOA, 3, TIMER2, 4, ADC1, 3), /* D4/PA3 */ PMAP_ROW(GPIOB, 0, TIMER3, 3, ADC1, 8), /* D5/PB0 */ PMAP_ROW(GPIOA, 11, TIMER1, 4, NULL, ADCx), /* D6/PA11 */ PMAP_ROW(GPIOA, 12, NULL, 0, NULL, ADCx), /* D7/PA12 */ PMAP_ROW(GPIOB, 5, NULL, 0, NULL, ADCx), /* D8/PB5 */ PMAP_ROW(GPIOA, 10, TIMER1, 3, NULL, ADCx), /* D9/PA10 */ PMAP_ROW(GPIOA, 9, TIMER1, 2, NULL, ADCx), /* D10/PA9 */ PMAP_ROW(GPIOB, 6, TIMER4, 1, NULL, ADCx), /* D11/PB6 */ PMAP_ROW(GPIOB, 7, TIMER4, 2, NULL, ADCx), /* D12/PB7 */ PMAP_ROW(GPIOB, 1, TIMER3, 4, ADC1, 9), /* D13/PB1 */ PMAP_ROW(GPIOA, 2, TIMER2, 3, ADC1, 2), /* D14/PA2 */ PMAP_ROW(GPIOA, 4, NULL, 0, NULL, ADCx), /* D15/PA4 */ PMAP_ROW(GPIOA, 5, NULL, 0, NULL, ADCx), /* D16/PA5 */ PMAP_ROW(GPIOA, 6, NULL, 0, NULL, ADCx), /* D17/PA6 */ PMAP_ROW(GPIOA, 7, NULL, 0, NULL, ADCx), /* D18/PA7 */ }; |
注释/* D0/PA0 */ /* D5/PB0 */等,Dxx就代表的xx引脚,Pxx就是对应芯片的引脚(也就是对应WRTnode2R标准底板丝印上的引脚);比如,下面我操作的是3引脚(D3),对应底板上的PB2。
其中支持PWM的PIN为0, 1, 2, 4, 5, 6, 9, 10, 11, 12, 13, 14
支持ADC的PIN为0, 1, 4, 5, 13, 14
其中D15-D18引脚已经被spi占用
完整的定义在github上可以找到:
https://github.com/WRTnode/wrtnode2r_stm32/blob/master/maple/wirish/boards/wrtnode2/board.cpp#L67
root@OpenWrt:~# spi-bridge RT-Thread shell commands: reset - reset to bootloader. 2r_version - get wrtnode2r stm32 version. pwmWrite - pwmWrite pinNum duty_cycle. analogRead - analogRead pinNum. togglePin - togglePin pinNum. digitalWrite -digitalWrite pinNum 0x1/0x0. digitalRead - digitalRead pinNum. pinMode - pinMode pinNum mode. list_device - list device in system list_timer - list timer in system list_mempool - list memory pool in system list_mutex - lst mutex in system list_event - list event in system list_sem - list semaphore in system list_thread - list thread version - show RT-Thread version information help - RT-Thread shell help. free - Show th memory usage in the system. time - Execute command with time. ps - List threads in the system. msh > msh > pinMode 3 OUTPUT //pinMode第一个参数指引脚3;第二个参数设置引脚3状态为OUTPUT ;pinMode没有返回值,命令执行之后直接按下enter键即可 msh > digitalWrite 3 HIGH //digitalWrit第一个参数指引脚3;第二个参数设置引脚3的电平为高 msh > digitalWrite 3 LOW //digitalWrit第一个参数指引脚3;第二个参数设置引脚3的电平为低 |
pinMode用于设置管脚的属性(输入还是输出),命令格式为
pinMode pinNum mode
mode可以数字或以下关键词
OUTPUT OUTPUT_OPEN_DRAIN INPUT_ANALOG INPUT_PULLUP INPUT_PULLDOWN INPUT_FLOATING PWM PWM_OPEN_DRAIN
完整的定义在github上可以找到:
https://github.com/WRTnode/wrtnode2r_stm32/blob/master/applications/application.c#L66
digitalWrite用于控制输出管脚的输出电平,命令格式如下:
digitalWrite pinNum HIGH/LOW
再来介绍pwmWrite命令,命令格式如下:
pwmWrite pinNum duty_cycle
其中,duty_cycle的取值范围是0~65535,占空比就是duty_cycle/65535
当然,首先要将管脚设置为PWM模式,通过以下命令
$ pinMode 0 PWM
这里我使用逻辑分析仪来观察管脚上的PWM波形
首先将占空比设置为50%,运行以下命令
$ pwmWrite 0 32768
逻辑分析仪上采集到波形如下:
可以看到产生了频率是550Hz,占空比是50%的PWM波形
再次运行如下命令
$ pwmWrite 0 10000
逻辑分析仪上采集到波形如下:
可以看到产生了15.62%占空比的波形,与10000/65535相符。
可以看到,随着STM32上系统的不断完善,目前已经可以利用openwrt中的spi-bridge进行GPIO的控制了。
在脚本语言中调用Arduino函数与STM32交互
用0、1、2、6引脚(PA0、PA1、PA8、PA11)做跑马灯
#!/bin/sh spi-bridge write "pinMode 0 0" spi-bridge write "pinMode 1 0" spi-bridge write "pinMode 2 0" spi-bridge write "pinMode 6 0" while true do spi-bridge write "digitalWrite 0 1" sleep 1 spi-bridge write "digitalWrite 0 0" spi-bridge write "digitalWrite 1 1" sleep 1 spi-bridge write "digitalWrite 1 0" spi-bridge write "digitalWrite 2 1" sleep 1 spi-bridge write "digitalWrite 2 0" spi-bridge write "digitalWrite 6 1" sleep 1 spi-bridge write "digitalWrite 6 0" done |
运行效果如下:
代码下载及编译
WRTnode2R上STM32的全部代码均开源,用户可以任意下载。同时也欢迎向代码库提交代码。
Github repo:
https://github.com/WRTnode/wrtnode2r_stm32
有些用户希望能够在usart1上使用msh,而不是通过7688的spi,所以我们提供了将msh映射到usart1的branch,代码库如下:
https://github.com/WRTnode/wrtnode2r_stm32/tree/uart-msh
理论上,我们将会同步更新两个branch的代码,但是如果有一些冲突时,以master分支为主。
我们在使用rtthread时对rtt主线版本代码做了一小部分修改,并且已经向RTT提出了pull request,但是rtt在选择是否merge会有一定延时,所以我们建议下载我们fork出的rtt的代码,我们也会积极的rebase Rtt主线代码。
https://git.oschina.net/SchumyHao/rt-thread.git
编译流程:
首先安装python scons gcc-arm-none-eabi(4.8 or above),然后执行以下代码:
$ mkdir wrtnode2r-stm32 $ cd wrtnode2r-stm32 $ git clone https://github.com/WRTnode/wrtnode2r_stm32 wrtnode2r_stm32 $ git clone https://git.oschina.net/SchumyHao/rt-thread.git rtt $ cd wrtnode2r_stm32 设置gcc路径: $ vi rtconfig.py $ scons
编译成功后,生成的rtthread.bin即为stm32的固件。
固件更新
在WRTnode2R的stm32上,为了实现在7688上更新stm32的固件,我们在stm32的前4k地址烧写了一个bootloader程序,同时在7688上也提供了刷写固件的程序flash-stm32。
如果使用flash-stm32,用户仅需要执行
flash-stm32 URL
URL这个命令行参数可以是stm32固件的链接,也可以是指定的本地stm32系统;就可以将存在服务器上的固件或者本地的固件烧写到stm32中。
如果用户希望进行stm32的开发,我们建议用户使用stlink等在线调试工具来开发、调试和烧写代码。在开发前只需要注意点三:
1.用户开发的代码中要将中断向量表重定向到0x08001000位置。
2.用户代码下载时,要下载到0x08001000位置。
3.如果用户完全抛弃我们提供的代码进行开发,但是又希望能够支持在线更新固件的功能,需要在用户开发的代码中实现7688控制stm32复位的功能。(在我们提供的代码中,已经实现了reset这个cmd,所以基于我们提供的代码进行开发,不需要完成这一步)
以上部分文档来自http://bbs.elecfans.com/jishu_529687_1_1.html,非常感谢disorder及电子发烧友论坛的支持。