luat_lib_mcu.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. @module mcu
  3. @summary 封装mcu一些特殊操作
  4. @version core V0007
  5. @date 2021.08.18
  6. @tag LUAT_USE_MCU
  7. */
  8. #include "luat_base.h"
  9. #include "luat_mcu.h"
  10. #include "luat_zbuff.h"
  11. #include "luat_spi.h"
  12. #include "luat_i2c.h"
  13. #include "luat_uart.h"
  14. #define LUAT_LOG_TAG "mcu"
  15. #include "luat_log.h"
  16. /*
  17. 设置主频,单位MHZ
  18. @api mcu.setClk(mhz)
  19. @int 主频,根据设备的不同有不同的有效值,请查阅手册
  20. @return bool 成功返回true,否则返回false
  21. @usage
  22. -- 注意: 并非所有模块都支持主频调整,请查阅手册
  23. -- Air101/Air103/Air601 支持设置成 2/40/80/160/240. 特别提醒, 设置到2M后, 如果要休眠, 必须先设置到80M
  24. -- ESP32系列支持设置成 40/80/160/240 , 需要2024.1.1之后的固件
  25. -- Air780系列, Air105, 不支持设置主频
  26. -- Air780系列, 进入休眠模式时自动降频到24M
  27. -- 设置到80MHZ
  28. mcu.setClk(80)
  29. sys.wait(1000)
  30. -- 设置到240MHZ
  31. mcu.setClk(240)
  32. sys.wait(1000)
  33. -- 设置到2MHZ
  34. mcu.setClk(2)
  35. sys.wait(1000)
  36. */
  37. static int l_mcu_set_clk(lua_State* L) {
  38. int ret = luat_mcu_set_clk((size_t)luaL_checkinteger(L, 1));
  39. lua_pushboolean(L, ret == 0 ? 1 : 0);
  40. return 1;
  41. }
  42. /*
  43. 获取主频,单位MHZ
  44. @api mcu.getClk()
  45. @return int 若失败返回-1,否则返回主频数值,若等于0,可能处于32k晶振的省电模式
  46. @usage
  47. local mhz = mcu.getClk()
  48. print("Boom", mhz)
  49. */
  50. static int l_mcu_get_clk(lua_State* L) {
  51. int mhz = luat_mcu_get_clk();
  52. lua_pushinteger(L, mhz);
  53. return 1;
  54. }
  55. /*
  56. 获取设备唯一id. 注意,可能包含不可见字符,如需查看建议toHex()后打印
  57. @api mcu.unique_id()
  58. @return string 设备唯一id.若不支持, 会返回空字符串.
  59. @usage
  60. local unique_id = mcu.unique_id()
  61. print("unique_id", unique_id)
  62. */
  63. static int l_mcu_unique_id(lua_State* L) {
  64. size_t len = 0;
  65. const char* id = luat_mcu_unique_id(&len);
  66. lua_pushlstring(L, id, len);
  67. return 1;
  68. }
  69. /*
  70. 获取启动后的tick数,本身是无符号值,范围0~0xffffffff,lua是有符号计算,计算时超过0x7fffffff会变负数
  71. @api mcu.ticks()
  72. @return int 当前tick值
  73. @usage
  74. local tick = mcu.ticks()
  75. print("ticks", tick)
  76. -- 如需不会溢出的值, 可用mcu.ticks2(), 于2024.5.7新增
  77. */
  78. static int l_mcu_ticks(lua_State* L) {
  79. long tick = luat_mcu_ticks();
  80. lua_pushinteger(L, tick);
  81. return 1;
  82. }
  83. /*
  84. 获取每秒的tick数量
  85. @api mcu.hz()
  86. @return int 每秒的tick数量,通常为1000
  87. @usage
  88. local hz = mcu.hz()
  89. print("mcu.hz", hz)
  90. */
  91. static int l_mcu_hz(lua_State* L) {
  92. uint32_t hz = luat_mcu_hz();
  93. lua_pushinteger(L, hz);
  94. return 1;
  95. }
  96. /*
  97. 读写mcu的32bit寄存器或者ram,谨慎使用写功能,请熟悉mcu的寄存器使用方法后再使用
  98. @api mcu.reg32(address, value, mask)
  99. @int 寄存器或者ram地址
  100. @int 写入的值,如果没有,则直接返回当前值
  101. @int 位掩码,可以对特定几个位置的bit做修改, 默认0xffffffff,修改全部32bit
  102. @return int 返回当前寄存的值
  103. @usage
  104. local value = mcu.reg32(0x2009FFFC, 0x01, 0x01) --对0x2009FFFC地址上的值,修改bit0为1
  105. */
  106. static int l_mcu_reg32(lua_State* L) {
  107. uint32_t addr = luaL_checkinteger(L, 1);
  108. volatile uint32_t *address = (uint32_t *)(addr & 0xfffffffc);
  109. if (lua_isinteger(L, 2)) {
  110. volatile uint32_t value = lua_tointeger(L, 2);
  111. volatile uint32_t mask = luaL_optinteger(L, 3, 0xffffffff);
  112. volatile uint32_t org = *address;
  113. *address = (org & ~mask)| (value & mask);
  114. lua_pushinteger(L, *address);
  115. } else {
  116. lua_pushinteger(L, *address);
  117. }
  118. return 1;
  119. }
  120. /*
  121. 转换10进制数为16进制字符串输出
  122. @api mcu.x32(value)
  123. @int 需要转换的值
  124. @return string 16进制字符串
  125. @usage
  126. local value = mcu.x32(0x2009FFFC) --输出"0x2009fffc"
  127. */
  128. static int l_mcu_x32(lua_State* L) {
  129. uint32_t value = luaL_checkinteger(L, 1);
  130. char c[16];
  131. sprintf_(c, "0x%lx", value);
  132. lua_pushstring(L, c);
  133. return 1;
  134. }
  135. // #ifdef __LUATOS_TICK_64BIT__
  136. /*
  137. 获取启动后的高精度tick,如果支持bit64库,可以直接输出转换好的bit64结构
  138. @api mcu.tick64()
  139. @boolean 是否输出bit64结构,true是,其他都是false,留空也是false,用于兼容旧的demo
  140. @return string 当前tick值,8个字节的uint64,如果支持64bit库,同时要求输出64bit结构的话,会输出9字节的string
  141. @return int 1us有几个tick,0表示未知
  142. @usage
  143. local tick_str, tick_per = mcu.tick64()
  144. print("ticks", tick_str, tick_per)
  145. */
  146. static int l_mcu_hw_tick64(lua_State* L) {
  147. uint64_t tick = luat_mcu_tick64();
  148. uint32_t us_period = luat_mcu_us_period();
  149. #ifdef LUAT_USE_BIT64
  150. if (lua_isboolean(L, 1) && lua_toboolean(L, 1))
  151. {
  152. uint8_t data[9] = {0};
  153. memcpy(data, &tick, 8);
  154. lua_pushlstring(L, (const char*)data, 9);
  155. }
  156. else
  157. {
  158. lua_pushlstring(L, (const char*)&tick, 8);
  159. }
  160. #else
  161. lua_pushlstring(L, (const char*)&tick, 8);
  162. #endif
  163. lua_pushinteger(L, us_period);
  164. return 2;
  165. }
  166. /*
  167. 计算2个64bit tick的差值
  168. @api mcu.dtick64(tick1, tick2, check_value)
  169. @string 64bit的string
  170. @string 64bit的string
  171. @int 参考值,可选项,如果为0,则返回结果中第一个项目为true
  172. @return boolean 与参考值比较,如果大于等于为true,反之为false
  173. @return int 差值tick1 - tick2,如果超过了0x7fffffff,结果可能是错的
  174. @usage
  175. local result, diff_tick = mcu.dtick64(tick1, tick2)
  176. print("ticks", result, diff_tick)
  177. */
  178. static int l_mcu_hw_diff_tick64(lua_State* L) {
  179. uint64_t tick1, tick2;
  180. int64_t diff;
  181. int check_value = 0;
  182. size_t len1;
  183. const char *data1 = luaL_checklstring(L, 1, &len1);
  184. size_t len2;
  185. const char *data2 = luaL_checklstring(L, 2, &len2);
  186. check_value = luaL_optinteger(L, 3, 0);
  187. memcpy(&tick1, data1, len1);
  188. memcpy(&tick2, data2, len2);
  189. diff = tick1 - tick2;
  190. lua_pushboolean(L, (diff >= (int64_t)check_value)?1:0);
  191. lua_pushinteger(L, diff);
  192. return 2;
  193. }
  194. /*
  195. 选择时钟源,当前仅air105支持
  196. @api mcu.setXTAL(source_main, source_32k, delay)
  197. @boolean 高速时钟是否使用外部时钟源,如果为空则不改变
  198. @boolean 低速32K是否使用外部时钟源,如果为空则不改变
  199. @int PLL稳定时间,在切换高速时钟的时候,根据硬件环境,需要delay一段时间等待PLL稳定,默认是1200,建议不小于1024
  200. @usage
  201. mcu.setXTAL(true, true, 1248) --高速时钟使用外部时钟,低速32K使用外部晶振, delay1248
  202. */
  203. static int l_mcu_set_xtal(lua_State* L) {
  204. int source_main = 255;
  205. int source_32k = 255;
  206. int delay = luaL_optinteger(L, 3, 1200);
  207. if (lua_isboolean(L, 1)) {
  208. source_main = lua_toboolean(L, 1);
  209. }
  210. if (lua_isboolean(L, 2)) {
  211. source_32k = lua_toboolean(L, 2);
  212. }
  213. luat_mcu_set_clk_source(source_main, source_32k, delay);
  214. return 0;
  215. }
  216. // #endif
  217. #ifdef LUAT_COMPILER_NOWEAK
  218. #else
  219. LUAT_WEAK void luat_mcu_set_hardfault_mode(int mode) {;}
  220. LUAT_WEAK void luat_mcu_xtal_ref_output(uint8_t main_enable, uint8_t slow_32k_enable) {;}
  221. LUAT_WEAK int luat_uart_pre_setup(int uart_id, uint8_t use_alt_type){return -1;}
  222. LUAT_WEAK int luat_i2c_set_iomux(int id, uint8_t value){return -1;}
  223. #endif
  224. /*
  225. mcu死机时处理模式
  226. @api mcu.hardfault(mode)
  227. @int 处理模式,0死机停机,1死机后重启,2死机后尽量将错误信息提交给外部工具后重启 3.死机时写入关键信息到flash后立刻重启
  228. @usage
  229. mcu.hardfault(0) --死机后停机,一般用于调试状态
  230. mcu.hardfault(1) --死机后重启,一般用于正式产品
  231. mcu.hardfault(2) --死机后尽量将错误信息提交给外部工具后重启,一般用于压力测试或者正式产品
  232. */
  233. static int l_mcu_set_hardfault_mode(lua_State* L)
  234. {
  235. luat_mcu_set_hardfault_mode(luaL_optinteger(L, 1, 0));
  236. return 0;
  237. }
  238. /*
  239. 在外设打开前,将外设IO复用到非默认配置上,目前只支持Air780E的部分外设复用到其他配置,这是一个临时接口,如果后续有更合适的api,本接口将不再更新
  240. @api mcu.iomux(type, channel, value)
  241. @int 外设类型,目前只有mcu.UART,mcu.I2C
  242. @int 总线序号,0~N,
  243. @int 新的配置,这个需要根据具体平台决定
  244. @usage
  245. mcu.iomux(mcu.UART, 2, 1) -- Air780E的UART2复用到gpio12和gpio13(Air780EG默认是这个复用,不要动)
  246. mcu.iomux(mcu.UART, 2, 2) -- Air780E的UART2复用到gpio6和gpio7
  247. mcu.iomux(mcu.I2C, 0, 1) -- Air780E的I2C0复用到gpio12和gpio13
  248. mcu.iomux(mcu.I2C, 0, 2) -- Air780E的I2C0复用到gpio16和gpio17
  249. mcu.iomux(mcu.I2C, 1, 1) -- Air780E的I2C1复用到gpio4和gpio5
  250. */
  251. static int l_mcu_iomux(lua_State* L)
  252. {
  253. int type = luaL_optinteger(L, 1, 0xff);
  254. int channel = luaL_optinteger(L, 2, 0xff);
  255. int value = luaL_optinteger(L, 3, 0);
  256. LLOGD("mcu iomux %d,%d,%d", type, channel, value);
  257. switch(type)
  258. {
  259. case LUAT_MCU_PERIPHERAL_UART:
  260. luat_uart_pre_setup(channel, value);
  261. break;
  262. case LUAT_MCU_PERIPHERAL_I2C:
  263. luat_i2c_set_iomux(channel, value);
  264. break;
  265. }
  266. return 0;
  267. }
  268. /*
  269. IO外设功能复用选择,注意普通MCU通常是以GPIO号为唯一ID号,但是专用SOC,比如CAT1的,可能以PAD号或者模块pin脚号(pin.xxx后续支持)为唯一ID号。本函数不是所有平台适用
  270. @api mcu.altfun(type, sn, pad_index, alt_fun, is_input)
  271. @int 外设类型,目前有mcu.UART,mcu.I2C,mcu.SPI,mcu.PWM,mcu.GPIO,mcu.I2S,mcu.LCD,mcu.CAM,具体需要看平台
  272. @int 总线序号,0~N,如果是mcu.GPIO,则是GPIO号。具体看平台的IOMUX复用表
  273. @int 唯一ID号,如果留空不写,则表示清除配置,使用平台的默认配置。具体看平台的IOMUX复用表
  274. @int 复用功能序号,0~N。具体看平台的IOMUX复用表
  275. @boolean 是否是输入功能,true是,留空是false
  276. @usage
  277. -- UART2复用到paddr 25/26 alt 3
  278. mcu.altfun(mcu.UART,2, 25, 3, true)
  279. mcu.altfun(mcu.UART,2, 26, 3, false)
  280. -- Air8101的SDIO复用演示, 1线模式, 暂不支持4线
  281. mcu.altfun(mcu.SDI0,0,14)
  282. mcu.altfun(mcu.SDI0,0,15)
  283. mcu.altfun(mcu.SDI0,0,16)
  284. // mcu.altfun(mcu.SDI0,0,17)
  285. // mcu.altfun(mcu.SDI0,0,18)
  286. // mcu.altfun(mcu.SDI0,0,19)
  287. */
  288. static int l_mcu_alt_ctrl(lua_State* L)
  289. {
  290. #ifdef LUAT_MCU_IOMUX_CTRL
  291. int type = luaL_optinteger(L, 1, 0xff);
  292. int sn = luaL_optinteger(L, 2, 0xff);
  293. int pad = luaL_optinteger(L, 3, -1);
  294. int alt_fun = luaL_optinteger(L, 4, 0);
  295. uint8_t is_input = 0;
  296. if (lua_isboolean(L, 5))
  297. {
  298. is_input = lua_toboolean(L, 5);
  299. }
  300. LLOGD("mcu altfun %d,%d,%d,%d,%d", type, sn, pad, alt_fun, is_input);
  301. luat_mcu_iomux_ctrl(type, sn, pad, alt_fun, is_input);
  302. #else
  303. LLOGW("no support mcu.altfun");
  304. #endif
  305. return 0;
  306. }
  307. /*
  308. 获取高精度的计数
  309. @api mcu.ticks2(mode)
  310. @int 模式, 看后面的用法说明
  311. @return int 根据mode的不同,返回值的含义不同
  312. @usage
  313. -- 本函数于2024.5.7新增
  314. -- 与mcu.ticks()的区别是,底层计数器是64bit的, 在可预计的将来不会溢出
  315. -- 所以本函数返回的值总是递增的, 而且32bit固件也能处理
  316. -- 模式可选值 及 对应的返回值
  317. -- 0: 返回微秒数, 以秒为分割, 例如 1234567890us 返回2个值: 1234, 567890
  318. -- 1: 返回毫秒数, 以千秒为分割, 例如 1234567890ms 返回2个值: 1234, 567890
  319. -- 2: 返回秒数, 以百万秒为分割, 例如 1234567890s 返回2个值: 1234, 567890
  320. local us_h, us_l = mcu.ticks2(0)
  321. local ms_h, ms_l = mcu.ticks2(1)
  322. local sec_h, sec_l = mcu.ticks2(2)
  323. log.info("us_h", us_h, "us_l", us_l)
  324. log.info("ms_h", ms_h, "ms_l", ms_l)
  325. log.info("sec_h", sec_h, "sec_l", sec_l)
  326. */
  327. static int l_mcu_ticks2(lua_State* L) {
  328. int mode = luaL_optinteger(L, 1, 0);
  329. uint64_t tick = luat_mcu_tick64();
  330. uint32_t us_period = luat_mcu_us_period();
  331. uint64_t us = tick / us_period;
  332. uint64_t ms = us / 1000;
  333. uint64_t sec = ms / 1000;
  334. switch (mode)
  335. {
  336. case 0:
  337. // 模式0, 返回微秒数
  338. lua_pushinteger(L, (uint32_t)(us / 1000000));
  339. lua_pushinteger(L, (uint32_t)(us % 1000000));
  340. return 2;
  341. case 1:
  342. // 模式1, 返回毫秒数
  343. lua_pushinteger(L, (uint32_t)(ms / 1000000));
  344. lua_pushinteger(L, (uint32_t)(ms % 1000000));
  345. return 2;
  346. case 2:
  347. // 模式2, 返回秒数
  348. lua_pushinteger(L, (uint32_t)(sec / 1000000));
  349. lua_pushinteger(L, (uint32_t)(sec % 1000000));
  350. return 2;
  351. default:
  352. break;
  353. }
  354. return 0;
  355. }
  356. /*
  357. 晶振参考时钟输出
  358. @api mcu.XTALRefOutput(source_main, source_32k)
  359. @boolean 高速晶振参考时钟是否输出
  360. @boolean 低速32K晶振参考时钟是否输出
  361. @usage
  362. -- 本函数于2024.5.17新增
  363. -- 当前仅Air780EXXX系列支持
  364. mcu.XTALRefOutput(true, false) --高速晶振参考时钟输出,低速32K不输出
  365. */
  366. static int l_mcu_xtal_ref_output(lua_State* L) {
  367. int source_main = 0;
  368. int source_32k = 0;
  369. // int delay = luaL_optinteger(L, 3, 1200);
  370. if (lua_isboolean(L, 1)) {
  371. source_main = lua_toboolean(L, 1);
  372. }
  373. if (lua_isboolean(L, 2)) {
  374. source_32k = lua_toboolean(L, 2);
  375. }
  376. luat_mcu_xtal_ref_output(source_main, source_32k);
  377. return 0;
  378. }
  379. #include "rotable2.h"
  380. static const rotable_Reg_t reg_mcu[] =
  381. {
  382. { "setClk" , ROREG_FUNC(l_mcu_set_clk)},
  383. { "getClk", ROREG_FUNC(l_mcu_get_clk)},
  384. { "unique_id", ROREG_FUNC(l_mcu_unique_id)},
  385. { "ticks", ROREG_FUNC(l_mcu_ticks)},
  386. { "hz", ROREG_FUNC(l_mcu_hz)},
  387. { "reg32", ROREG_FUNC(l_mcu_reg32)},
  388. { "x32", ROREG_FUNC(l_mcu_x32)},
  389. // #ifdef __LUATOS_TICK_64BIT__
  390. { "tick64", ROREG_FUNC(l_mcu_hw_tick64)},
  391. { "dtick64", ROREG_FUNC(l_mcu_hw_diff_tick64)},
  392. { "setXTAL", ROREG_FUNC(l_mcu_set_xtal)},
  393. { "hardfault", ROREG_FUNC(l_mcu_set_hardfault_mode)},
  394. #ifdef LUAT_MCU_IOMUX_CTRL
  395. { "altfun", ROREG_FUNC(l_mcu_alt_ctrl)},
  396. #else
  397. { "iomux", ROREG_FUNC(l_mcu_iomux)},
  398. #endif
  399. { "ticks2", ROREG_FUNC(l_mcu_ticks2)},
  400. { "XTALRefOutput", ROREG_FUNC(l_mcu_xtal_ref_output)},
  401. // #endif
  402. //@const UART number 外设类型-串口
  403. { "UART", ROREG_INT(LUAT_MCU_PERIPHERAL_UART) },
  404. //@const I2C number 外设类型-I2C
  405. { "I2C", ROREG_INT(LUAT_MCU_PERIPHERAL_I2C) },
  406. //@const SPI number 外设类型-SPI
  407. { "SPI", ROREG_INT(LUAT_MCU_PERIPHERAL_SPI) },
  408. //@const PWM number 外设类型-PWM
  409. { "PWM", ROREG_INT(LUAT_MCU_PERIPHERAL_PWM) },
  410. //@const PWM number 外设类型-CAN
  411. { "CAN", ROREG_INT(LUAT_MCU_PERIPHERAL_CAN) },
  412. //@const GPIO number 外设类型-GPIO
  413. { "GPIO", ROREG_INT(LUAT_MCU_PERIPHERAL_GPIO) },
  414. //@const I2S number 外设类型-I2S, 音频总线
  415. { "I2S", ROREG_INT(LUAT_MCU_PERIPHERAL_I2S) },
  416. //@const LCD number 外设类型-LCD, LCD专用总线
  417. { "LCD", ROREG_INT(LUAT_MCU_PERIPHERAL_LCD) },
  418. //@const CAM number 外设类型-CAM,与 mcu.CAMERA一样
  419. { "CAM", ROREG_INT(LUAT_MCU_PERIPHERAL_CAMERA) },
  420. //@const CAMERA number 外设类型-CAMERA,就是CAM,摄像头
  421. { "CAMERA", ROREG_INT(LUAT_MCU_PERIPHERAL_CAMERA) },
  422. //@const ONEWIRE number 外设类型-ONEWIRE,单总线协议
  423. { "ONEWIRE", ROREG_INT(LUAT_MCU_PERIPHERAL_ONEWIRE) },
  424. //@const SDIO number 外设类型-SDIO,接TF卡
  425. { "SDIO", ROREG_INT(LUAT_MCU_PERIPHERAL_SDIO) },
  426. //@const KEYBORAD number 外设类型-KEYBORAD,键盘
  427. { "KEYBORAD", ROREG_INT(LUAT_MCU_PERIPHERAL_KEYBORAD) },
  428. //@const ETH number 外设类型-ETH,网口
  429. { "ETH", ROREG_INT(LUAT_MCU_PERIPHERAL_ETH) },
  430. { NULL, ROREG_INT(0) }
  431. };
  432. LUAMOD_API int luaopen_mcu( lua_State *L ) {
  433. luat_newlib2(L, reg_mcu);
  434. return 1;
  435. }