之前在用户空间用操作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.通过信号量实现底层中断和上层对共同代码访问的同步保护.