实现步进电机的驱动
一套驱动支持多个设备
软件环境:Linux-3.5内核
硬件环境:FriendlyArm Tiny4412开发板
/* bj_drv.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/gpio.h>
#include <linux/moduleparam.h>
#include "bjioctl.h"
#define STEP ARRAY_SIZE(beat_code)
#define DEVCNT 2
#define BJBASE 0x11400000 // GPJ0base
#define BJCON 0x0240 // GPJ0con
#define BJDAT 0x0244 // GPJ0dat
enum { OFF, ON };
static unsigned char beat_code[] = { // 步进电机节拍对应的 IO 控制代码
0x09,0x03,0x06,0x0C
};
/*
static unsigned char beat_code[] = { // 步进电机节拍对应的 IO 控制代码
0x09,0x01,0x03,0x02,0x06,0x04,0x0C,0x08
};
*/
static int msec = 5;
static int enperied = 1;
static int major = 50;
struct motor {
u32 start;
u32 end;
int minor;
char dir_motor;
long beats;
struct cdev cdev;
struct class *class;
struct timer_list timer;
};
struct motor motor[DEVCNT];
/*
static u32 start = 0;
static u32 end = 0;
static int devnum = 0;
static struct cdev cdev;
static struct class *class = NULL;
static struct timer_list timer;
*/
static void set_mytimer(int minor, u32 sec);
static void service_timer(unsigned long data);
static void motor_start(int minor, signed long angle);
static void motor_stop(int minor);
static int motor_turn(int minor);
static void service_timer(unsigned long data) {
int minor = (int)data;
if (enperied)
mod_timer(&motor[minor].timer, jiffies + msec*HZ/1000);
if (motor_turn(minor))
enperied = 0;
}
static void set_mytimer(int minor, u32 sec) {
setup_timer(&motor[minor].timer, service_timer, (unsigned long)minor);
motor[minor].timer.expires = jiffies + sec*HZ/1000;
add_timer(&motor[minor].timer);
}
static long
demo_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int minor = ((struct motor *)filp->private_data)->minor;
if (arg < 0)
return -EINVAL;
if (_IOC_TYPE(cmd) == BJMAC) {
switch (_IOC_NR(cmd)) {
default:
return -EINVAL;
case 0:
motor_start(minor, arg);
break;
case 1:
motor_start(minor, -1 * arg);
break;
case 2:
motor_stop(minor);
break;
}
}
return 0;
}
static int demo_open (struct inode *inodp, struct file *filp) {
filp->private_data = container_of(inodp->i_cdev, struct motor, cdev);
return 0;
}
static int demo_release (struct inode *inodp, struct file *filp) {
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = demo_open,
.unlocked_ioctl = demo_unlocked_ioctl,
.release = demo_release,
};
static int bj_probe(struct platform_device *pdev)
{
int ret;
int i = 0;
int devnum = 0;
struct resource *pres;
struct device *device = NULL;
struct class *class = NULL;
motor[pdev->id].minor = pdev->id;
printk("--- driver %d device ---\n", pdev->id);
pres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
motor[pdev->id].start = pres->start;
motor[pdev->id].end = pres->end;
class = class_create(THIS_MODULE, pdev->name);
if (IS_ERR(class))
return PTR_ERR(class);
printk("--- class create %s ---\n", pdev->name);
for (i = pres->start; i <= pres->end; i++) {
if ((ret = gpio_request(i, "motor")) < 0)
goto error0;
gpio_direction_output(i, OFF);
}
printk("--- %d gpio request ---\n", pres->start);
devnum = MKDEV(major, pdev->id);
printk("--- devnum is %d ---\n", devnum);
ret = register_chrdev_region(devnum, 1, pdev->name);
if (ret < 0) {
ret = -EINVAL;
goto error0;
}
printk("--- register %d ---\n", devnum);
cdev_init(&motor[pdev->id].cdev, &fops);
if ((ret = cdev_add(&motor[pdev->id].cdev, devnum, 1)) < 0)
goto error1;
printk("--- cdev add %d ---\n", devnum);
device = device_create(class, NULL, devnum, NULL, pdev->name);
if (IS_ERR(device)) {
ret = PTR_ERR(device);
goto error2;
}
motor[pdev->id].class = class;
printk("--- device create %s ok! ---\n", pdev->name);
return 0;
error2:
cdev_del(&motor[pdev->id].cdev);
error1:
unregister_chrdev_region(devnum, 1);
error0:
class_destroy(class);
for ( ; i >= pres->start; i--)
gpio_free(i);
return ret;
}
static int bj_remove(struct platform_device *pdev) {
int i = 0;
int devnum = 0;
struct resource *pres;
struct class *class = motor[pdev->id].class;
pres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
devnum = MKDEV(major, pdev->id);
device_destroy(class, devnum);
cdev_del(&motor[pdev->id].cdev);
for (i = pres->start; i <= pres->end; i++)
gpio_free(i);
cdev_del(&motor[pdev->id].cdev);
unregister_chrdev_region(devnum, 1);
class_destroy(class);
printk("--- cdev %s die! ---\n", pdev->name);
return 0;
}
static struct platform_device_id millet_tables[] = {
{ "door_motor", },
{ "window_motor", },
};
static struct platform_driver bj_drv = {
.id_table = millet_tables,
.driver= {
.name = "bj_motor",
},
.probe = bj_probe,
.remove = bj_remove,
};
module_platform_driver(bj_drv);
static void motor_start(int minor, signed long angle) {
motor[minor].beats = (angle * 4076 / 2) / 360; // 实测为 4076 拍转动一圈
set_mytimer(minor, msec);
enperied = 1;
}
static void motor_stop(int minor) {
motor[minor].beats = 0;
del_timer_sync(&motor[minor].timer);
enperied = 1;
}
static int motor_turn(int minor)
{
int i = 0, j = 0;
int ret = 0;
static unsigned char index[DEVCNT] = {0};
long beats[DEVCNT] = {0};
beats[minor] = motor[minor].beats;
if (beats[minor] != 0) {
if (beats[minor] > 0) {
index[minor]++;
index[minor] = index[minor] & (STEP - 1); // (STEP == 4) ? 0x3 : 0x7
motor[minor].beats--;
} else {
index[minor]--;
index[minor] = index[minor] & (STEP - 1);
motor[minor].beats++;
}
for (i = motor[minor].start, j = 0; i <= motor[minor].end; i++, j++) {
if (beat_code[index[minor]] & (0x1 << j))
gpio_set_value(i, ON);
else
gpio_set_value(i, OFF);
}
ret = 0;
} else
ret = 1;
return ret;
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wanghaomaker");
MODULE_VERSION("wanghao plus 3");
MODULE_DESCRIPTION("It is a simplest demo for motor driver module");
===================================================