can_normal.lua 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. --[[
  2. @module can_normal
  3. @summary CAN总线正常工作模式使用示例
  4. @version 1.0
  5. @date 2025.11.25
  6. @author 魏健强
  7. @usage
  8. 本文件为CAN总线正常工作模式使用示例,核心业务逻辑为:
  9. 1. 初始化CAN总线
  10. 2. 发送和接收CAN总线数据
  11. 本文件没有对外接口,直接在main.lua模块中require "can_normal"就可以加载运行;
  12. ]]
  13. local can_id = 0
  14. local stb_pin = 28 -- 780EPM V1.3开发板上STB引脚为GPIO28
  15. local rx_id = 0x12345677
  16. local tx_id = 0x12345678
  17. local test_cnt = 0
  18. local tx_buf = zbuff.create(8) -- 创建zbuff
  19. local send_queue = {} -- 发送队列
  20. local MAX_SEND_QUEUE_LEN = 50 -- 发送队列最大长度
  21. local send_res = false
  22. -- 数据插入发送队列
  23. local function can_send_data(id, msg_id, id_type, RTR, need_ack, data)
  24. if #send_queue >= MAX_SEND_QUEUE_LEN then
  25. log.error("can_send_data", "send queue full")
  26. end
  27. table.insert(send_queue, {
  28. id = id,
  29. msg_id = msg_id,
  30. id_type = id_type,
  31. RTR = RTR,
  32. need_ack = need_ack,
  33. data = data
  34. })
  35. sys.publish("CAN_SEND_DATA_EVENT")
  36. end
  37. local function can_cb(id, cb_type, param)
  38. if cb_type == can.CB_MSG then
  39. log.info("有新的消息")
  40. local succ, id, id_type, rtr, data = can.rx(id)
  41. while succ do
  42. log.info(mcu.x32(id), #data, data:toHex())
  43. succ, id, id_type, rtr, data = can.rx(id)
  44. end
  45. end
  46. if cb_type == can.CB_TX then
  47. if param then
  48. log.info("发送成功")
  49. send_res = true
  50. else
  51. log.info("发送失败")
  52. send_res = false
  53. end
  54. sys.publish("CAN_SEND_DATA_RES")
  55. end
  56. if cb_type == can.CB_ERR then
  57. -- param参数就是4字节错误码
  58. log.error("CAN错误", "错误码:", string.format("0x%08X", param))
  59. -- 解析错误码
  60. local direction = (param >> 16) & 0xFF -- byte2: 方向
  61. local error_type = (param >> 8) & 0xFF -- byte1: 错误类型
  62. local position = param & 0xFF -- byte0: 错误位置
  63. -- 判断错误方向
  64. if direction == 0 then
  65. log.info("错误方向", "发送错误")
  66. else
  67. log.info("错误方向", "接收错误")
  68. end
  69. -- 判断错误类型
  70. if error_type == 0 then
  71. log.info("错误类型", "位错误")
  72. elseif error_type == 1 then
  73. log.info("错误类型", "格式错误")
  74. elseif error_type == 2 then
  75. log.info("错误类型", "填充错误")
  76. end
  77. -- 输出错误位置
  78. log.info("错误位置", string.format("0x%02X", position))
  79. end
  80. if cb_type == can.CB_STATE then
  81. -- 获取总线状态
  82. local state = can.state(can_id)
  83. log.info("can.state", "当前状态", state)
  84. -- 根据状态处理
  85. if state == can.STATE_ACTIVE then
  86. log.info("can.state", "总线正常")
  87. elseif state == can.STATE_PASSIVE then
  88. log.warn("can.state", "被动错误状态")
  89. elseif state == can.STATE_BUSOFF then
  90. log.error("can.state", "总线离线")
  91. -- 需要手动恢复
  92. can.reset(can_id)
  93. end
  94. end
  95. end
  96. local function can_tx_test(data)
  97. while true do
  98. sys.wait(10000)
  99. test_cnt = test_cnt + 1
  100. if test_cnt > 8 then
  101. test_cnt = 1
  102. end
  103. tx_buf:set(0, test_cnt) -- zbuff的类似于memset操作,类似于memset(&buff[start], num, len)
  104. tx_buf:seek(test_cnt) -- zbuff设置光标位置(可能与当前指针位置有关;执行后指针会被设置到指定位置)
  105. can_send_data(can_id, 0x123, can.STD, false, true, "Hello") -- 发送标准帧数据
  106. can_send_data(can_id, 0x123, can.STD, true, true, "") -- 发送遥控帧数据
  107. can_send_data(can_id, tx_id, can.EXT, false, true, tx_buf)--发送扩展帧数据
  108. end
  109. end
  110. -- can.debug(true)
  111. -- gpio.setup(stb_pin,0) -- 配置STB引脚为输出低电平
  112. gpio.setup(stb_pin, 1) -- 780EPM V1.3开发板STB信号有逻辑取反,要配置成输出高电
  113. can.init(can_id, 128) -- 初始化CAN,参数为CAN ID,接收缓存消息数的最大值
  114. can.on(can_id, can_cb) -- 注册CAN的回调函数
  115. can.timing(can_id, 1000000, 6, 6, 4, 2) -- CAN总线配置时序
  116. -- 接收消息过滤(以下四行代码四选一使用)
  117. can.node(can_id, rx_id, can.EXT) -- 只接收消息id为rx_id的扩展帧数据
  118. -- can.filter(can_id, false, 0x123 << 21, 0x07ffffff) -- 接收消息id为0x12开头的标准帧数据,0x120~0x12f
  119. -- can.filter(can_id, false, 0x12345678 << 3, 0x07ffff) -- 接收消息id为0x1234开头的扩展帧数据,0x12340000~0x1234ffff
  120. -- can.filter(can_id, false, 0, 0xFFFFFFFF) -- 接收所有消息
  121. -- 模式
  122. can.mode(can_id, can.MODE_NORMAL) -- 一旦设置mode就开始正常工作了,此时不能再设置node,timing,filter等
  123. local resend_num = 5 -- 发送失败重发次数
  124. local function send_task()
  125. local send_item
  126. local result, buff_full
  127. -- 遍历数据发送队列send_queue
  128. while true do
  129. sys.waitUntil("CAN_SEND_DATA_EVENT",1000)
  130. while #send_queue > 0 do
  131. send_res = false
  132. -- 取数据发送
  133. send_item = table.remove(send_queue, 1)
  134. local resend_cnt = 0
  135. while not send_res do
  136. can.tx(send_item.id, send_item.msg_id, send_item.id_type, send_item.RTR, send_item.need_ack,
  137. send_item.data)
  138. sys.waitUntil("CAN_SEND_DATA_RES",500)
  139. sys.wait(10)
  140. resend_cnt = resend_cnt + 1
  141. if resend_cnt >= resend_num then
  142. log.warn("can send", "重发次数到达上限,停止重发")
  143. -- 默认直接丢弃数据
  144. -- table.insert(send_queue, send_item) -- 重新插入发送队列
  145. break
  146. end
  147. end
  148. end
  149. end
  150. end
  151. sys.taskInit(send_task)
  152. sys.taskInit(can_tx_test)