点亮一颗 LED: 单片机 ch32v003 (RISC-V) 使用 rust 编写固件

首发日期 2024-04-09, 以下为原文内容:


使用 rust 编写单片机的程序 ? 很新, 但没问题. 使用 RISC-V CPU 的单片机 (比如 ch32v003) ? 也没问题. 同时使用 ? 哦嚯, 问题出现了 !!

ch32v003 是一款使用 rv32ec 指令集的国产单片机, 很便宜 (某宝零卖只要 0.4 元一个, 在同档次几乎是无敌的存在).

主要困难在于 rv32ec 其中的 e. rust 编译器使用 LLVM 作为后端 (机器代码生成), 很早就对 rv32i 提供了支持. 但是 LLVM 对 rv32e 的支持一直有问题, 相关支持补丁最近才刚刚合并.

本文主要参考以下资料:

  • https://noxim.xyz/blog/rust-ch32v003/
  • https://github.com/ch32-rs/ch32v00x-hal

在此感谢 Noxim 这位大神在使用 rust 编写 rv32ec 单片机程序方面的先驱探索和贡献 !

目录

  • 1 单片机 (MCU) ch32v003 简介
  • 2 rust 编译工具 (rv32ec)
  • 3 创建 rust 项目
  • 4 点亮一颗 LED
  • 5 总结与展望

1 单片机 (MCU) ch32v003 简介

相关链接: https://www.wch.cn/products/CH32V003.html

单片机 (也叫 MCU, 微控制器) 是低成本嵌入式系统的核心部件, 在单个芯片上集成了一整台计算机 (包括 CPU, RAM, flash, 输入输出设备 (GPIO, UART, I2C, SPI, ADC, DAC, OPA, USB) 等), 体积小, 功耗低, 使用方便, 只需要搭配很少的外围器件即可运行.

ch32v003 是国产芯片, 是南京沁恒微电子股份有限公司 (WCH) 推出的一款 32 位通用 RISC-V CPU 的单片机.

为什么选择 ch32v003 ? 国产, RISC-V, 便宜 !

RISC-V 是一种很年轻的开源指令集架构 (ISA), 最近几年火的不得了, 未来很有可能与 x86, ARM 形成三足鼎立之势. 因为 RISC-V 是开源的, 任何人都能基于 RISC-V 指令集来设计处理器, 比如 ch32v003 单片机使用沁恒自研的青稞 V2A CPU 核心, 没有授权费用, 在成本上具有明显优势. 目前 RISC-V 还正在努力的进入服务器, 个人计算设备 (比如手机) 等高性能计算领域. 但是 RISC-V 在嵌入式 (单片机) 领域已经广泛使用了, 有百亿颗芯片的出货量.

RISC-V 指令集是模块化设计, 分为基础指令集和许多的功能扩展指令集. RISC-V 基础指令集非常简单 (只有约 40 条指令), 分为 rv32i (用于 32 位处理器), rv64i (用于 64 位处理器), 以及 rv32e (用于 32 位嵌入式处理器).

其中 rv32i 和 rv32e 的区别是, rv32i 具有 32 个通用寄存器, rv32e 只有 16 个通用寄存器 (少了一半). 对于低成本处理器 (比如单片机) 来说, 减少这些寄存器, 可以显著减少门电路数量, 缩小芯片面积, 从而大幅度降低成本.

ch32v003 单片机使用 rv32ec 指令集, 其中 rv32e 就是上面说的, c 表示压缩指令集扩展, 可以提高代码密度 (同样的程序减少占用的存储空间). RISC-V 非压缩指令 (正常指令) 的每条指令占用 4 字节存储空间, 压缩指令一条只需要 2 字节.

在这里插入图片描述

(图片来源: wch 官网)

在这里插入图片描述

ch32v003 单片机具有一个最高 48MHz 主频的 rv32ec 指令集 CPU 核心, 2KB SRAM (存储运行数据), 16KB flash (存储程序代码), 5 个定时器 (timer). 输入输出接口有: 最多 18 个通用输入输出 (GPIO), 1 个 UART, 1 个 I2C, 1 个 SPI, 8 通道 10 位精度的模拟数字转换器 (ADC), 1 个运算放大器 (OPA, 相当于买运放赠送单片机 ~ ). 有 4 种封装可选.

其中窝比较喜欢的是 TSSOP20 封装 (型号 ch32v003f4p6), 引脚比较多有 20 个 (其中 2 个用于供电, 18 个用于输入输出), 焊接也比较方便.

窝觉得, ch32v003 单片机的主要缺点是 flash 容量较小, 只有 16KB. 如果出个 32KB 的型号就更好了.


本文用到的硬件设备有:

在这里插入图片描述

ch32v003 单片机开发板 (CH32V003F4P6-R0-1v1).

在这里插入图片描述

单片机调试和固件写入工具 (WCH-LinkE-R0-1v3).

以及一台 x86 PC (笔记本或台式机), 几条杜邦线.

2 rust 编译工具 (rv32ec)

为什么选择 rust 来编写单片机程序 ? 就俩字, 舒服 ! 再也不想使用古老的 C 语言了呜呜 ~~


本文使用的计算机软件环境如下:

  • 操作系统: ArchLinux

建议通过 rustup 来安装 rust 编译工具: https://www.rust-lang.org/zh-CN/learn/get-started

建议使用国内镜像进行加速: https://rsproxy.cn/

  • (1) 安装 nightly 版的 rust 编译器:

    rustup toolchain install nightly
    
  • (2) 安装 rust-src 组件:

    rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
    
  • (3) 安装之后:

    > rustup toolchain list
    stable-x86_64-unknown-linux-gnu (default)
    nightly-x86_64-unknown-linux-gnu
    
  • 编译器版本:

    > rustc +nightly --version --verbose
    rustc 1.79.0-nightly (9d5cdf75a 2024-04-07)
    binary: rustc
    commit-hash: 9d5cdf75aa42faaf0b58ba21a510117e8d0051a3
    commit-date: 2024-04-07
    host: x86_64-unknown-linux-gnu
    release: 1.79.0-nightly
    LLVM version: 18.1.3
    
    > rustc --version --verbose
    rustc 1.77.1 (7cf61ebde 2024-03-27)
    binary: rustc
    commit-hash: 7cf61ebde7b22796c69757901dd346d0fe70bd97
    commit-date: 2024-03-27
    host: x86_64-unknown-linux-gnu
    release: 1.77.1
    LLVM version: 17.0.6
    

相关背景故事 (历史):

这件事其实去年就已经可以做到了 (Noxim 大神的博客发布日期是 2023-03-26).

但是之前 rust 编译器对 rv32e 没有支持 (本质上是因为 LLVM 不支持), 所以 Noxim 大神只好自己魔改了 rustc:

  • https://github.com/Noxime/rust/tree/rv32e
  • https://github.com/Noxime/llvm-project/tree/rv32e

所以当时是很麻烦的: 首先下载大神修改后的代码, 然后编译 rust 编译器, 然后再使用编译好的 rust 编译器来编译自己的单片机程序 … .

直到最近 LLVM 才添加了对 rv32e 的实验性支持:

  • LLVM 18.1.0 Released! https://discourse.llvm.org/t/llvm-18-1-0-released/77448 (发布日期 2024-03-06)

其中 LLVM 18.1.0rc Release Notes 有:

Changes to the RISC-V Backend

  • CodeGen of RV32E/RV64E was supported experimentally.
  • CodeGen of ilp32e/lp64e was supported experimentally.

也就是说, 直到 LLVM 18.1.0 才支持.

目前 rust 稳定版 (rustc 1.77.1) 对应的 LLVM 版本是 17.0.6, nightly 对应的 LLVM 版本是 18.1.3.

3 创建 rust 项目

  • (1) 使用 cargo 创建项目:

    > cargo new --bin t4Created binary (application) `t4` package
    

    项目名称随意, 此处以 t4 举栗.

  • (2) 完整源代码:

文件 t4/Cargo.toml:

[package]
name = "t4"
version = "0.1.0"
edition = "2021"[dependencies]
panic-halt = "^0.2.0"
riscv = "^0.11.1"
ch32v0 = { version = "^0.2.0", features = ["rt", "ch32v003"] }
qingke-rt = "^0.1.9"
qingke = "^0.1.9"[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
debug = true# 不应使用 dev 编译 (代码太大), 应该始终使用 --release
[profile.dev]
opt-level = "z"

由于单片机的 flash 容量较小, 编译时应该尽量减小生成代码的大小. 此处 [profile.release] 相关的编译选项就是为了这个目的.

文件 t4/.cargo/config.toml:

# ch32v003 (rv32ec)
[build]
target = "riscv32ec-unknown-none-elf.json"[unstable]
build-std = ["core"]
build-std-features = ["compiler-builtins-mem"][target.riscv32ec-unknown-none-elf]
rustflags = ["-C", "link-arg=-Tlink.x",
]

cargo 相关的编译配置.

文件 t4/riscv32ec-unknown-none-elf.json:

{"arch": "riscv32","atomic-cas": false,"cpu": "generic-rv32","crt-objects-fallback": "false","data-layout": "e-m:e-p:32:32-i64:64-n32-S32","eh-frame-header": false,"emit-debug-gdb-scripts": false,"features": "+e,+c,+forced-atomics","linker": "rust-lld","linker-flavor": "gnu-lld","llvm-target": "riscv32","llvm-abiname": "ilp32e","max-atomic-width": 32,"panic-strategy": "abort","relocation-model": "static","target-pointer-width": "32"
}

rustc 目前还没有内置 riscv32ec 编译目标, 所以需要自定义.

文件 t4/build.rs:

use std::path::PathBuf;
use std::{env, fs};fn main() {println!("cargo:rerun-if-changed=build.rs");println!("cargo:rerun-if-changed=ch32v003/memory.x");let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());fs::write(out_dir.join("memory.x"),include_bytes!("ch32v003/memory.x"),).unwrap();println!("cargo:rustc-link-search={}", out_dir.display());
}

编译脚本, 主要作用是指定 memory.x 链接脚本.

文件 t4/ch32v003/memory.x:

PROVIDE(_hart_stack_size = 64);MEMORY
{FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16KRAM (xrw)  : ORIGIN = 0x20000000, LENGTH = 2K
}REGION_ALIAS("REGION_TEXT", FLASH);
REGION_ALIAS("REGION_RODATA", FLASH);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);
REGION_ALIAS("REGION_HEAP", RAM);
REGION_ALIAS("REGION_STACK", RAM);

这个链接脚本定义了 ch32v003 单片机的内存地址布局.

文件 t4/src/main.rs:

#![no_std]
#![no_main]use panic_halt as _;
use qingke::riscv::asm;
use qingke_rt::entry;use ch32v0::ch32v003::Peripherals as P;#[entry]
fn main() -> ! {let p = unsafe { P::steal() };// 初始化配置外设// 启用 GPIOCp.RCC.apb2pcenr().modify(|_, w| w.iopcen().set_bit());// 配置 PC1 引脚 (推挽输出 10MHz)p.GPIOC.cfglr().modify(|_, w| w.mode1().variant(0b01).cnf1().variant(0b00));// 主循环loop {// 点亮 LED (PC1)p.GPIOC.outdr().modify(|_, w| w.odr1().clear_bit());// 延时等待asm::delay(1_000_000);// 关闭 LED (PC1)p.GPIOC.outdr().modify(|_, w| w.odr1().set_bit());// 延时等待asm::delay(500_000);}
}

终于到了单片机程序代码. 首先, 通过写入配置寄存器, 对各种需要用到的外设进行初始化配置. 此处只启用了 GPIOC 和 PC1 引脚. 然后进入无限循环, 控制 PC1 引脚输出高低电平, 使 LED 闪烁.


  • (3) 编译项目:

    > cargo +nightly build --releaseFinished `release` profile [optimized + debuginfo] target(s) in 14.78s
    
  • (4) 将编译结果 ELF 文件转换为 Intel HEX 文件:

    > cd target/riscv32ec-unknown-none-elf/release
    > llvm-objcopy -O ihex t4 out.hex
    

    编译输出目录的文件:

    > ls -l
    总计 68
    drwxr-xr-x 1 s2 s2   290  4818:53 build/
    drwxr-xr-x 1 s2 s2  3318  4819:07 deps/
    drwxr-xr-x 1 s2 s2     0  4818:53 examples/
    drwxr-xr-x 1 s2 s2     0  4818:53 incremental/
    -rwxr-xr-x 1 s2 s2  1425  4819:09 out.hex*
    -rwxr-xr-x 2 s2 s2 60932  4819:07 t4*
    -rw-r--r-- 1 s2 s2   697  4818:53 t4.d
    

4 点亮一颗 LED

  • (1) 安装 wlink 刷写工具: https://github.com/ch32-rs/wlink

    这个是第三方开发的开源的刷写工具, 在 GNU/Linux 系统上使用比较方便. 当然使用 WCH 官方提供的刷写工具也可以.

    版本信息:

    > type wlink
    wlink is /home/s2/.cargo/bin/wlink
    > wlink --version
    wlink 0.0.9
    
  • (2) 使用 WCH-LINKE 连接 ch32v003 单片机.

    按照下表接线:

序号WCH-LINKE 针脚ch32v003 引脚
1GNDGND
23V3VCC
3SWDIOPD1

如图:

在这里插入图片描述

WCH-LINKE 通过 USB 连接 PC.

  • (3) 查看连接的芯片信息:

    > lsusbBus 001 Device 024: ID 1a86:8010 QinHeng Electronics WCH-Link
    
    > wlink status
    11:19:29 [INFO] Connected to WCH-Link v2.9(v29) (WCH-LinkE-CH32V305)
    11:19:29 [INFO] Attached chip: CH32V003 [CH32V003F4P6] (ChipID: 0x00300500)
    11:19:29 [INFO] Chip ESIG: FlashSize(16KB) UID(cd-ab-84-aa-49-bc-9a-12)
    11:19:29 [INFO] Flash protected: false
    11:19:29 [INFO] RISC-V ISA(misa): Some("RV32CEX")
    11:19:29 [INFO] RISC-V arch(marchid): Some("WCH-V2A")
    11:19:29 [WARN] The halt status may be incorrect because detaching might resume the MCU
    11:19:29 [INFO] Dmstatus {.0: 0x4c0382,allhavereset: true,anyhavereset: true,allresumeack: false,anyresumeack: false,allunavail: false,anyunavail: false,allrunning: false,anyrunning: false,allhalted: true,anyhalted: true,authenticated: true,version: 0x2,
    }
    11:19:29 [INFO] Dmcontrol {.0: 0x80000001,haltreq: true,resumereq: false,ackhavereset: false,ndmreset: false,dmactive: true,
    }
    11:19:29 [INFO] Hartinfo {.0: 0x2120f4,nscratch: 0x2,dataaccess: true,datasize: 0x2,dataaddr: 0xf4,
    }
    11:19:29 [INFO] Abstractcs {.0: 0x8000002,progbufsize: 0x8,busy: false,cmderr: 0x0,datacount: 0x2,
    }
    11:19:29 [INFO] haltsum0: 0x1
    
  • (4) 将编译好的固件写入单片机的 flash 存储器 (也叫 “烧录”):

    > wlink flash out.hex
    11:25:42 [INFO] Connected to WCH-Link v2.9(v29) (WCH-LinkE-CH32V305)
    11:25:42 [INFO] Attached chip: CH32V003 [CH32V003F4P6] (ChipID: 0x00300500)
    11:25:42 [INFO] Chip ESIG: FlashSize(16KB) UID(cd-ab-84-aa-49-bc-9a-12)
    11:25:42 [INFO] Flash protected: false
    11:25:42 [INFO] Read out.hex as IntelHex format
    11:25:42 [INFO] Flashing 500 bytes to 0x08000000
    11:25:42 [INFO] Read protected: false
    ██████████████████████████████████ 500/500
    11:25:42 [INFO] Flash done
    11:25:43 [INFO] Now reset...
    

    这个单片机程序比较简单, 所以大小只有几百字节.


然后就能看到 LED 闪烁啦 ~

在这里插入图片描述

5 总结与展望

在计算机领域, 学习最大的困难是什么 ? 搭建开发环境 !!!

如何让整个系统跑起来, 让一代又一代的人头疼. 所以, 要先从最简单的做起, 先实现一个能运行的最小系统. 比如在学习编程语言的时候, 国际惯例就是先写一个 “Hello world” 并运行.

那么对应于嵌入式 (单片机) 领域, 当然是点亮一颗 LED ! 这个已经做到了, 然后就可以起飞啦 … . (此处缺一个表情包)

对 rv32ec 的初步支持还没有进入 rust 稳定版 (目前是 rustc 1.77, 要等到下个版本才有), 所以本文的内容还算比较新鲜热乎. 目前 rust 对 rv32ec 的支持还不是很好, 比如 rv32ec 编译目标还没有内置. 所以可以看到, 在本文中编译一个单片机程序还是比较麻烦的. 但是以后相关工具的支持会越来越好的, 开发也会越来越容易的.

还有一个 rust 编写的嵌入式 (单片机) 开发框架 Embassy: https://embassy.dev/

Embassy 使用异步编程 (async), 并且有硬件抽象层 (HAL) 和设备驱动程序, 可以作为传统实时操作系统 (RTOS) 的一种替代方案. 使用 Embassy 和 rust 编写单片机程序会舒适很多, 期待这个年轻框架发展的越来越好.


本文使用 CC-BY-SA 4.0 许可发布.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/809244.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简单了解JVM

一.JVM简介 jvm及Java virtual machineJava虚拟机,它是一个虚构出来的计算机,一种规范。其实抛开这么专业的句子不说,就知道 JVM 其实就类似于一台小电脑运行在 windows 或者 linux 这些操作系统环境下即可。它直接和操作系统进行交互&#…

Handler——小白能懂的原理,老鸟需要的面经

1.机制学习 1.1Handler定义 发送并处理 与线程的消息队列关联的Message和Runnable 1.2基本用法 1、Message.obtain() 从消息池取得Message 2、Handler().sendMessage(msg) 发送消息 3、Handler().post 将Runnable包装成Message发送 以下提供一个结构代码 import android.os.H…

VMware导出虚拟机vmkd格式转换qcow2

VMware虚拟机导出qcow2格式可以上传至云服务 1、需要导出的虚拟机 2、克隆虚拟机 3、选择克隆源 4、创建完整克隆 5、完成 6、找到VMware安装路径 7、找到vmware-vdiskmanager所在路径使用cmd或Windows PowerShell进入目录 进入vmware-vdiskmanager目录 cd F:\软件\VMware Wo…

debian安装和基本使用

debian安装和基本使用 文章目录 debian安装和基本使用1. 为什么选择debian2. 如何下载Debian2.1 小型安装镜像2.2 完整安装镜像 3. Debian操作系统安装3.1 创建Debian虚拟机3.2 安装操作系统 4. Debian系统的初始设置4.1 桌面环境的配置4.2 配置网络4.3 生效网络配置4.4 配置de…

React 快速入门:掌握前端开发的核心技能

React 快速入门:掌握前端开发的核心技能 一、React 简介1.1 React 的历史1.2 React 的概念1.3 React 的特点1.4 React 的官网地址 二、开发环境搭建三、React 基础3.1 JSX3.2 组件3.3 Props3.4 State3.5 props 和 state 的区别3.6 Hook 四、React 生命周期五、添加样…

Redis Pipelining 底层原理分析及实践

作者:vivo 互联网服务器团队-Wang Fei Redis是一种基于客户端-服务端模型以及请求/响应的TCP服务。在遇到批处理命令执行时,Redis提供了Pipelining(管道)来提升批处理性能。本文结合实践分析了Spring Boot框架下Redis的Lettuce客户端和Redisson客户端对…

Unity构建详解(7)——AssetBundle格式解析

【文件格式】 文件可以分为文本文件、图片文件、音频文件、视频文件等等,我们常见的这些文件都有行业内的标准格式,其意味着按照一定的规则和规范去保存读取文件,可以获取我们想要的数据。 有些软件会有自己的文件格式,会按照其…

风储微网虚拟惯性控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 风储微网虚拟惯性控制系统simulink建模与仿真。风储微网虚拟惯性控制系统是一种模仿传统同步发电机惯性特性的控制策略,它通过集成风力发电系统、储能系统和其他分…

如何动态渲染HTML内容?用v-html!

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

计算机网络知识等汇总补充

计算机网络知识汇总补充 一、四次挥手1、为什么TCP要等待2MSL2、如果说一个系统中,有大量的time_wait和close_wait,会是什么原因? 二、你是怎么解决粘包问题?三、你觉得哪些场景适合redis四、redis的持久化策略五、你会怎么保证my…

4-云原生监控体系-Grafana-基本使用

1. 介绍 使用Grafana,您可以通过漂亮、灵活的仪表板创建、探索和共享所有数据。查询、可视化、提醒和理解您的数据,无论数据存储在何处。 图片出处: https://grafana.com/grafana/ 官方网站 2. 界面介绍 Connections 可以配置数据源&#x…

php-redis windows ,pecl 已经不维护了,解决方案:php 8.2 | 8.3+ redis extension windows

从论坛上pecl 已经不维护了,直接让大家到ci 去下载 https://stackoverflow.com/questions/76496488/redis-dll-not-found-for-php8-2/76496489#76496489 让我们找最新的一次commit ,然后又action 构建,再下载,这样的话也好&#…

Redis从入门到精通(十三)Redis分布式缓存(一)RDB和AOF持久化、Redis主从集群的搭建与原理分析

文章目录 第5章 Redis分布式缓存5.1 Redis持久化5.1.1 RDB持久化5.1.1.1 执行时机5.1.1.2 bgsave原理 5.1.2 AOF持久化5.1.2.1 AOF原理5.1.2.2 AOF配置5.1.2.3 AOF文件重写 5.1.3 RDB和AOF的对比 5.2 Redis主从5.2.1 搭建主从结构5.2.2 主从数据同步原理5.2.2.1 全量同步5.2.2.…

集群开发学习(一)(安装GO和MySQL,K8S基础概念)

完成gin小任务 参考文档: https://www.kancloud.cn/jiajunxi/ginweb100/1801414 https://github.com/hanjialeOK/going 最终代码地址:https://github.com/qinliangql/gin_mini_test.git 学习 1.安装go wget https://dl.google.com/go/go1.20.2.linu…

【Ubuntu】 Github Readme导入GIF

1.工具安装 我们使用 ffmpeg 软件来完成转换工作1.1 安装命令 sudo add-apt-repository ppa:jonathonf/ffmpeg-3sudo apt-get updatesudo apt-get install ffmpeg1.2 转换命令 (1)直接转换命令: ffmpeg -i out.mp4 out.gif(2) 带参数命令&…

【洛谷 P4017】最大食物链计数 题解(深度优先搜索+动态规划+邻接表+记忆化搜索+剪枝)

最大食物链计数 题目背景 你知道食物链吗?Delia 生物考试的时候,数食物链条数的题目全都错了,因为她总是重复数了几条或漏掉了几条。于是她来就来求助你,然而你也不会啊!写一个程序来帮帮她吧。 题目描述 给你一个…

SuperGluePretrainedNetwork调用接口版本(两个版本!)

本脚本是一个基于Python的应用,旨在演示如何使用SuperGlue算法进行图像之间的特征匹配。SuperGlue是一个强大的特征匹配工具,能够在不同的图像之间找到对应的关键点。这个工具尤其适用于计算机视觉任务,如立体视觉、图像拼接、对象识别和追踪…

RN封装三角形组件(只支持上下箭头)

import React from react; import { View, StyleSheet } from react-native;const Triangle ({ direction, width, height, color }) > {// 根据方向选择三角形的样式const triangleStyle direction up? {borderTopWidth: 0,borderBottomWidth: height,borderLeftWidth: …

docker完美安装分布式任务调度平台XXL-JOB

分布式任务调度平台XXL-JOB 1、官方文档 自己看 https://www.xuxueli.com/xxl-job/#1.1%20%E6%A6%82%E8%BF%B0 2、使用docker部署 本人使用的腾讯云,安装docker暴露一下端口,就很舒服的安装这个服务了。 docker pull xuxueli/xxl-job-admin:2.4.03…

Harmony鸿蒙南向驱动开发-PIN接口使用

功能简介 PIN即管脚控制器,用于统一管理各SoC的管脚资源,对外提供管脚复用功能:包括管脚推拉方式、管脚推拉强度以及管脚功能。 PIN接口定义了操作PIN管脚的通用方法集合,包括: 获取/释放管脚描述句柄:传…