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

引言

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

前期准备

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

电机启动停止指令

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

开发过程

主要参照了rtthread官方的pin设备文档,但是发现根据PIN设备文档来配置仍存在一些问题,这个后面会提到
官方文档链接:
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/pin/pin
根据官方文档,PIN设备的接口如下
基于RT-Thread的CAN电机驱动板设计 (四)使用PIN设备配置按键中断实现电机启停
通过PIN接口为按键绑定中断函数,官方文档中给出的示例代码如下:

#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发现下面这个例子。
基于RT-Thread的CAN电机驱动板设计 (四)使用PIN设备配置按键中断实现电机启停
基于RT-Thread的CAN电机驱动板设计 (四)使用PIN设备配置按键中断实现电机启停
基于RT-Thread的CAN电机驱动板设计 (四)使用PIN设备配置按键中断实现电机启停

/*
 * 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

//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协议的电机,值得大家借鉴。

上一篇:Motor Vocabuary


下一篇:嵌入式课程---嵌入式Linux的直流电机驱动开发