如何通过树莓派的GPIO接口控制步进电机
1、实验目的
在树莓派上编写一个C程序,通过其GPIO口控制步进电机的转动方向以及速度。
2、为什么要用树莓派来控制步进电机
在我的概念中,无论是控制接在树莓派上的摄像头来拍照,还是通过树莓派控制LED发光,我都还是在“虚拟世界”中折腾树莓派,因为它没有向外界输出任何动作。而在构建一个稍微复杂的系统时,这种能力可能是很重要的,于是,用树莓派来控制步进电机是我必须要做的事情。
3、实现方案
怎样通过Raspberry Pi的GPIO口来控制步进电机?已经有很多人这样做了。由于步进电机通常需要一块驱动板,而这样的产品已经很容易买到并且极其廉价(肯定比你自己做要便宜),因此,我们没有必要自己去做。我在淘宝上不断地查啊查,终于找到了白菜价的步进电机和配套驱动板,加起来才一共8块多钱。
下面来看看我买的步进电机和驱动板的样子:
步进电机和驱动板的接线:
该驱动板配有一个原理图,根据它我们可以知道如何把整个系统连接起来:
可见,驱动板上有4个输入口:IN1~IN4,这4个口用来接树莓派的4个GPIO口。同时,我们需要为驱动板提供5V的供电,这从哪里来?当然是从树莓派引出来。从下面的GPIO口分布图可以得知,“2”口就是+5V,正好就利用它。
我将树莓派上的GPIO 17、18、21、22口(用树莓派的命名方式就是0、1、2、3口)分别接到步进电机驱动板上的IN1、IN2、IN3、IN4口,最后接好线的效果如下:
4、编写程序
在参考了这款步进电机配套的51 单片机的示例代码后,我知道了依次把驱动板的IN1~IN4置为高电平,就可以驱动步进电机,也就是说,要把树莓派的4个GPIO输出口依次置为高电平。例如,假设用0代表低电平,1代表高电平的话,GPIO 17、18、21、22口的电平第一次被置为1、0、0、0,第二次被置为0、1、0、0,第三次被置为0、0、1、0,第四次被置为0、0、0、1。
于是我写出了下面的代码(仍然使用WiringPi这个库来操作GPIO):
- /* moto.c
- * A program to control a stepper motor through the GPIO on Raspberry Pi.
- *
- * Author: Darran Zhang (http://www.codelast.com)
- */
-
- #include
- #include
- #include
- #include
-
- #define CLOCKWISE 1
- #define COUNTER_CLOCKWISE 2
-
- void delayMS(int x);
- void rotate(int* pins, int direction);
-
- int main(int argc,char* argv[]) {
- if (argc < 4) {
- printf("Usage example: ./motor 0 1 2 3 n");
- return 1;
- }
-
- /* number of the pins which connected to the stepper motor driver board */
- int pinA = atoi(argv[1]);
- int pinB = atoi(argv[2]);
- int pinC = atoi(argv[3]);
- int pinD = atoi(argv[4]);
-
- int pins[4] = {pinA, pinB, pinC, pinD};
-
- if (-1 == wiringPiSetup()) {
- printf("Setup wiringPi failed!");
- return 1;
- }
-
- /* set mode to output */
- pinMode(pinA, OUTPUT);
- pinMode(pinB, OUTPUT);
- pinMode(pinC, OUTPUT);
- pinMode(pinD, OUTPUT);
-
- delayMS(50); // wait for a stable status
- for (int i = 0; i < 500; i++) {
- rotate(pins, CLOCKWISE);
- }
-
- return 0;
- }
-
- /* Suspend execution for x milliseconds intervals.
- * @param ms Milliseconds to sleep.
- */
- void delayMS(int x) {
- usleep(x * 1000);
- }
-
- /* Rotate the motor.
- * @param pins A pointer which points to the pins number array.
- * @param direction CLOCKWISE for clockwise rotation, COUNTER_CLOCKWISE for counter clockwise rotation.
- */
- void rotate(int* pins, int direction) {
- for (int i = 0; i < 4; i++) {
- if (CLOCKWISE == direction) {
- for (int j = 0; j < 4; j++) {
- if (j == i) {
- digitalWrite(pins[3 - j], 1); // output a high level
- } else {
- digitalWrite(pins[3 - j], 0); // output a low level
- }
- }
- } else if (COUNTER_CLOCKWISE == direction) {
- for (int j = 0; j < 4; j++) {
- if (j == i) {
- digitalWrite(pins[j], 1); // output a high level
- } else {
- digitalWrite(pins[j], 0); // output a low level
- }
- }
- }
- delayMS(4);
- }
- }
复制代码
编译程序:
- g++ motor.c -o motor -lwiringPi
复制代码
运行程序:
这里向程序传入了4个参数,它们分别代表要控制的树莓派的GPIO口。切记,由于使用了WiringPi库,所以要参考上面的GPIO分布图的左边那部分来确定这些数字。
可以看到步进电机已经转动了(如下面的Youku视频所示)。如果你发现转动方向(顺/逆时针)反了,你可以把传入的参数顺序调整一下,就可以让它转对方向。
从前面的代码可见,如果要改变步进电机的转速,只需要改变rotate()函数中每次delay的时间即可。因此,如果我们把delay的时间逐渐由大变小,就会导致步进电机呈加速状态。让步进电机周期性加速的完整代码如下:
- /* motor_speed_up.c
- * A program to control a stepper motor(speed up) through the GPIO on Raspberry Pi.
- *
- * Author: Darran Zhang (http://www.codelast.com)
- */
-
- #include
- #include
- #include
- #include
-
- #define CLOCKWISE 1
- #define COUNTER_CLOCKWISE 2
-
- void delayMS(int x);
- void rotate(int* pins, int direction, int delay);
- void stop(int* pins);
-
- int main(int argc,char* argv[]) {
- if (argc < 4) {
- printf("Usage example: ./motor 0 1 2 3 n");
- return 1;
- }
-
- /* number of the pins which connected to the stepper motor driver board */
- int pinA = atoi(argv[1]);
- int pinB = atoi(argv[2]);
- int pinC = atoi(argv[3]);
- int pinD = atoi(argv[4]);
-
- int pins[4] = {pinA, pinB, pinC, pinD};
-
- if (-1 == wiringPiSetup()) {
- printf("Setup wiringPi failed!");
- return 1;
- }
-
- /* set mode to output */
- pinMode(pinA, OUTPUT);
- pinMode(pinB, OUTPUT);
- pinMode(pinC, OUTPUT);
- pinMode(pinD, OUTPUT);
-
- delayMS(50); // wait for a stable status
- int delay = 25;
- while (true) {
- for (int i = 0; i < 10; i++) {
- rotate(pins, CLOCKWISE, delay);
- }
- delay--;
- if (delay < 4) {
- delay = 25;
- stop(pins);
- delayMS(500);
- }
- }
-
- return 0;
- }
-
- /* Suspend execution for x milliseconds intervals.
- * @param ms Milliseconds to sleep.
- */
- void delayMS(int x) {
- usleep(x * 1000);
- }
-
- /* Rotate the motor.
- * @param pins A pointer which points to the pins number array.
- * @param direction CLOCKWISE for clockwise rotation, COUNTER_CLOCKWISE for counter clockwise rotation.
- * @param delay The time intervals(in ms) to delay, and if the value is smaller, the motor rotates faster.
- */
- void rotate(int* pins, int direction, int delay) {
- for (int i = 0; i < 4; i++) {
- if (CLOCKWISE == direction) {
- for (int j = 0; j < 4; j++) {
- if (j == i) {
- digitalWrite(pins[3 - j], 1); // output a high level
- } else {
- digitalWrite(pins[3 - j], 0); // output a low level
- }
- }
- } else if (COUNTER_CLOCKWISE == direction) {
- for (int j = 0; j < 4; j++) {
- if (j == i) {
- digitalWrite(pins[j], 1); // output a high level
- } else {
- digitalWrite(pins[j], 0); // output a low level
- }
- }
- }
- delayMS(delay);
- }
- }
-
- /* Stop the motor.
- * @param pins A pointer which points to the pins number array.
- */
- void stop(int* pins) {
- for (int i = 0; i < 4; i++) {
- digitalWrite(pins[i], 0); // output a low level
- }
- }
复制代码
|