Rust + MicroBit 按钮调试代码

#![no_main]
#![no_std]
use panic_halt;
use microbit::hal::nrf51::{interrupt, GPIOTE, UART0};
use microbit::hal::prelude::*;
use microbit::hal::serial;
use microbit::hal::serial::BAUD115200;
use cortex_m::interrupt::Mutex;
use cortex_m::peripheral::Peripherals;
use cortex_m_rt::entry;
use core::cell::RefCell;
use core::fmt::Write;
use core::ops::DerefMut;
static GPIO: Mutex<RefCell<Option<GPIOTE>>> = Mutex::new(RefCell::new(None));
static TX: Mutex<RefCell<Option<serial::Tx<UART0>>>> = Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
    if let (Some(p), Some(mut cp)) = (microbit::Peripherals::take(), Peripherals::take()) {
        // 引入两个版本的外设
        cortex_m::interrupt::free(move |cs| {
            /* 开启外部设备的 GPIO 中断 */
            cp.NVIC.enable(microbit::Interrupt::GPIOTE);
            microbit::NVIC::unpend(microbit::Interrupt::GPIOTE);
            /* 切分 GPIO 口,方便使用 */
            let gpio = p.GPIO.split();
            /* 将 Button 的 IO 口作为输入 IO 口 */
            let _ = gpio.pin26.into_floating_input();
            let _ = gpio.pin17.into_floating_input();
            /* 当 GPIO 17 ( A 键)出现了下降沿的时候,触发中断 */
            p.GPIOTE.config[0]
                .write(|w| unsafe { w.mode().event().psel().bits(17).polarity().hi_to_lo() });
            p.GPIOTE.intenset.write(|w| w.in0().set_bit());
            p.GPIOTE.events_in[0].write(|w| unsafe { w.bits(0) });
            /* 当 GPIO 26 (B键)出现了下降沿的时候,触发中断 */
            p.GPIOTE.config[1]
                .write(|w| unsafe { w.mode().event().psel().bits(26).polarity().hi_to_lo() });
            p.GPIOTE.intenset.write(|w| w.in1().set_bit());
            p.GPIOTE.events_in[1].write(|w| unsafe { w.bits(0) });
            *GPIO.borrow(cs).borrow_mut() = Some(p.GPIOTE);
            /* 根据需要,设置 GPIO 口作为输入输出口 */
            let tx = gpio.pin24.into_push_pull_output().downgrade();
            let rx = gpio.pin25.into_floating_input().downgrade();
            /* 将准备的 GPIO 口作为串口来使用 */
            let (mut tx, _) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
            let _ = write!(
                tx,
                "\n\rWelcome to the buttons demo. Press buttons A and/or B for some action.\n\r",
            );
            *TX.borrow(cs).borrow_mut() = Some(tx);
        });
    }
    loop {
        continue;
    }
}
// 定义一个中断(如果函数出现了错误,就会触发中断),当我从按钮按下接收到了一个中断,这个函数就会被调用。
#[interrupt]
fn GPIOTE() {
    /* 进入中断内部的内容 */
    cortex_m::interrupt::free(|cs| {
        if let (Some(gpiote), &mut Some(ref mut tx)) = (
            GPIO.borrow(cs).borrow().as_ref(),
            TX.borrow(cs).borrow_mut().deref_mut(),
        ) {
            let buttonapressed = gpiote.events_in[0].read().bits() != 0; // 识别出 A 键按下
            let buttonbpressed = gpiote.events_in[1].read().bits() != 0; // 识别出 B 键按下
            /* 将按钮打印到串口中 */
            let _ = write!(
                tx,
                "Button pressed {}\n\r",
                match (buttonapressed, buttonbpressed) {
                    (false, false) => "",
                    (true, false) => "A",
                    (false, true) => "B",
                    (true, true) => "A + B",
                }
            );
            /* 清空事件 */
            gpiote.events_in[0].write(|w| unsafe { w.bits(0) });
            gpiote.events_in[1].write(|w| unsafe { w.bits(0) });
        }
    });
}

Code language: PHP (php)

Micro:bit 收信强度测试

本代码用于测试不同信号强度下的发信距离。

7c3v6
radio.onReceivedNumber(function (receivedNumber) {
    basic.showNumber(radio.receivedPacket(RadioPacketProperty.SignalStrength))
})
input.onButtonPressed(Button.A, function () {
    basic.showString("A")
})
input.onButtonPressed(Button.B, function () {
    basic.showString("B")
})
basic.showIcon(IconNames.Yes)
Code language: JavaScript (javascript)

如何使用 MakeCode 来进行 micro:bit 的串口调试

micro:bit 支持在串口输出一些信息,并在你的调试工具中展示这些数据。

这个时候,你可以直接使用 MakeCode 自带的 WebUSB 的功能来实行这个操作。

如何使用?

想要使用串口调试非常方便,首先,你需要确保你的 micro:bit 可以使用 WebUSB (固件版本为 249,250及以上),然后,在你的 make:code 中使用设备配对的功能,来进行配对,将你的 micro:bit 与编辑器进行配对。

4z0uv

然后,新增一个串口输出字符串的块

添加完成块以后,将代码烧入到你的 micro:bit 中,然后在左侧会看到提示。

bgc1a

我这里只显示了“模拟器”,你链接了设备以后,这里还会显示设备的提醒,点击进去就可以看到控制台的输出了。

27s22

Micro:Bit 是如何将 600K + 的文件烧入 NRF51822 的?

前置信息

Micro:bit 的蓝牙芯片是 NRF51822

根据 Nodric Semiconductor 公司的说明,NRF51822 的 ROM 大小只有 256/128 K,但是当我们从 makecode 编辑器上下载 hex 文件的时候,即使只是输出一个 1 ,你的代码文件也有足足 630K。

2pqbu

在我的 micro:bit 还没有到手的时候,我一直在怀疑,make:code 是使用了什么样的黑科技,才能够实现将 630K 的文件烧录到 256K 的 ROM 里。我甚至怀疑 micro:bit 根本无法烧录这个文件。

micro:bit 到手以后,我试着去烧录,发现虽然文件大,但是的的确确可以烧录进去。

当时我就震惊了!Micro:bit 这是掌握了核心科技啊,能够把 600K + 的文件烧录进 256K 的 ROM 里。

随着研究的深入,以及开始对 hex 文件进行解包以后。我发现,原来 hex 文件中包含了整个项目的所有的文件,我们看到的 ts 文件等都会打包在这个文件中。

这就更加令我震惊了,难道 Micro:bit 已经可以实现在 16K Ram ,256K Rom 的芯片上移植一个 Node.js 的运行环境?那这个公司的黑科技未免也太多了。而且,因为要烧录整个文件进去,600K+的文件再怎么压缩也要占个100~200K,剩下给运行环境的空间并不大。

继续研究,发现我可以在本地重新打包,于是,我尝试重新打包文件

pxt build 

打包完成后,生成了 built 文件夹,里面就是我们的 hex 文件。

9jetw

诶,这里怎么还有一个 asm 文件? asm 不是汇编语言 assembly language 的缩写么?打开一看,的确是汇编语言。

再看一下文件大小,豁然开朗。

ifbry

原来, Makecode 将整个项目文件都打包进入到 package 中,然后,将代码文件转译为汇编语言文件(实际应该是转成二进制可执行文件,这里的 asm 文件应该是用于调试的),写入到 nrf51822 中。

这样,我们实际写入的代码其实只有 24 K,就能够理解为什么 600 多K 的项目能够写入到 nrf51822 了,因为其实 其中的 600 k 都是源码文件,在烧录的时候会被抛弃掉。

如何在本地运行一个 Micro:bit 编辑器

006tKfTcgy1g0lrdvr8bgj31j30u0n4c
micro:bit makecode在线编辑器

一般来说,我们会使用 micro:bit 的 makecode 平台进行内容的编辑,不过,有些时候,我们希望在本地进行编辑,因此, 我们需要把 编辑器运行在本地。

在本地运行编辑器的方法非常的简单,首先,确保你已经安装了 Node.js 环境,然后执行如下命令安装 pxt

npm install pxt

安装完成后,执行如下命令来启动编辑器

mkdir microbit # 创建一个新的目录,方便管理文件
pxt target microbit # 创建 micro:bit 目录
pxt serve # 启动编辑器
Code language: PHP (php)

启动后,会自动打开浏览器,访问编辑器。如果没有启动,你可以点击命令行里的连接,自己打开编辑器。

006tKfTcgy1g0lrw3191bj31pa0r0ajq
连接位置

打开以后,就可以看到在本地运行的 makecode 编辑平台了。

006tKfTcgy1g0lrwonaqpj31h80u012f
makecode 编辑平台

如何解包 Micro:bit 可视化编辑器生成的 Hex 文件

在上一篇文章《Micro:Bit 中隐藏的 Typescript 文件》中我提到,micro:bit 的代码中有一些 Typescript 文件,记录了我们的接口。如果我们可以将其下载到本地,完整的解读,那么我们可以得到一个更加完善的接口文档。接下来,我们就对编辑器生成的 Hex 文件进行解包。

Hex 解包过程

生成 Hex 文件

首先,你需要生成一个 用于解包的 Hex 文件,这样可以方便你看自己想看的接口。比如我要看的蓝牙接口默认并不在项目里,需要自己以拓展的形式加入到项目中。

安装 PXT 命令行工具

想要解包,你需要使用 micro:bit 自己的解包工具(集成在其命令行里)

我们需要使用 npm 来安装 pxt 命令行工具,所以,在继续执行下面的命令前,使用 node --version来确定你已经装了 node.js 环境。

npm install pxt

安装完成后,创建一个新的文件夹,比如叫 extract ,并安装 micro:bit 解包所需的依赖

cd extract
pxt target microbit

解包 Hex 文件

当环境好了以后,我们就可以开始准备解包我们的 Hex 文件了。

pxt extract 文件路径

解压完成后,会在文件路径下生成一个项目文件夹

006tKfTcgy1g0lr66a24sj31160o0gon
解压所得文件

这样,你就可以看到我们在在线编辑器中所看到的文件了。

Micro:bit 中隐藏的 TypeScript

Micro:Bit 目前提供的可视化编程是基于 JavaScript 实现的,如果你将你的界面从 Block 块编辑器切换为 JavaScript 编辑器,就可以看到对应的代码了。

jwb4j
micro:bit 中的 JavaScript

除了 JavaScript 以外,你还可以在 Micro:Bit 中找到 TypeScript 代码。

点击右上方的小齿轮,选择项目设定

tld1n
进入项目设定

在页面的左侧可以看到一个资源管理器,点击资源管理器,就可以看到你的项目中的所有文件。

ugecr
资源管理器

在这些 typescript 中,你可以看到一些我们在使用块时的定义

vqk8a
定义

借助这些 Typescript 文件,相信可以帮助你更好的使用 micro:bit 的编辑器。

Micro:Bit 如何升级固件

在 Micro:Bit 的官方编辑器上,有一个功能:设备配对,这个是依赖于 Chrome 的 Web USB 接口实现的,通过浏览器烧写你的 Micro:Bit 。

设备配对的入口
在线编辑的说明

点击设备配对时,你会看到一个设备,点击就可以配对了。

pllb5
配对界面

不过,并不是每次都可以完成配对的,很多时候,你可能没有办法搜索到。这是因为你的 Micro:Bit 设备依然运行的是旧版的固件,你需要升级一下固件。

升级固件

检测你的固件版本

在升级固件版本之前,你需要先去检测当前的固件版本。你可以使用 USB 数据线,将 Micro:Bit 与你的电脑相连。

连接成功后,你可以在 USB 驱动器中看到 Details.txt 文件

3azja
Details.txt 文件路径

打开这个文件后,你可以看到硬件的版本。

硬件版本

比如,我手上的这个硬件版本为 250 ,是可以使用 WebUSB 的。如果你的版本是0234, 0241, 0243之一,你就需要升级你的版本到最新(此文写于 2018 年 2 月 2日,当日最新版本是 250)。如果你的版本是 249 ,可以不需要升级。

进入维护模式

进入维护模式和进入配对模式一样,也需要按下特殊的键位。你需要在按下 Reset 按钮的同时,插上数据线,这样,你就可以看到在磁盘中多了一个 MAINTENANCE 的驱动器。

006tKfTcgy1g0l8vloftcg30ci08iqv8
进入维护模式

下载最新固件

接下来,你需要去下载最新的固件。你可以在官网的 Firmware 页面找到下载。

006tKfTcgy1g0l8yjnpowj31nm0gejvw
下载最新固件

点击下载,将固件下载到本地,并将其拖入到 MAINTENANCE 驱动器即可。

Micro:Bit 会自动开始刷新固件,等待其重启以后,你就可以在浏览器中下载固件,并进行固件的刷写了。非常的方便。

mb online
在线下载
按钮重新恢复为绿色的时候,就说明下载成功了。

Micro:Bit 蓝牙如何在开机状态进行连接?

如果你使用了 Micro:Bit 的话,你就知道,Micro:Bit 的蓝牙模块有一个默认的 Paring Mode。但是在 Paring Mode 就切换到了另外的模式,并非我们自己写的代码。

0v4z9
micro:bit 结构说明

如何进入 Paring Mode?

首先,需要为你的 Micro:Bit 通电,确保其可以正常运行。

接下来,按下Micro:Bit正面(有 LED 阵列的一面)的 A 键和 B 键,并保持按下的状态。在按下状态的同时,按一下背面的 Reset 按钮,并放开。此时需要确保没有松开 A 键和 B 键。

稍等片刻后,正面的 LED 阵列就会显示 “PAIRING MODE!” 的字样,说明你进入了 Paring Mode。此时,你就可以通过你的 Android/iPhone 手机进行连接了。

如果你想要开发自己的应用程序,我们应该是要求设备不需要进入对应的模式来设置的,毕竟这样操作极为不方便。而且如果你是要做一些自己的硬件,比如小车、机器人的时候,应该是不需要进入特定的模式来进行连接的,毕竟板子会直接装在亚克力的壳子上,极为不方便。

如何解决呢?

很简单,其实在 micro:bit 的在线编辑器中,你可以找到项目设定,打开项目设定

3147u
项目设定

在项目设定中,你可以修改蓝牙的连接模式!

qff2a
三种不同的连接模式

你只需要将模式切换为 “No Paring Required: Anyone Can connect Via Bluetooth”,就可以无序进入到 Paring Mode 了。