基于RT-Thread的CAN电机驱动板设计 (四)使用PIN设备配置按键中断实现电机启停

引言

在前几篇文章中实现了电机驱动板需要实现的大部分功能,本来想进一步加一点锦上添花的小功能即配置按键中断向电机发送CAN指令实现电机的启动与停止,但是在这个过程中也遇到了不少问题,所以记录下来。

资料连接

本项目的所有资料全部开源:

硬件工程https://lceda.cn/FranHawk/485tocan_motor_controller
软件工程https://github.com/FranHawk/RT-Thread-485toCAN

前期准备

  • 制作好的电机驱动板一块
  • USB转485模块一个,用来插在PC上实现485协议与电机驱动板通讯
  • rtthread studio开发环境

电机启动停止指令


开发过程

主要参照了rtthread官方的pin设备文档,但是发现根据PIN设备文档来配置仍存在一些问题,这个后面会提到
官方文档链接:
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/pin/pin
根据官方文档,PIN设备的接口如下

通过PIN接口为按键绑定中断函数,官方文档中给出的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define KEY0_PIN_NUM            55  /* PD8 */
/* 中断回调函数 */
void beep_on(void *args)
{
rt_kprintf("turn on beep!\n");

rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
}
static void pin_beep_sample(void)
{
/* 按键0引脚为输入模式 */
rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
/* 绑定中断,上升沿模式,回调函数名为beep_on */
rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
}

我刚开始编写代码的时候,按照示例代码编写发现根本进入不了中断服务函数,最后通过在rtthread studio中查找pin设备的API发现下面这个例子。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-08-15 misonyo first implementation.
*/
/*
* 程序清单:这是一个 PIN 设备使用例程
* 例程导出了 pin_beep_sample 命令到控制终端
* 命令调用格式:pin_beep_sample
* 程序功能:通过按键控制蜂鸣器对应引脚的电平状态控制蜂鸣器
*/
#include <rtthread.h>
#include <rtdevice.h>
/* 引脚编号,通过查看驱动文件drv_gpio.c确定 */
#ifndef BEEP_PIN_NUM
#define BEEP_PIN_NUM 35 /* PB0 */
#endif
#ifndef KEY0_PIN_NUM
#define KEY0_PIN_NUM 55 /* PD8 */
#endif
#ifndef KEY1_PIN_NUM
#define KEY1_PIN_NUM 56 /* PD9 */
#endif
void beep_on(void *args)
{
rt_kprintf("turn on beep!\n");
rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
}
void beep_off(void *args)
{
rt_kprintf("turn off beep!\n");
rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
}
static void pin_beep_sample(void)
{
/* 蜂鸣器引脚为输出模式 */
rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT);
/* 默认低电平 */
rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
/* 按键0引脚为输入模式 */
rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
/* 绑定中断,上升沿模式,回调函数名为beep_on */
rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
/* 使能中断 */
rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE);
/* 按键1引脚为输入模式 */
rt_pin_mode(KEY1_PIN_NUM, PIN_MODE_INPUT_PULLUP);
/* 绑定中断,上升沿模式,回调函数名为beep_off */
rt_pin_attach_irq(KEY1_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_off, RT_NULL);
/* 使能中断 */
rt_pin_irq_enable(KEY1_PIN_NUM, PIN_IRQ_ENABLE);
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(pin_beep_sample, pin beep sample);

发现官网文档中的示例程序少了一句
rt_pin_irq_enable(KEY1_PIN_NUM, PIN_IRQ_ENABLE);
手动使能pin中断,这个问题卡了我半天…在这里记录下来。

具体代码

同样采用顶半处理和底半处理的结构,通过信号量完成同步,代码比较简单,重要的是别忘了rt_pin_irq_enable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//KEY0电机启动中断函数
void motor_on_irq(void *args)
{
rt_sem_release(&motor_on_sem);
rt_pin_write(LED0_PIN, PIN_HIGH);
}
//KEY1电机关闭中断函数
void motor_off_irq(void *args)
{
rt_sem_release(&motor_off_sem);
rt_pin_write(LED0_PIN, PIN_HIGH);
}
//电机启动底半处理
static void motor_on_thread_entry()
{

rt_size_t size;
struct rt_can_msg can_motor_on_msg;
/* 电机开启指令初始化 */
can_motor_on_msg.ide = RT_CAN_STDID; /* 标准格式 */
can_motor_on_msg.rtr = RT_CAN_DTR; /* 数据帧 */
can_motor_on_msg.len = 8; /* 数据长度为 8 */
can_motor_on_msg.data[0] = MOTOR_ON_CMD;/* 指令类型为状态查询 */
can_motor_on_msg.data[1] = 0x00;
can_motor_on_msg.data[2] = 0x00;
can_motor_on_msg.data[3] = 0x00;
can_motor_on_msg.data[4] = 0x00;
can_motor_on_msg.data[5] = 0x00;
can_motor_on_msg.data[6] = 0x00;
can_motor_on_msg.data[7] = 0x00;
while (1)
{
/* 阻塞等待接收信号量 */
rt_sem_take(&motor_on_sem, RT_WAITING_FOREVER);
//rt_kprintf("power up motor!\n");
/* CAN向电机1发送数据 */
can_motor_on_msg.id = MOTOR1_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_on_msg, sizeof(can_motor_on_msg));

/* CAN向电机2发送数据 */
can_motor_on_msg.id = MOTOR2_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_on_msg, sizeof(can_motor_on_msg));

/* CAN向电机3发送数据 */
can_motor_on_msg.id = MOTOR3_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_on_msg, sizeof(can_motor_on_msg));

/* CAN向电机4发送数据 */
can_motor_on_msg.id = MOTOR4_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_on_msg, sizeof(can_motor_on_msg));

/* CAN向电机5发送数据 */
can_motor_on_msg.id = MOTOR5_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_on_msg, sizeof(can_motor_on_msg));

/* CAN向电机6发送数据 */
can_motor_on_msg.id = MOTOR6_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_on_msg, sizeof(can_motor_on_msg));

/* CAN向电机7发送数据 */
can_motor_on_msg.id = MOTOR7_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_on_msg, sizeof(can_motor_on_msg));

/* CAN向电机8发送数据 */
can_motor_on_msg.id = MOTOR8_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_on_msg, sizeof(can_motor_on_msg));
}
}
//电机停止底半处理
static void motor_off_thread_entry()
{

rt_size_t size;
struct rt_can_msg can_motor_off_msg;
/* 电机关闭指令初始化 */
can_motor_off_msg.ide = RT_CAN_STDID; /* 标准格式 */
can_motor_off_msg.rtr = RT_CAN_DTR; /* 数据帧 */
can_motor_off_msg.len = 8; /* 数据长度为 8 */
can_motor_off_msg.data[0] = MOTOR_OFF_CMD;/* 指令类型为状态查询 */
can_motor_off_msg.data[1] = 0x00;
can_motor_off_msg.data[2] = 0x00;
can_motor_off_msg.data[3] = 0x00;
can_motor_off_msg.data[4] = 0x00;
can_motor_off_msg.data[5] = 0x00;
can_motor_off_msg.data[6] = 0x00;
can_motor_off_msg.data[7] = 0x00;

while (1)
{
/* 阻塞等待接收信号量 */
rt_sem_take(&motor_off_sem, RT_WAITING_FOREVER);
//rt_kprintf("power off motor!\n");
/* CAN向电机1发送数据 */
can_motor_off_msg.id = MOTOR1_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_off_msg, sizeof(can_motor_off_msg));

/* CAN向电机2发送数据 */
can_motor_off_msg.id = MOTOR2_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_off_msg, sizeof(can_motor_off_msg));

/* CAN向电机3发送数据 */
can_motor_off_msg.id = MOTOR3_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_off_msg, sizeof(can_motor_off_msg));

/* CAN向电机4发送数据 */
can_motor_off_msg.id = MOTOR4_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_off_msg, sizeof(can_motor_off_msg));

/* CAN向电机5发送数据 */
can_motor_off_msg.id = MOTOR5_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_off_msg, sizeof(can_motor_off_msg));

/* CAN向电机6发送数据 */
can_motor_off_msg.id = MOTOR6_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_off_msg, sizeof(can_motor_off_msg));

/* CAN向电机7发送数据 */
can_motor_off_msg.id = MOTOR7_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_off_msg, sizeof(can_motor_off_msg));

/* CAN向电机8发送数据 */
can_motor_off_msg.id = MOTOR8_ID;
/* 发送一帧 CAN 数据 */
size = rt_device_write(can_dev, 0, &can_motor_off_msg, sizeof(can_motor_off_msg));
}
}
//电机启停线程初始化函数
static rt_err_t motor_on_off_thread_init(void)
{
rt_err_t res_on, res_off, res;
rt_thread_t thread_on, thread_off;
res = RT_EOK;

/* 按键0引脚为输入模式 */
rt_pin_mode(KEY0_PIN, PIN_MODE_INPUT_PULLUP);
/* 绑定中断,上升沿模式,回调函数名为motor_on */
rt_pin_attach_irq(KEY0_PIN, PIN_IRQ_MODE_FALLING, motor_on_irq, RT_NULL);
rt_pin_irq_enable(KEY0_PIN, PIN_IRQ_ENABLE);
/* 按键1引脚为输入模式 */
rt_pin_mode(KEY1_PIN, PIN_MODE_INPUT_PULLUP);
/* 绑定中断,上升沿模式,回调函数名为motor_on */
rt_pin_attach_irq(KEY1_PIN, PIN_IRQ_MODE_FALLING, motor_off_irq, RT_NULL);
rt_pin_irq_enable(KEY1_PIN, PIN_IRQ_ENABLE);
/* 初始化电机启动信号量 */
rt_sem_init(&motor_on_sem, "motor_on_sem", 0, RT_IPC_FLAG_FIFO);
/* 初始化电机启动信号量 */
rt_sem_init(&motor_off_sem, "motor_off_sem", 0, RT_IPC_FLAG_FIFO);

/* 创建电机启动线程,优先级为4 */
thread_on = rt_thread_create("motor_on", motor_on_thread_entry, RT_NULL, 1024, 4, 10);
if (thread_on != RT_NULL)
{
rt_thread_startup(thread_on);
}
else
{
res_on = RT_ERROR;
}
/* 创建电机停止线程,优先级为4 */
thread_off = rt_thread_create("motor_off", motor_off_thread_entry, RT_NULL, 1024, 4, 10);
if (thread_off != RT_NULL)
{
rt_thread_startup(thread_off);
}
else
{
res_off = RT_ERROR;
}
if ((res_on == RT_ERROR) || (res_off == RT_ERROR))
{
res = RT_ERROR;
}
return res;
}

总结

至此,整个基于RT-Thread的CAN电机驱动板项目开发完毕,学到了很多新东西,加深了我对rtthread中驱动还有线程间通信和同步的理解,这个项目的可扩展性比较强,不仅可以用于这款电机,还能用于其他CAN协议的电机,值得大家借鉴。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2021 张竞豪的小岛 All Rights Reserved.

UV : | PV :