mcu - 封装 mcu 一些特殊操作
孟伟
一、概述
mcu 模块是 LuatOS 中封装 MCU 一些特殊操作的核心模块,提供了对 MCU 底层功能的访问和控制能力。
其中 mcu.ticks()、mcu.tick64() 和 mcu.ticks2() 的区别如下:
| 特性 | mcu.ticks() | mcu.tick64(is_bit64) | mcu.ticks2(mode) |
|---|---|---|---|
| 时间单位 | 毫秒 (ms) | 微秒 (µs) | 由 mode 参数决定 |
| 返回值/类型 | 32位无符号整数 | 64位值(8个字节的uint64或bit64结构) | 整数或字符串(依模式而定) |
| 数据范围 | 约49天循环一次 | 理论上数万年不溢出 | 依赖所选模式 |
| 主要用途 | 通用毫秒级延时、计时 | 高精度、长时间跨度计时 | 获取CPU时钟等更底层时间信息 |
| 是否依赖bit64库 | 否 | 是(当is_bit64为true时) | 通常不需要 |
二、核心示例
1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;
2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;
3、更加完整和详细的 demo,请参考 此链接 中的 demo/mcu
-- 获取当前主频_
_local mhz = mcu.getClk()_
_log.info("当前主频:", mhz, "MHz")_
_-- 获取设备唯一ID_
_local unique_id = mcu.unique_id()_
_log.info("设备唯一ID:", unique_id)_
_log.info("设备唯一ID(16进制):", string.toHex(unique_id))
三、常量详解
核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;
每个常量对应的常量取值仅做日志打印时查询使用,不要将这个常量取值用做具体的业务逻辑判断,因为LuatOS内核固件可能会变更每个常量对应的常量取值;
如果用做具体的业务逻辑判断,一旦常量取值发生改变,业务逻辑就会出错;
3.1 mcu.UART
常量含义:外设类型-串口;
数据类型:number;
常量取值:0;
3.2 mcu.I2C
常量含义:外设类型-I2C;
数据类型:number;
常量取值:1;
3.3 mcu.SPI
常量含义:外设类型-SPI;
数据类型:number;
常量取值:2;
3.4 mcu.PWM
常量含义:外设类型-PWM;
数据类型:number;
常量取值:3;
3.5 mcu.CAN
常量含义:外设类型-CAN;
数据类型:number;
常量取值:4;
3.6 mcu.GPIO
常量含义:外设类型-GPIO;
数据类型:number;
常量取值:5;
3.7 mcu.I2S
常量含义:外设类型-I2S, 音频总线;
数据类型:number;
常量取值:6;
3.8 mcu.LCD
常量含义:外设类型-LCD, LCD专用总线;
数据类型:number;
常量取值:8;
3.9 mcu.CAM
常量含义:外设类型-CAM,与 mcu.CAMERA一样;
数据类型:number;
常量取值:9;
3.10 mcu.CAMERA
常量含义:外设类型-CAMERA,就是CAM,摄像头;
数据类型:number;
常量取值:9;
3.11 mcu.ONEWIRE
常量含义:外设类型-ONEWIRE,单总线协议;
数据类型:number;
常量取值:10;
3.12 mcu.SDIO
常量含义:外设类型-SDIO,接TF卡;
数据类型:number;
常量取值:7;
3.13 mcu.KEYBORAD
常量含义:外设类型-KEYBORAD,键盘;
数据类型:number;
常量取值:11;
3.14 mcu.ETH
常量含义:外设类型-ETH,网口;
数据类型:number;
常量取值:12;
四、函数详解
mcu.unique_id()
功能
获取设备唯一 ID,基于模块硬件ID生成的唯一标识符。
注意事项
- 可能包含不可见字符,如需查看建议使用 string.toHex()函数转换后打印。
参数
无。
返回值
local unique_id = mcu.unique_id()
unique_id
含义说明:设备唯一标识字符串;
数据类型:string;
取值范围:0~32字节,若不支持,会返回空字符串;
注意事项:可能包含不可见字符;
示例
local unique_id = mcu.unique_id()
print("唯一ID:", unique_id)
mcu.ticks()
功能
获取启动后的 tick 数。
主要应用于超时检测、状态机计时、按键消抖等场景
计算一个操作的对应毫秒时长可参考下面代码:
local tick1 = mcu.ticks()
-- 执行一些操作
sys.wait(100) -- 等待100ms
local tick2 = mcu.ticks()
-- 获取系统频率并计算毫秒
local ticks_per_second = mcu.hz()
local milliseconds = (diff_ticks * 1000) / ticks_per_second
print(string.format("耗时: %.2f 毫秒", milliseconds))
注意事项
- 本身是无符号值,范围 0~0xffffffff;
- Lua 是有符号计算,计算时超过 0x7fffffff 会变负数;
- 如需在可预见时间内不会溢出的值, 可用 mcu.ticks2()。
mcu.ticks():32 位计数器,约 49.7 天 溢出一次
mcu.ticks2():64 位计数器,可预见时间内不会溢出。
参数
无。
返回值
local tick = mcu.ticks()
tick
含义说明:当前tick值;
数据类型:number;
取值范围:0~0xffffffff;
返回示例:123456789;
示例
local tick = mcu.ticks()
print("ticks:", tick)
mcu.hz()
功能
获取每秒的 tick 数量。
通常为 1000(每秒 1000 个 tick);但是也有例外,需要根据这个函数的返回值准确获取。
参数
无。
返回值
local hz = mcu.hz()
hz
含义说明:每秒的tick数量;
数据类型:number;
注意事项:通常为1000;但是也有例外,需要根据这个函数的返回值准确获取;
返回示例:1000;
示例
local hz = mcu.hz()
print("每秒tick数:", hz)
mcu.x32(value)
功能
转换 10 进制数为 16 进制字符串输出。
参数
value
参数含义:需要转换的值;
数据类型:number;
取值范围:0~0xffffffff;
是否必选:必须传入此参数;
参数示例:0x2009FFFC;
返回值
local hex_str = mcu.x32()
hex_str
含义说明:16进制字符串;
数据类型:string;
返回示例:"0x2009fffc";
示例
local hex_str = mcu.x32(0x2009FFFC) _-- 输出"0x2009fffc"_
local hex_str = mcu.x32(453453) _-- 输出"0x6eb4d"_
mcu.tick64(is_bit64)
功能
获取启动后的高精度 tick。
接口主要用于需要高精度时间测量和长时间运行计时的场景。
计算一个操作的对应毫秒时长可参考下面代码:
local tick1 = mcu.tick64()
--执行一些操作,需要计算时长的操作。
local tick2 = mcu.tick64()
-- 计算tick差值
local result, diff_ticks = mcu.dtick64(tick2, tick1)
-- 获取每秒tick数
local ticks_per_second = mcu.hz()
-- 计算毫秒时长
local milliseconds = (diff_ticks * 1000) / ticks_per_second
print("耗时:", milliseconds, "毫秒")
注意事项
- 如果支持 bit64 库,可以直接输出转换好的 bit64 结构。
参数
is_bit64
参数含义:是否输出bit64结构;
数据类型:boolean;
取值范围:true表示输出bit64结构,其他都是false,默认false;
是否必选:可选;
注意事项:用于兼容旧的demo;
参数示例:false;
返回值
local tick_str, tick_per = mcu.tick64(is_bit64)
tick_str
含义说明:当前tick值;
数据类型:string;
取值范围:8个字节的uint64;如果支持64bit库,同时要求输出64bit结构的话,会输出9字节的string;
tick_per
含义说明:1us有几个tick;
数据类型:number;
取值范围:0表示未知;
示例
local tick_str, tick_per = mcu.tick64()
print("高精度tick:", tick_str, "每微秒tick数:", tick_per)
mcu.dtick64(tick1, tick2, check_value)
功能
计算 2 个 64bit tick 的差值。
注意事项
差值如果超过了 0x7fffffff,结果可能是错误的。因为如果是 32 位固件的话,最大有符号正整数是 0x7fffffff,所以如果差值超过这个数就会不准确
参数
tick1
参数含义:第一个64bit的string;
数据类型:string;
取值范围:0~0xffffffff;
是否必选:必须传入此参数;
参数示例:通过mcu.tick64()获取的值;
tick2
参数含义:第二个64bit的string;
数据类型:string;
取值范围:0~0xffffffff;
是否必选:必须传入此参数;
参数示例:通过mcu.tick64()获取的值;
check_value
参数含义:参考值;
数据类型:number;
取值范围:0~0xffffffff;默认是0
是否必选:可选;如果为0,则返回结果中第一个参数为true;
返回值
local result, diff_tick = mcu.dtick64(tick1, tick2, check_value)
result
含义说明:tick1和tick2的差值与参考值比较的结果;
数据类型:boolean;
取值范围:如果大于等于参考值为true,反之为false;
返回示例:true;
diff_tick
含义说明:差值tick1 - tick2;
数据类型:number;
取值范围:0~0xffffffff;
注意事项:如果超过了0x7fffffff,结果可能是错的;因为如果是32位固件的话,最大有符号正整数是0x7fffffff,所以如果差值超过这个数就会不准确
返回示例:1000;
示例
local tick1, _ = mcu.tick64()
sys.wait(1000)
local tick2, _ = mcu.tick64()
local result, diff_tick = mcu.dtick64(tick1, tick2)
print("比较结果:", result, "差值:", diff_tick)
mcu.hardfault(mode)
功能
配置 MCU 死机时的处理模式。
注意事项
此接口使用的时候注意放在代码的最前面,开机就配置。配置的值重启后还会生效。
如果不调用此接口默认是死机后重启,如果调用此接口没有配置mode参数,默认是0死机后停机
死机是指底层死机,脚本语法出现报错不属于死机;死机一般是因为代码中有死循环或者内存不足或看门狗死机或堆栈溢出等情况。
参数
mode
参数含义:处理模式;
数据类型:number;
取值范围:0(死机停机)、1(死机后重启)、2(死机后尽量将错误信息提交给外部工具后重启)、3(死机时写入关键信息到flash后立刻重启);
是否必选:必须传入此参数;
参数示例:1;
返回值
无。
示例
_-- 死机后停机,一般用于调试状态_
mcu.hardfault(0)
_-- 死机后重启,一般用于正式产品_
mcu.hardfault(1)
_-- 死机后尽量将错误信息提交给外部工具后重启,一般用于压力测试或者正式产品_
mcu.hardfault(2)
mcu.iomux(type, channel, value)
功能
在外设打开前,将外设 IO 复用到非默认配置上。
保留此接口为了兼容老固件,新产品开发需要复用功能请查看 pins 库中的 setup 接口。
注意事项
- 目前只支持 Air780E 的部分外设复用到其他配置。
- 这是一个临时接口,新产品开发需要复用功能请查看 pins 库中的 setup 接口。
参数
type
参数含义:外设类型;
数据类型:number;
取值范围:目前只有mcu.UART, mcu.I2C;
是否必选:必须传入此参数;
参数示例:mcu.UART;
channel
参数含义:总线序号;
数据类型:number;
取值范围:0~N;N是根据俄外设类型不同N也不同,比如i2c只有0和1两个通道,所以N就是1;
是否必选:必须传入此参数;
参数示例:0;
value
参数含义:新的配置;
数据类型:number;
取值范围:根据具体平台决定;
是否必选:必须传入此参数;
注意事项:详情参考示例部分
参数示例:1;
返回值
无。
示例
_-- Air780E的UART2复用到gpio12和gpio13(Air780EG默认是这个复用,不要动)_
mcu.iomux(mcu.UART, 2, 1)
_-- Air780E的UART2复用到gpio6和gpio7_
mcu.iomux(mcu.UART, 2, 2)
_-- Air780E的I2C0复用到gpio12和gpio13_
mcu.iomux(mcu.I2C, 0, 1)
_-- Air780E的I2C0复用到gpio16和gpio17_
mcu.iomux(mcu.I2C, 0, 2)
_-- Air780E的I2C1复用到gpio4和gpio5_
mcu.iomux(mcu.I2C, 1, 1)
mcu.altfun(type, sn, pad_index, alt_fun, is_input)
功能
IO 外设功能复用选择。
保留此接口为了兼容老固件,新产品开发需要复用功能请查看 pins 库中的 setup 接口。
注意事项
- 普通 MCU 通常是以 GPIO 号为唯一 ID 号,但是专用 SOC,比如 CAT1 的,可能以 PAD 号或者模块 pin 脚号为唯一 ID 号。
- 本函数不是所有平台适用。
参数
type
参数含义:外设类型;
数据类型:number;
取值范围:目前有mcu.UART, mcu.I2C, mcu.SPI, mcu.PWM, mcu.GPIO, mcu.I2S, mcu.LCD, mcu.CAM,具体需要看平台;
是否必选:必须传入此参数;
参数示例:mcu.UART;
sn
参数含义:总线序号;
数据类型:number;
取值范围:0~N,如果是mcu.GPIO,则是GPIO号;如果是mcu.I2C,则是I2C通道号;
是否必选:必须传入此参数;
注意事项:具体看平台的IOMUX复用表;
参数示例:2;
pad_index
参数含义:唯一ID号;
数据类型:number;
是否必选:可选;如果留空不写,则表示清除配置,使用平台的默认配置;
注意事项:具体看平台的IOMUX复用表;
参数示例:1;
alt_fun
参数含义:复用功能序号;
数据类型:number;
取值范围:0~N;如果是mcu.GPIO,则是GPIO号;如果是mcu.I2C,则是I2C通道号;
是否必选:可选;
注意事项:具体看平台的IOMUX复用表;
参数示例:3;
is_input
参数含义:是否是输入功能;
数据类型:boolean;
取值范围:true表示是输入功能;默认false,表示是输出功能;
是否必选:可选;
参数示例:true;
返回值
无。
示例
_-- UART2复用到paddr 25/26 alt 3_
mcu.altfun(mcu.UART,2, 25, 3, true)
mcu.altfun(mcu.UART,2, 26, 3, false)
_-- Air8101的SDIO复用演示, 1线模式, 暂不支持4线_
mcu.altfun(mcu.SDI0,0,14)
mcu.altfun(mcu.SDI0,0,15)
mcu.altfun(mcu.SDI0,0,16)
mcu.ticks2(mode)
功能
获取高精度的计数。
注意事项
与 mcu.ticks()的区别是,底层计数器是 64bit 的,在可预计的将来不会溢出。
所以本函数返回的值总是递增的,而且 32bit 固件也能处理。
参数
mode
参数含义:模式;
数据类型:number;
取值范围:_-- 模式可选值 及 对应的返回值_
_-- 0: 返回微秒数, 以秒为分割, 例如 1234567890us 返回2个值: 1234, 567890_
_-- 1: 返回毫秒数, 以千秒为分割, 例如 1234567890ms 返回2个值: 1234, 567890_
_-- 2: 返回秒数, 以百万秒为分割, 例如 1234567890s 返回2个值: 1234, 567890_
是否必选:必须传入此参数;
参数示例:0;
返回值
local us_h, us_l = mcu.ticks2(mode)
high
含义说明:高部分值;
数据类型:number;
取值范围:根据mode的不同,返回值的含义不同;
返回示例:1234;
low
含义说明:低部分值;
数据类型:number;
取值范围:根据mode的不同,返回值的含义不同;
返回示例:567890;
示例
local us_h, us_l = mcu.ticks2(0)
local ms_h, ms_l = mcu.ticks2(1)
local sec_h, sec_l = mcu.ticks2(2)
log.info("微秒:", us_h, us_l)
log.info("毫秒:", ms_h, ms_l)
log.info("秒:", sec_h, sec_l)
五、产品支持说明
支持 LuatOS 开发的所有产品都支持 mcu 核心库。