exvib1.lua 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. --[[
  2. @summary exvib1扩展库
  3. @version 1.0
  4. @date 2025.09.07
  5. @author 孟伟
  6. @usage
  7. -- 应用场景
  8. 此库适用于滚珠震动传感器BL_2529,主要目的是对振动中断进行过滤,识别有效震动
  9. 对于一些震动传感器的中断管脚算法处理,也可以用做参考。
  10. 实现的功能:
  11. 1. GPIO 中断检测:通过 GPIO 引脚检测震动传感器产生的脉冲信号
  12. 2. 双重消抖机制:
  13. - io中断消抖 gpio.debounce()
  14. 3. 时间窗口检测:在指定时间窗口(time_window)内统计脉冲数量
  15. 4. 阈值触发:当脉冲数超过设定阈值(pulse_threshold)时触发回调
  16. 5. 脉冲超时机制:在检测状态下,如果超过pulse_timeout时间没有新的脉冲,则提前结束当前检测周期并判断是否触发回调
  17. 状态机工作流程:
  18. - IDLE状态:等待第一个有效脉冲
  19. - DETECTING状态:进入检测窗口,统计脉冲数量
  20. - 触发条件:
  21. 时间窗口结束
  22. 脉冲空闲时间超过设定超时
  23. - 结果判断:脉冲数≥阈值则调用用户回调
  24. -- 用法实例
  25. 本扩展库对外提供了以下2个接口:
  26. 1)启动震动检测功能 exvib1.open(opts)
  27. 2)停止震动检测功能 exvib1.close()
  28. --加载exvib1扩展库
  29. local exvib1= require "exvib1"
  30. -- 震动事件回调
  31. local function vibration_cb(pulse_cnt)
  32. log.info("VIB", "detected! pulses =", pulse_cnt)
  33. end
  34. --演示最简单的使用方法,都使用默认配置
  35. exvib1.open({
  36. gpio_pin = 24,
  37. on_event = vibration_cb,
  38. })
  39. 以下为exvib1扩展库两个函数的详细说明及代码实现:
  40. ]]
  41. local exvib1 = {}
  42. -- 默认配置
  43. local cfg = {
  44. gpio_pin = nil, -- 传感器中断所接 GPIO
  45. pull = gpio.PULLUP,
  46. trigger = gpio.RISING,
  47. debounce_irq = 100, -- gpio消抖时间,gpio.debounce 时间(ms)
  48. time_window = 1000, -- 检测窗口(ms)
  49. pulse_threshold = 3, -- 触发阈值
  50. pulse_timeout = 200, -- 脉冲超时(ms)
  51. poll_interval = 10, -- 状态机轮询(ms)
  52. on_event = nil, -- 用户回调
  53. }
  54. -- 内部状态
  55. local st = {
  56. pulse_cnt = 0,
  57. last_valid = 0,
  58. detect_t0 = 0,
  59. state = "IDLE",
  60. }
  61. -- 重置内部状态,将状态机置为空闲状态并清零脉冲计数
  62. local function reset()
  63. st.state = "IDLE"
  64. st.pulse_cnt = 0
  65. end
  66. -- GPIO 中断处理函数,用于处理传感器的脉冲信号
  67. local function isr()
  68. local now = mcu.ticks()
  69. st.pulse_cnt = st.pulse_cnt + 1
  70. st.last_valid = now
  71. -- 如果当前状态为空闲状态
  72. if st.state == "IDLE" then
  73. -- 切换到检测状态
  74. st.state = "DETECTING"
  75. -- 记录检测开始时间
  76. st.detect_t0 = now
  77. end
  78. end
  79. -- 状态机处理函数,用于检测是否满足震动触发条件
  80. local function fsm()
  81. -- 如果当前状态不是检测状态,则直接返回
  82. if st.state ~= "DETECTING" then return end
  83. local now = mcu.ticks()
  84. -- 处理时间戳溢出情况
  85. if now < st.detect_t0 or now < st.last_valid then
  86. st.detect_t0 = 0
  87. st.last_valid = 0
  88. return -- 等待下次调用重新判断
  89. end
  90. -- 计算从检测开始到现在经过的时间
  91. local elapsed = now - st.detect_t0
  92. -- 判断是否脉冲空闲时间过长
  93. local idle_too_long = (now - st.last_valid) >= cfg.pulse_timeout
  94. -- 当检测窗口结束或者脉冲空闲时间过长时
  95. if elapsed >= cfg.time_window or idle_too_long then
  96. -- 检查脉冲计数是否达到触发阈值,并且用户回调函数存在
  97. if st.pulse_cnt >= cfg.pulse_threshold and st.on_event then
  98. -- 调用用户回调函数并传入脉冲计数值
  99. st.on_event(st.pulse_cnt)
  100. end
  101. -- 重置内部状态
  102. reset()
  103. end
  104. end
  105. --[[
  106. 启动震动检测功能
  107. @api exvib1.open(opts)
  108. @table opts 配置参数表,用于自定义震动检测功能的各项属性。
  109. @return nil 无返回值
  110. @usage
  111. -- 配置参数介绍
  112. --local otps = {
  113. -- gpio_pin --"传感器中断所接 GPIO 引脚号,默认值为 nil",
  114. -- pull --"上拉/下拉模式,可选 gpio.PULLUP 或 gpio.PULLDOWN,默认值为 gpio.PULLUP",
  115. -- trigger --"触发方式,可选 gpio.RISING 或 gpio.FALLING,默认值为 gpio.RISING",
  116. -- debounce_irq --"GPIO 消抖时间,单位为毫秒,默认值为 100",
  117. -- time_window --"检测窗口时间,单位为毫秒,默认值为 1000",
  118. -- pulse_threshold --"触发阈值,即连续脉冲次数,默认值为 3",
  119. -- pulse_timeout --"脉冲超时时间,单位为毫秒,默认值为 200",
  120. -- poll_interval --"状态机轮询时间,单位为毫秒,默认值为 10",
  121. -- on_event --"用户回调函数,用于处理检测到的震动事件,默认值为 nil",
  122. --}
  123. -- 震动事件回调
  124. local function vibration_cb(pulse_cnt)
  125. log.info("VIB", "detected! pulses =", pulse_cnt)
  126. end
  127. exvib1.open({
  128. gpio_pin = 24,
  129. on_event = vibration_cb,
  130. })
  131. --不同场景下的参数配置可参考下面的示例
  132. --高灵敏度,响应快,误触可能高
  133. exvib1.open({
  134. gpio_pin = 24,
  135. on_event = vibration_cb,
  136. time_window = 300, -- 检测窗口(ms)
  137. pulse_threshold = 1, -- 触发阈值
  138. pulse_timeout = 100, -- 脉冲超时(ms)
  139. })
  140. --默认配置,较高灵敏度
  141. exvib1.open({
  142. gpio_pin = 24,
  143. on_event = vibration_cb,
  144. time_window = 1000, -- 检测窗口(ms)
  145. pulse_threshold = 3, -- 触发阈值
  146. pulse_timeout = 200, -- 脉冲超时(ms)
  147. })
  148. --中等灵敏度,
  149. exvib1.open({
  150. gpio_pin = 24,
  151. on_event = vibration_cb,
  152. time_window = 2000, -- 检测窗口(ms)
  153. pulse_threshold = 3, -- 触发阈值
  154. pulse_timeout = 300, -- 脉冲超时(ms)
  155. })
  156. --低灵敏度,减少误报
  157. exvib1.open({
  158. gpio_pin = 24,
  159. on_event = vibration_cb,
  160. time_window = 3000, -- 检测窗口(ms)
  161. pulse_threshold = 10, -- 触发阈值
  162. pulse_timeout = 500, -- 脉冲超时(ms)
  163. })
  164. ]]
  165. -- 启动震动检测功能
  166. function exvib1.open(opts)
  167. -- 如果没有传入配置参数,则使用空表
  168. opts = opts or {}
  169. -- 用传入的配置参数更新默认配置
  170. for k, v in pairs(opts) do cfg[k] = v end
  171. -- 更新用户回调函数,如果传入了新的回调则使用新的,否则保持原有回调
  172. st.on_event = opts.on_event or st.on_event
  173. -- 配置 GPIO 消抖时间,设置中断处理函数、上拉模式和触发方式
  174. gpio.debounce(cfg.gpio_pin, cfg.debounce_irq)
  175. gpio.setup(cfg.gpio_pin, isr, cfg.pull, cfg.trigger)
  176. -- 启动定时器循环调用状态机处理函数
  177. sys.timerLoopStart(fsm, cfg.poll_interval)
  178. log.info("Vibration", "start on gpio", cfg.gpio_pin)
  179. end
  180. --[[
  181. 关闭震动检测功能
  182. @api exvib1.close()
  183. @return nil 无返回值
  184. @usage
  185. exvib1.close() --关闭震动检测功能
  186. --]]
  187. function exvib1.close()
  188. -- 关闭 GPIO 引脚
  189. gpio.close(cfg.gpio_pin)
  190. -- 停止定时器
  191. sys.timerStop(fsm)
  192. reset()
  193. end
  194. return exvib1