基于步进电机在LINUX平台下的调试(之二)

    之前在用户空间用操作gpio实现完整的步进电机的控制功能.下面将其挪到底层去,顺便总结下驱动所用到的知识点.

驱动源码:

/*
 *  Copyright (c) 2014-5-15 SE7EN
 *
 *  Four phases step motor support
 */

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/delay.h>

#define DRIVER_VERSION "2014.05.15"
#define DRIVER_AUTHOR "SE7EN"
#define DRIVER_DESC "Four Phases StepMotor Driver"
#define DRIVER_LICENSE "GPL"

#define	DEVICE_NAME	"motor"
#define	MOTOR_MAJOR	173

#define	MOTOR_IOC_MAGIC	'm'
#define	MOTOR_IOCGETDATA	_IOR(MOTOR_IOC_MAGIC,1,int)
#define	MOTOR_IOCSETDATA	_IOW(MOTOR_IOC_MAGIC,2,int)
#define	MOTOR_IOCSETLEFT	_IOW(MOTOR_IOC_MAGIC,3,int)
#define	MOTOR_IOCSETRIGHT	_IOW(MOTOR_IOC_MAGIC,4,int)
#define	MOTOR_IOC_MAXNR		4

#define	DEF_PULSE	2500
#define	PHASE_A		GPIO_PE(20)
#define	PHASE_B		GPIO_PE(21)
#define	PHASE_C		GPIO_PE(22)
#define	PHASE_D		GPIO_PE(29)
#define DIRECTION_L    	GPIO_PB(2)
#define DIRECTION_R	GPIO_PA(29)

#define	THOUSAND_US	1000000

/* debug macros */
#define DEBUG	1

#ifdef DEBUG
#define DPRINTK(fmt,args...) printk(fmt,##args)
#else
#define DPRINTK(fmt,args...)
#endif


typedef struct _stepMotorInfo
{
	uint openCnt;
	uint pulse;
	uint phase_a;
	uint phase_b;
	uint phase_c;
	uint phase_d;
	uint direction_left;
	uint direction_right;
	uint irqCntLeft;
	uint irqCntRight;
	spinlock_t lock;
	struct semaphore sem;
	struct tasklet_struct tasklet_left;
	struct tasklet_struct tasklet_right;
	struct class *class;
	struct cdev cdev;
}stepMotorInfo,*pstepMotorInfo;

stepMotorInfo motor;

static int motor_major = MOTOR_MAJOR;

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
MODULE_VERSION(DRIVER_VERSION);

static irqreturn_t motorTurnLeftHandle(int irq,void *dev_id)
{

	pstepMotorInfo pMotor = (pstepMotorInfo)dev_id;

	tasklet_schedule(&pMotor->tasklet_left);

	return IRQ_HANDLED;
}

static irqreturn_t motorTurnRightHandle(int irq,void *dev_id)
{

	pstepMotorInfo pMotor = (pstepMotorInfo)dev_id;	

	tasklet_schedule(&pMotor->tasklet_right);

	return IRQ_HANDLED;
}



//A->AB->B->BC->C->CD->D->DA->A
static void stepMotorTurnLeft(pstepMotorInfo motor)
{
	//->A
	gpio_direction_output(motor->phase_b,0);
	gpio_direction_output(motor->phase_c,0);
	gpio_direction_output(motor->phase_d,0);
	gpio_direction_output(motor->phase_a,1);
	udelay(motor->pulse);
	//->AB
	gpio_direction_output(motor->phase_b,1);
	udelay(motor->pulse);
	//->B
	gpio_direction_output(motor->phase_a,0);
	udelay(motor->pulse);
	//->BC
	gpio_direction_output(motor->phase_c,1);
	udelay(motor->pulse);
	//->C
	gpio_direction_output(motor->phase_b,0);
	udelay(motor->pulse);
	//->CD
	gpio_direction_output(motor->phase_d,1);
	udelay(motor->pulse);
	//->D
	gpio_direction_output(motor->phase_c,0);
	udelay(motor->pulse);
	//->DA
	gpio_direction_output(motor->phase_a,1);
	udelay(motor->pulse);
}

//A->AD->D->DC->C->CB->B->BA->A
static void stepMotorTurnRight(pstepMotorInfo motor)
{
	//->A
	gpio_direction_output(motor->phase_d,0);
	gpio_direction_output(motor->phase_b,0);
	gpio_direction_output(motor->phase_c,0);
	gpio_direction_output(motor->phase_a,1);
	udelay(motor->pulse);
	//->AD
	gpio_direction_output(motor->phase_d,1);
	udelay(motor->pulse);
	//->D
	gpio_direction_output(motor->phase_a,0);
	udelay(motor->pulse);
	//->DC
	gpio_direction_output(motor->phase_c,1);
	udelay(motor->pulse);
	//->C
	gpio_direction_output(motor->phase_d,0);
	udelay(motor->pulse);
	//->CB
	gpio_direction_output(motor->phase_b,1);
	udelay(motor->pulse);
	//->B
	gpio_direction_output(motor->phase_c,0);
	udelay(motor->pulse);
	//->BA
	gpio_direction_output(motor->phase_a,1);
	udelay(motor->pulse);
}

static int motorSpinLeft(pstepMotorInfo motor)
{
	int ret = -1;

	ret = down_trylock(&motor->sem);
	if(ret)
		return ret;

	stepMotorTurnLeft(motor);

	up(&motor->sem);	

	return ret;
}

static int motorSpinRight(pstepMotorInfo motor)
{
	int ret = -1;

	ret = down_trylock(&motor->sem);
	if(ret)
		return ret;

	stepMotorTurnRight(motor);

	up(&motor->sem);	

	return ret;
}

static void motor_do_tasklet_left(unsigned long arg)
{

	pstepMotorInfo pMotor = (pstepMotorInfo)arg;

	motorSpinLeft(pMotor);
}

static void motor_do_tasklet_right(unsigned long arg)
{

	pstepMotorInfo pMotor = (pstepMotorInfo)arg;

	motorSpinRight(pMotor);
}

static int motor_open(struct inode *inode, struct file *filp)
{
	int ret = -1;

	spin_lock(&motor.lock);

	if(motor.openCnt)
	{
		spin_unlock(&motor.lock);
		return -EBUSY;
	}	

	filp->private_data = &motor;

	motor.openCnt++;

	motor.phase_a = PHASE_A;
	motor.phase_b = PHASE_B;
	motor.phase_c = PHASE_C;
	motor.phase_d = PHASE_D;
	motor.direction_left = DIRECTION_L; 
	motor.direction_right = DIRECTION_R;
	motor.pulse = DEF_PULSE;
	motor.irqCntLeft = 0;
	motor.irqCntRight = 0;

	ret = gpio_request(motor.phase_a,"PHASE_A");
	if(ret < 0)
		goto err_request_phase_a;
	else
		gpio_direction_output(motor.phase_a,0);

	ret = gpio_request(motor.phase_b,"PHASE_B");
	if(ret < 0)
		goto err_request_phase_b;
	else
		gpio_direction_output(motor.phase_b,0);

	ret = gpio_request(motor.phase_c,"PHASE_C");
	if(ret < 0)
		goto err_request_phase_c;
	else
		gpio_direction_output(motor.phase_c,0);

	ret = gpio_request(motor.phase_d,"PHASE_D");
	if(ret < 0)
		goto err_request_phase_d;
	else
		gpio_direction_output(motor.phase_d,0);

	tasklet_init(&motor.tasklet_left,motor_do_tasklet_left,(unsigned long)&motor);
	tasklet_init(&motor.tasklet_right,motor_do_tasklet_right,(unsigned long)&motor);

	ret = gpio_is_valid(motor.direction_left);
	if(ret) 
	{
		ret = gpio_request(motor.direction_left, "MOTOR_DIRECTION_LEFT");
		if (ret < 0) 
		{
			printk(KERN_EMERG "request motor_direction_left gpio failed.\n");
			goto err_motor_direction_left;
		}
	}        
	else 
	{
		printk("ERROR: motor_direction_left gpio is invalid.\n");
		goto err_motor_direction_left;
	}

	ret = gpio_is_valid(motor.direction_right);
	if(ret) 
	{
		ret = gpio_request(motor.direction_right, "MOTOR_DIRECTION_RIGHT");
		if (ret < 0) 
		{
			printk(KERN_EMERG "request motor_direction_right gpio failed.\n");
			goto err_motor_direction_right;
		}
	}        
	else 
	{
		printk("ERROR: motor_direction_right gpio is invalid.\n");
		goto err_motor_direction_right;
	}


	ret = request_irq(gpio_to_irq(motor.direction_left),
			motorTurnLeftHandle,
			IRQF_DISABLED |
			IRQF_TRIGGER_FALLING,
			"motor_l",&motor);
	if (ret < 0) {
		printk(KERN_EMERG "request the pin of motor's direction_left as irq failed.\n");
		goto err_motor_left_irq;
	}

	ret = request_irq(gpio_to_irq(motor.direction_right),
			motorTurnRightHandle,
			IRQF_DISABLED |
			IRQF_TRIGGER_FALLING,
			"motor_r",&motor);
	if (ret < 0) {
		printk(KERN_EMERG "request the pin of motor's direction_right as irq failed.\n");
		goto err_motor_right_irq;
	}

	spin_unlock(&motor.lock);	

	return ret;

err_motor_right_irq:
	free_irq(gpio_to_irq(motor.direction_left),&motor);

err_motor_left_irq:
	gpio_free(motor.direction_right);

err_motor_direction_right:
	gpio_free(motor.direction_left);

err_motor_direction_left:
	gpio_free(motor.phase_d);

err_request_phase_d:
	gpio_free(motor.phase_c);

err_request_phase_c:
	gpio_free(motor.phase_b);

err_request_phase_b:
	gpio_free(motor.phase_a);

err_request_phase_a:
	spin_unlock(&motor.lock);

	return ret;
}

static int motor_release(struct inode *inode, struct file *filp)
{
	pstepMotorInfo dev = filp->private_data;

	spin_lock(&dev->lock);
	free_irq(gpio_to_irq(dev->direction_left),dev);
	free_irq(gpio_to_irq(dev->direction_right),dev);
	gpio_free(dev->phase_a);
	gpio_free(dev->phase_b);
	gpio_free(dev->phase_c);
	gpio_free(dev->phase_d);
	gpio_free(dev->direction_left);
	gpio_free(dev->direction_right);
	dev->openCnt--;
	spin_unlock(&dev->lock);

	return 0;
}

static long motor_ioctl(struct file *filp,unsigned int cmd, unsigned long arg)
{
	int ret = -1;

	uint freq = 0;

	pstepMotorInfo dev = filp->private_data;

	if (_IOC_TYPE(cmd) != MOTOR_IOC_MAGIC)
		return -EINVAL;
	if (_IOC_NR(cmd) > MOTOR_IOC_MAXNR)
		return -EINVAL;

	if (_IOC_DIR(cmd) & _IOC_READ)
		ret = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
	else if (_IOC_DIR(cmd) & _IOC_WRITE)
		ret = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
	if (ret)
		return -EFAULT;

	switch(cmd)
	{
		case MOTOR_IOCGETDATA:
			freq = THOUSAND_US/dev->pulse;
			ret = __put_user(freq,(uint *)arg);
			break;

		case MOTOR_IOCSETDATA:
			ret = __get_user(freq,(uint *)arg);
			if(0 == ret)
				dev->pulse = THOUSAND_US/freq;
			break;

		case MOTOR_IOCSETLEFT:
			motorSpinLeft(dev);		
			break;

		case MOTOR_IOCSETRIGHT:
			motorSpinRight(dev);		
			break;

		default:
			return -EINVAL;
	}

	return ret;
}

static const struct file_operations motor_fops = {
	.owner  	=       THIS_MODULE,
	.open		=	motor_open,
	.release	=	motor_release,	
	.unlocked_ioctl =       motor_ioctl
};

static int stepMotor_init(void)
{

	int ret = -1;

	dev_t devno = MKDEV(motor_major,0);

	if(motor_major)
		ret = register_chrdev_region(devno,1,"motordev");
	else
	{
		ret = alloc_chrdev_region(&devno,0,1,"motordev");
		motor_major = MAJOR(devno);
	}		

	if(ret < 0)
	{
		printk(KERN_EMERG "\tget dev_number fail!\n");
		return ret;
	}
	if(0 == motor_major)
		motor_major = ret;

	cdev_init(&motor.cdev,&motor_fops);
	motor.cdev.owner = THIS_MODULE;
	motor.cdev.ops = &motor_fops;

	ret = cdev_add(&motor.cdev,MKDEV(motor_major,0),1); 
	if(ret)
		printk(KERN_NOTICE "Error %d adding motor.\n",ret);		

	motor.class = class_create(THIS_MODULE,DEVICE_NAME);
	if(IS_ERR(motor.class))
	{
		cdev_del(&motor.cdev);
		unregister_chrdev_region(MKDEV(motor_major,0),1);
		return PTR_ERR(motor.class);
	}

	device_create(motor.class,NULL,MKDEV(motor_major,0),NULL,DEVICE_NAME);

	motor.openCnt = 0;
	spin_lock_init(&motor.lock);

	sema_init(&motor.sem,1);	

	printk(KERN_INFO DEVICE_NAME"\tregister ok!\n");

	return ret;
}

static void stepMotor_exit(void)
{
	cdev_del(&motor.cdev);
	device_destroy(motor.class,MKDEV(motor_major,0));
	class_destroy(motor.class);
	unregister_chrdev_region(MKDEV(motor_major,0),1);
	printk(KERN_INFO DEVICE_NAME"\tunregister!\n");
}

module_init(stepMotor_init);
module_exit(stepMotor_exit);

用户空间的测试代码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/ioctl.h>

#define MOTOR_IOC_MAGIC 'm'
#define MOTOR_IOCGETDATA        _IOR(MOTOR_IOC_MAGIC,1,int)
#define MOTOR_IOCSETDATA        _IOW(MOTOR_IOC_MAGIC,2,int)
#define MOTOR_IOCSETLEFT        _IOW(MOTOR_IOC_MAGIC,3,int)
#define MOTOR_IOCSETRIGHT       _IOW(MOTOR_IOC_MAGIC,4,int)
#define MOTOR_IOC_MAXNR         4

#define	THOUSAND_US	1000000

#define	MOTOR_DEV	"/dev/motor"

/*
Input:The frequency of the stepMotor,the unit is HZ.
Output:NULL
Return:The width of the pulse,the unit is u-second.
*/

static unsigned int frequencyToPulse(const unsigned int freq)
{
	return THOUSAND_US/freq;
}

int main(int argc,char **argv)
{
	int ret = -1,motorFd = -1,cmd = 0;
	unsigned int count = 0;
	unsigned int arg = 400;

	const unsigned char *pLeft = "left";
        const unsigned char *pRight = "right";
        const unsigned char *pAuto = "auto";
        const unsigned char *pNormal = "normal";

	if(argc != 4)
        {
                printf("Pls input the direction of motor:'left' or 'right' the speed of motor:NHZ the mode of the motor:'auto' or 'normal' \n ");
                printf("For Example:./xxx left/right 400 auto/normal\n");
                exit(-1);
        }



	motorFd = open(MOTOR_DEV,O_RDWR);
	if(motorFd < 0)
	{
		perror("Open /dev/motor Fail");
		return motorFd;
	}

	cmd = MOTOR_IOCGETDATA;
	ret = ioctl(motorFd,cmd,&arg);
	if(ret < 0)
	{
		perror("Get the frequency of motor fail");
		return ret;
	}
	else
	{
		printf("Get the frequency of motor is %dHZ\n",arg);
	}

	arg = atoi(argv[2]);

	cmd = MOTOR_IOCSETDATA;
	ret = ioctl(motorFd,cmd,&arg);
	if(ret < 0)
	{
		perror("Set the frequency of motor fail");
		return ret;
	}
	else
	{

		printf("Set the frequency of motor is %dHZ\n",arg);
	}

	cmd = MOTOR_IOCGETDATA;
	ret = ioctl(motorFd,cmd,&arg);
	if(ret < 0)
	{
		perror("Get the frequency of motor fail");
		return ret;
	}
	else
	{
		printf("Get the frequency of motor is %dHZ\n",arg);
	}

	if(0 == (strcmp(pLeft,argv[1])))
		cmd = MOTOR_IOCSETLEFT;	

	if(0 == (strcmp(pRight,argv[1])))
		cmd = MOTOR_IOCSETRIGHT;	

	if(0 == (strcmp(pNormal,argv[3])))
		while(1);

	while(1)
	{
		ret = ioctl(motorFd,cmd,&arg);

		if(ret < 0)
			continue;
	}

	close(motorFd);

	return 0;
}

所用到的知识点:

1.主次设备号的动态申请;

2.字符设备的注册;

3.设备节点的自动生成和自动销毁;

4.自旋锁的使用;

5.通过文件私有数据结构实现相关资源文件结构的共享;

6.内核gpio子系统;

7.内核中断;

8.中断下半部tasklet的使用;

9.ioctl幻数命令的使用;

10.对用户空间下传指针的合法性检验;

11.对于char short int类型用户空间和内核空间的快速交互API;

12.驱动版本的标准控制.

13.通过信号量实现底层中断和上层对共同代码访问的同步保护.


你可能感兴趣的:(基于步进电机在LINUX平台下的调试(之二))