因为这是本专栏的第一篇文章,所以我打算先在这里介绍下专栏的写作目标。

Rust 是一种系统编程语言。 它有着惊人的运行速度,能够防止段错误,并保证线程安全。

Rust 官方一直标榜着自己是系统编程语言,然而最根本的系统编程就是嵌入式系统开发。如果不能在嵌入式系统里大施拳脚,那么 Rust 就没有底气能与 C 语言叫板。经过了 3 年迭代,Rust 在嵌入式开发领域已经日渐成型,并且官方也成立了嵌入式工作组特别关注 Rust 嵌入式库与工具链的开发,同时也在不断完善The embedded rust book。这里推荐大家关注工作组的 newsletter,里面有很多工作组最新工作进展。

而本专栏将会更面向于嵌入式开发的入门教程和实践,也就是说,本专栏的文章并不假定读者拥有任何嵌入式开发的知识或经验,但是要求读者有一定 Rust 语言基础,比如说熟悉借用所有权系统,懂得使用 unsafe 手动操作内存结构等等。

专栏文章会分为几大类:

  • 单片机架构的基础知识
  • Rust 嵌入式开发的技巧
  • 各种可以跟着动手的实践项目

希望通过本专栏可以吸引 Rust 小伙伴加入嵌入式领域,<del>同时拐骗一波正在使用 C 语言开发嵌入式的水深火热的程序员。</del>

准备

为了能够自己动手实践嵌入式开发,我们需要先准备好一些材料:

  • STM32F103 最小系统开发板 (约 10 元)
  • STLINK V2 仿真器 (约 20 元)
  • 母对母杜邦线
  • USB 转 TTL 串口模块 (约 5 元)

STM32F103 是现在应用非常广泛,性能强大而且成本低廉的一款单片机,拥有着高达 72Mhz 的主频率,完全吊打 Arduino 等开发平台。

STM32F103 最小系统核心版

仿真器是连接 pc 与单片机的重要模块,主要用于程序烧写与调试。

STLINK V2

串口模块用于 pc 接收单片机的串口信息用以调试,由于现代计算机普遍已经取消了串口接口,所以使用 USB 串口就是最经济可靠的选择。

USB2TTL

Rust 工具链

  1. 文章下面将会使用 nightly-msvc channel 的 Rust 编译器工具链(因为作者使用 Windows 平台开发),读者也可以使用 gnu 或者 linux 平台,步骤上如果有所出入相信使用 linux 的老手是可以自己解决的。
> rustup default nightly-msvc
info: using existing install for 'nightly-x86_64-pc-windows-msvc'
info: default toolchain set to 'nightly-x86_64-pc-windows-msvc'nightly-x86_64-pc-windows-msvc unchanged - rustc 1.32.0-nightly (36a50c29f 2018-11-09)

2. 除了默认的标准库外,我们还需要提前编译好的 core 核心库。在我们这里添加几个常用的编译目标指令集,rustup 就会自动把核心库下载下来。

> rustup target add thumbv6m-none-eabi thumbv7m-none-eabi thumbv7em-none-eabi thumbv7em-none-eabihf
info: downloading component 'rust-std' for 'thumbv6m-none-eabi'
info: downloading component 'rust-std' for 'thumbv7m-none-eabi'
info: downloading component 'rust-std' for 'thumbv7em-none-eabi'
info: downloading component 'rust-std' for 'thumbv7em-none-eabihf'

3. 另外我们还需要一些传统而好用的二进制工具 (binary tool) 和调试器。在 ARM官网页面 下载适合平台的最新版安装即可。这一步安装的工具包括 arm-none-eabi-nm, arm-none-eabi-gdb, arm-none-eabi-objcopy 还有 arm-none-eabi-size 等等。

4. 最后我们还差 openocd,它负责保持与与仿真器的通讯连接,我们需要使用它来进行烧写和调试指令操作。openocd 的安装途径有很多,建议向购买仿真器的商家索要,或者可以从这里下载(可能需要科学上网)。

注: 上述 3,4 步的工具需要加入 Path 环境变量。

Blinky

Blinky 是嵌入式世界的 hello world —— 让一盏 LED 闪烁。这篇文章的最终目标就是把最小系统版上唯一一颗 LED 灯闪烁起来。

我们先创建一个新的项目。

> cargo new blinkyCreated binary (application) `blinky` package

打开 Cargo.toml 添加几个依赖项。

[dependencies]
cortex-m = "0.5.8"        # cortex-m 核心指令集
cortex-m-rt = "0.6.5"     # 最小运行时,负责启动内存初始化
panic-halt = "0.2.0"      # 定义发生 panic 时采取立即停机的行为

同一架构的单片机的内存容量往往有很大差异,不同厂家的内存排布也不一定相同,所以这里我们要用 memory.x 文件里定义开发板的内存结构。在项目目录中新建文件 memory.x 并写入:

MEMORY
{FLASH : ORIGIN = 0x08000000, LENGTH = 128KRAM : ORIGIN = 0x20000000, LENGTH = 20K
}

这里定义了我们这个 MCU 拥有 128k ROM 和 20k RAM,内存起点分别在 0x08000000 和 0x20000000。

memory.x 事实上是一段链接器脚本 (Linker Script),链接器脚本用来在内存中规划如何排布代码和静态变量。很明显仅靠这小段脚本还不足以声明好运行所需的所有段 (SECTION)。幸运的是,cortex-m-rt 运行库已经为我们写好了通用的链接脚本,我们仅仅需要在编译时将名为 memory.x 的内存定义脚本放在编译目录,memory.x 就会被自动 include 到模板中。 所以这里需要一段编译时自动拷贝 memory.x 的 build script。在项目目录中新建文件 build.rs 并写入:

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;fn main() {// Put the linker script somewhere the linker can find itlet out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());File::create(out.join("memory.x")).unwrap().write_all(include_bytes!("memory.x")).unwrap();println!("cargo:rustc-link-search={}", out.display());// Only re-run the build script when memory.x is changed,// instead of when any part of the source code changes.println!("cargo:rerun-if-changed=memory.x");
}

接着打开 src/main,写入:

#![no_std]
#![no_main]extern crate panic_halt;use cortex_m::asm;
use cortex_m_rt::entry;#[entry]
fn main() -> ! {asm::nop();loop { }
}

虽然这段代码看起来毫无作用,但是对于编译来说已经足够了。

可以注意一下这里的 main 函数并不是 Rust 语言内嵌的主函数,事实上,这个主函数仅仅是用户代码的入口,真正的主函数定义在 cortex_m_rt 库中,在启动后负责静态变量和中断向量表的内存初始化,接着才将执行权交回给这里的 main 函数。

执行编译。

> cargo build --target thumbv7m-none-eabiCompiling blinky v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.62s

至此 Blinky 已经成功编译好了,执行文件应该会出现在 /target/thumbv7m-none-eabi/debug/blinky 。接下来我们要把这个程序烧写到芯片的 ROM 上。首先使用杜邦线连接上仿真器与开发板,对应着接口上的名字,应该很容易将四条连接线接好,四个接口分别是 SWDIO, SWCLK, 3.3VGND

接着启动 openocd

> openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg
64-bits Open On-Chip Debugger 0.10.0-dev-00289-g5eb5e34 (2016-09-03-09:40)
Licensed under GNU GPL v2
For bug reports, readhttp://openocd.org/doc/doxygen/bugs.html...Polling target stm32f1x.cpu failed, trying to reexamine
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints

这一步以出现 xxxxx.cpu: hardware has x breakpoints, x watchpoints 提示为连接成功。 如果出现了其他错误,先检查是否已经安装仿真器的驱动,4条连接线有没有松动,或者更换一个 USB 口试试。

保留 openocd 终端,再打开一个新的终端启动 GDB (GNU Debugger) ,使用 GDB 进行执行程序烧写:

> arm-none-eabi-gdb
GNU gdb (GNU Tools for ARM Embedded Processors 6-2017-q1-update) 7.12.1.20170215-git...For help, type "help".
(gdb)

加载目标文件

(gdb) file ./target/thumbv7m-none-eabi/debug/blinky
Reading symbols from ./target/thumbv7m-none-eabi/debug/blinky...done.

连接上 openocd。(openocd 的默认端口为 3333)

(gdb) target remote :3333
Remote debugging using :3333
0x00000000 in ?? ()

重置 MCU,因为在运行状态无法进行烧写。

(gdb) monitor reset halt
stm32f1x.cpu: target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0800016c msp: 0x20000260

开始写入

(gdb) load
Start address 0x0, load size 0
Transfer rate: 0 bits in <1 sec.

写入后 MCU 默认会暂停在初始状态,这里要手动运行。

(gdb) continue
Continuing.

好了到目前为止,如果我们的开发板毫无反应,那就对了,我们现在要给它加上最重要的 Blinky 逻辑。

打开 Cargo.toml 再加上两个依赖

stm32f103xx-hal = { git = "https://github.com/japaric/stm32f103xx-hal.git" }    # MCU 外围部件操作的统一接口
nb = "0.1"    # stm32f103xx-hal 的异步阻塞模块,用来实现时钟等待同步

修改 src/main.rs

#![no_std]
#![no_main]extern crate panic_halt;
extern crate stm32f103xx_hal as hal;
#[macro_use]
extern crate nb;use cortex_m_rt::entry;use hal::prelude::*;
use hal::stm32f103xx;
use hal::timer::Timer;#[entry]
fn main() -> ! {let cp = cortex_m::Peripherals::take().unwrap();let dp = stm32f103xx::Peripherals::take().unwrap();let mut flash = dp.FLASH.constrain();let mut rcc = dp.RCC.constrain();// 设置时钟总线let clocks = rcc.cfgr.freeze(&mut flash.acr);// 设置通用引脚 (GPIO)let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);// LED 对应的 PC13 引脚let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);// 淘宝上有些版本的核心板的 LED 会接在 PB12 引脚上,这样的话用下面两行替换// let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);// let mut led = gpiob.pb12.into_push_pull_output(&mut gpiob.crh);let mut timer = Timer::syst(cp.SYST, 1.hz(), clocks);loop {block!(timer.wait()).unwrap();// 点亮 LEDled.set_high();block!(timer.wait()).unwrap();// 关闭 LEDled.set_low();}
}

重新编译 Rust。

> cargo build --target thumbv7m-none-eabiCompiling blinky v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.1s 

回到 GDB 终端,此时如果还在运行上一段代码,那按下 Ctrl + C 就可以中断执行。

Continuing.
Program received signal SIGINT, Interrupt.
0x08000240 in ?? ()
(gdb) 

直接执行 load 指令,GDB 会自动识别到可执行文件的变更并进行覆写。

(gdb) load
Start address 0x0, load size 0
Transfer rate: 0 bits in <1 sec.
(gdb) continue
Continuing. 

至此,我们的蓝色 LED 就应该会开始以一秒间隔开始闪烁了!


如果你有幸看到这,那就帮忙点个赞,让更多人看到吧!

ubuntu安装 rust nightly_Rust 嵌入式开发环境搭建指南 (一):让世界闪烁吧相关推荐

  1. ubuntu12.04嵌入式开发环境搭建(各种问题、各种配置)

    ubuntu-12.04嵌入式开发环境搭建 先安装ubuntu-16.04  *** 想使用root用户登陆的,操作如下 给root用户设定一个密码 supreme@supreme:~# sudo p ...

  2. Ubuntu 下无人机飞控 ArduPilot 开发环境搭建

    Ubuntu 下无人机飞控 ArduPilot 开发环境搭建 Ubuntu 下无人机飞控 ArduPilot 开发环境搭建 操作流程 升级安装包 安装 git 克隆代码 安装交叉编译工具链 将编译器添 ...

  3. 蓝鲸智云统一开发环境搭建指南

    腾讯官方的<蓝鲸智云统一开发环境搭建指南> 蓝鲸应用统一开发环境指南 一:背景 蓝鲸应用开发需要依赖django,celery,mysql-client等第三方库,而像celery又依赖其 ...

  4. 【教程】Windows下Miniconda+Pytorch+Pycharm开发环境搭建指南

    Windows下Miniconda+Pytorch+Pycharm开发环境搭建指南 给本科生写的新手教程,遇到很多新手的共性问题,可供大家参考.在开始阅读之前,请注意两个要关键:1.为什么要使用Con ...

  5. linux skype 接口,ubuntu /linux下skype api开发环境搭建

    1.安装Skype: 下载skype for linux,地址:http://www.skype.com/download/skype/linux/choose/ 可以下载一个ubuntu 8.10 ...

  6. stm32mp1安装linux系统,stm32mp1linux开发环境搭建(cortex a7)

    原标题:stm32mp1linux开发环境搭建(cortex a7) 写在前面: 本文章为<STM32MP1系列教程之Cortex-A7开发篇>系列中的一篇,全系列总计11篇.笔者使用的开 ...

  7. 黑莓java文件怎么安装程序_黑莓开发环境搭建windows7+eclipse+BlackBerry Java Plug+JDK6...

    最近有点空闲时间,所以就学学黑莓开发,当然要开发第一步就是搭建开发环境,google了一下,有很多教程,但都是很老的了,可能是因为国内开发黑莓的人很少吧.黑莓的好处就是出了名的安全,所以有一批忠实的粉 ...

  8. Ubuntu 12.04下Pomelo开发环境搭建(转)

    关于Pomelo的开发之前一直是在Win7下搞的,最近决定迁移到Ubuntu下,我用的是Ubuntu 12.04,个人认为Ubuntu下搭建Pomelo开发环境,最重要的工作就是安装Nodejs,搞定 ...

  9. #Kinect之路#基于ubuntu 12.04的Kinect开发环境搭建 OpenNI+NITE+AVIN2+qt

    之前基于Win7搭建了kinect的开发环境,由于本人的机子太慢,编一个qt的hello world都要半分钟,所以为了方便以后的开发调试,尝试在ubuntu 12.04下搭建开发环境. 注意,如果你 ...

最新文章

  1. 从原理上搞懂如何设置线程池参数大小?
  2. NPOI 导出 excel 性能测试
  3. [Leedcode][JAVA][第69题][x的平方根][二分查找][数学]
  4. python3.7怎么安装turtle_python3绘图程序教学:载入和查询Turtle模组(一)
  5. java.rmi.ConnectException: Connection refused to host:
  6. 萌新接触前端的第三课——JavaScript
  7. Report Builder:SRW包
  8. 漏洞编号 cve can bugtraq cncve cnvd cnnvd 简介
  9. powerdesigner16 license key过期问题
  10. Java 五子棋 包含强大人机 步骤流程 包含源码
  11. 面对境外网络攻击,作为安全技术人员该如何有效防御?
  12. 大聪明教你学Java | 实现邮件发送(以163邮箱向QQ邮箱发送邮件为例)
  13. 视频剪辑自学怎么入门?借助这款软件可以达到意想不到的效果
  14. 14、守护线程(thread.setDaemon(true))
  15. 3万字干货HTML+CSS入门指南(建议收藏)
  16. Kafka工作原理-数据写入、ACK、查询、消费原理
  17. MATLAB读取中文txt
  18. 阿里云国际站:阿里云发布云数据中心专用处理器
  19. 【效率特工队】如何在很多张JPG图片中,批量获取所有JPG图片的名称提取到Excel表格,用这个方法一并解决
  20. python分配buffer_Python3 Buffered I/O浅析

热门文章

  1. mpi tcp连接报错_关于WinCC与真实PLC之间的TCP/IP连接问题-工业支持中心-西门子中国...
  2. matlab 第二类边界条件,第二类边界条件.ppt
  3. 清华计算机考研笔记,[考研天地]清华计算机,电子,自动化全套考研资料
  4. html 常见hack,针对主流浏览器的CSS-HACK写法及IE常用条件注释
  5. android 置顶窗口位置,Android 滑动悬浮置顶指南
  6. php柱状图html代码,html5生成柱状图(条形图)效果的实例代码
  7. 11旋转编码器原理_旋转编码器的原理是什么?增量式编码器和绝对式编码器有什么区别?...
  8. python中module的意思_python中module的意思
  9. mean项目的分模块开发
  10. Redis与Memocache的区别