17、28BYJ-48 电机驱动
什么,学完了pinctrl子系统和GPIO子系统还只会点灯?
今天就来个高级点的点灯
这个电机驱动程序说白了就是 点灯
1、28BYJ-48 电机是四相八拍电机,所以需要4个GPIO来控制
简介:
28:步进电机的有效最大外径是28毫米
B:表示是步进电机
Y:表示是永磁式
J:表示是减速型(减速比1:64)
48:表示四相八拍
内部结构示意图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qYxCbNM4-1619338578619)(linux%E9%A9%B1%E5%8A%A8.assets/image-20210331103907659.png)]
1、相数:是指产生不同对极N、S磁场的激磁线圈对数。常用m表示 例如上图 有四对线圈 A、B、C、D
2、拍数:完成一个磁场周期性变化所需脉冲数,以四相电机为例,
有单相四拍运行方式即A-B-C-D,
有双相四拍运行方式即AB-BC-CD-DA
有四相八拍运行方式即A-AB-B-BC-C-CD-D-DA
3、步进角:
步进电机的定子绕组每改变一次通电状态,转子转过的角度称步进角。
转子磁极数越多,步进角 越小
定子相数越多,步进角越小
通电方式节拍越多,步进角越小
例如 八拍模式下 360/(4 * 8 * 2) = 5.625
关于启动频率,也就是pps 这里间隔时间最好是大于1200us
与开发板的管脚连接
第一步 在pinctrl子系统中,对这几个管脚进行配置
exynos4412-pinctrl.dtsi 文件中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5tk1Be60-1619338578629)(linux%E9%A9%B1%E5%8A%A8.assets/image-20210420154940506.png)]
mymoter: mymoter {
samsung,pins = "gpj0-3", "gpj0-4", "gpj0-5", "gpj0-6";
samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>; //设置为输出模式
samsung,pin-val = <0x0>; //默认输出低电平
// samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
};
2、第二步 在根节点下添加MOTOR节点
exynos4412-itop-elite.dts 文件中 在根节点中添加节点
/*motor*/
my_motor:my_motor{
compatible = "motor"; //描述
pinctrl-names = "default";
pinctrl-0 = <&mymotor>;
status = "okay"
/*GPIO管脚 "gpj0-3", "gpj0-4", "gpj0-5", "gpj0-6";*/
motorA-gpios = <&gpj0 3 GPIO_ACTIVE_HIGH>; //GPJ0_3 GPIO_ACTIVE_HIGH 表示高电平有效
motorB-gpios = <&gpj0 4 GPIO_ACTIVE_HIGH>;
motorC-gpios = <&gpj0 5 GPIO_ACTIVE_HIGH>;
motorD-gpios = <&gpj0 6 GPIO_ACTIVE_HIGH>;
}
3、 编写driver程序
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/pinctrl/consumer.h>
#include <dt-bindings/pinctrl/samsung.h>
#include <linux/of_gpio.h>
/* 字符设备框架
1、注册 platform driver
2、构建file_operations
3、获取硬件资源
4、注册字符设备驱动
4、生成字符设备节点
*/
/*
定义各个管脚的命令
*/
#define MOTOR_CMD_A _IOW('M',0,long)
#define MOTOR_CMD_B _IOW('M',1,long)
#define MOTOR_CMD_C _IOW('M',2,long)
#define MOTOR_CMD_D _IOW('M',3,long)
struct device *device_cdev;
struct class *class_cdev;
int GPIO_ID_A;
int GPIO_ID_B;
int GPIO_ID_C;
int GPIO_ID_D;
int motor_open(struct inode *inode, struct file *file)
{
printk("open is success!\n");
return 0;
}
int motor_release (struct inode *inode, struct file *file)
{
printk("release is success!\n");
return 0;
}
long motor_ioctrl(struct file *file, unsigned int cmd, unsigned long value)
{
switch(cmd)
{
case MOTOR_CMD_A:
gpio_set_value(GPIO_ID_A, value);
break;
case MOTOR_CMD_B:
gpio_set_value(GPIO_ID_B, value);
break;
case MOTOR_CMD_C:
gpio_set_value(GPIO_ID_C, value);
break;
case MOTOR_CMD_D:
gpio_set_value(GPIO_ID_D, value);
break;
}
}
/*2、获取硬件资源 */
int motor_probe(struct platform_device *pdev)
{
int ret;
/*1、获取GPIO号*/
GPIO_ID_A = of_get_named_gpio(pdev->dev.of_node, "motorA-gpios", 0);//index=0 ,因为在设备树中只引用了一个
if (GPIO_ID_A < 0)
{
printk("of get named gpio is error!\n");
return -1;
}
GPIO_ID_B = of_get_named_gpio(pdev->dev.of_node, "motorB-gpios", 0);//index=0 ,因为在设备树中只引用了一个
if (GPIO_ID_B < 0)
{
printk("of get named gpio is error!\n");
return -1;
}
GPIO_ID_C = of_get_named_gpio(pdev->dev.of_node, "motorC-gpios", 0);//index=0 ,因为在设备树中只引用了一个
if (GPIO_ID_C< 0)
{
printk("of get named gpio is error!\n");
return -1;
}
GPIO_ID_D = of_get_named_gpio(pdev->dev.of_node, "motorD-gpios", 0);//index=0 ,因为在设备树中只引用了一个
if (GPIO_ID_D< 0)
{
printk("of get named gpio is error!\n");
return -1;
}
/*2、申请一个GPIO管脚*/
ret = gpio_request(GPIO_ID_A, "motor_A_GPIO");
if (ret != 0)
{
printk("gpio request is error!\n");
return -1;
}
ret = gpio_request(GPIO_ID_B, "motor_B_GPIO");
if (ret != 0)
{
printk("gpio request is error!\n");
return -1;
}
ret = gpio_request(GPIO_ID_C, "motor_C_GPIO");
if (ret != 0)
{
printk("gpio request is error!\n");
return -1;
}
ret = gpio_request(GPIO_ID_D, "motor_D_GPIO");
if (ret != 0)
{
printk("gpio request is error!\n");
return -1;
}
/*3、 将管脚设置为输出*/
/* 这里先不设置,因为在pinctrl复用中已经将管脚设置为了OUTPUT*/
return 0;
}
int motor_remove(struct platform_device *pdev)
{
return 0;
}
struct of_device_id of_match_table = { // 与设备树节点进行匹配
.compatible = "motor"
};
/*1、初始化platform driver*/
struct platform_driver pdev = {
.probe = motor_probe, // 与 of_match_table 匹配成功后进入probe函数获取硬件资源
.remove = motor_remove,
.driver = {
.name = "motor", //无设备树时 使用.name 与device进行匹配
.owner = THIS_MODULE,
.of_match_table = &of_match_table,
}
};
//3、注册字符设备驱动
/*3.1 分配设备号*/
dev_t dev_number;
/*3.2 定义cdev*/
struct cdev cdev_;
/*3.3 构建file_operation结构体*/
struct file_operations fop = {
.owner = THIS_MODULE,
.open = motor_open,
.release = motor_release,
.unlocked_ioctl = motor_ioctrl
};
static int char_driver_init(void)
{
/*1、注册platform driver*/
int ret = platform_driver_register(&pdev);
if (0 != ret)
{
printk("platform driver register is error!\n");
return -1;
}
/*3.1 分配设备号(动态分配设备号)*/
ret = alloc_chrdev_region(&dev_number, 0, 1, "my_motor");
if (0 != ret)
{
printk("alloc chrdev region is error!\n");
return ret;
}
/*3.4 初始化cdev*/
cdev_.owner = THIS_MODULE;
cdev_init(&cdev_, &fop);
/*3.5 注册字符设备到内核*/
ret = cdev_add(&cdev_, dev_number, 1);
if (0 != ret)
{
printk("cdev add is error!\n");
return -1;
}
/*4、生成设备节点*/
/*4.1 创建字符设备类*/
class_cdev = class_create(THIS_MODULE, "motor");
if (NULL == class_cdev)
{
printk("class create is error!\n");
return -1;
}
/*生成设备节点*/
device_cdev = device_create (class_cdev, NULL, dev_number, NULL, "my_motor");
if (NULL == device_cdev)
{
printk("device create is error!\n");
}
return 0;
};
static void char_driver_exit(void)
{
gpio_free(GPIO_ID_A);
gpio_free(GPIO_ID_B);
gpio_free(GPIO_ID_C);
gpio_free(GPIO_ID_D); //释放GPIO
device_destroy(class_cdev, dev_number); // 卸载设备节点
class_destroy(class_cdev); //卸载设备类
cdev_del(&cdev_); //卸载cdev
// iounmap(led_con); // 取消地址映射
// iounmap(led_dat);
unregister_chrdev_region(dev_number, 1);// 注销设备号
platform_driver_unregister(&pdev); // 注销platform driver
}
module_init(char_driver_init);
module_exit(char_driver_exit);
MODULE_LICENSE("GPL");
4、应用程序
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#define MOTOR_DEV "/dev/my_motor"
/* 一个节拍 转动 5.625*/
#define MOTOR_CMD_A _IOW('M',0,long)
#define MOTOR_CMD_B _IOW('M',1,long)
#define MOTOR_CMD_C _IOW('M',2,long)
#define MOTOR_CMD_D _IOW('M',3,long)
#define HIGH 1
#define LOW 0
#define NUMBER 10000
#define SPEED 1500
//SPEED 越小转速越快
int fd;
int motor_off(void)
{
ioctl(fd, MOTOR_CMD_A, LOW);
ioctl(fd, MOTOR_CMD_B, LOW);
ioctl(fd, MOTOR_CMD_C, LOW);
ioctl(fd, MOTOR_CMD_D, LOW);
}
static int meter_turn(int n)
{
switch(n)
{
case 0:
ioctl(fd, MOTOR_CMD_A, LOW);
ioctl(fd, MOTOR_CMD_B, HIGH);
ioctl(fd, MOTOR_CMD_C, HIGH);
ioctl(fd, MOTOR_CMD_D, HIGH);
break;
case 1:
ioctl(fd, MOTOR_CMD_A, LOW);
ioctl(fd, MOTOR_CMD_B, LOW);
ioctl(fd, MOTOR_CMD_C, HIGH);
ioctl(fd, MOTOR_CMD_D, HIGH);
break;
case 2:
ioctl(fd, MOTOR_CMD_A, HIGH);
ioctl(fd, MOTOR_CMD_B, LOW);
ioctl(fd, MOTOR_CMD_C, HIGH);
ioctl(fd, MOTOR_CMD_D, HIGH);
break;
case 3:
ioctl(fd, MOTOR_CMD_A, HIGH);
ioctl(fd, MOTOR_CMD_B, LOW);
ioctl(fd, MOTOR_CMD_C, LOW);
ioctl(fd, MOTOR_CMD_D, HIGH);
break;
case 4:
ioctl(fd, MOTOR_CMD_A, HIGH);
ioctl(fd, MOTOR_CMD_B, HIGH);
ioctl(fd, MOTOR_CMD_C, LOW);
ioctl(fd, MOTOR_CMD_D, HIGH);
break;
case 5:
ioctl(fd, MOTOR_CMD_A, HIGH);
ioctl(fd, MOTOR_CMD_B, HIGH);
ioctl(fd, MOTOR_CMD_C, LOW);
ioctl(fd, MOTOR_CMD_D, LOW);
break;
case 6:
ioctl(fd, MOTOR_CMD_A, HIGH);
ioctl(fd, MOTOR_CMD_B, HIGH);
ioctl(fd, MOTOR_CMD_C, HIGH);
ioctl(fd, MOTOR_CMD_D, LOW);
break;
case 7:
ioctl(fd, MOTOR_CMD_A, LOW);
ioctl(fd, MOTOR_CMD_B, HIGH);
ioctl(fd, MOTOR_CMD_C, HIGH);
ioctl(fd, MOTOR_CMD_D, LOW);
break;
}
return 0;
}
void motor_direction(const char direction, int number)
{
int i ;
int step ;
if (direction == 'R')
step = -1;
else
step = 8;
for (i = 0; i < number; i++)
{
if (direction == 'R')
{
step++;
if (step > 7)
step = 0;
}
else
{
if (step == 0)
step = 8;
step--;
}
meter_turn(step);
usleep(SPEED);
}
}
int main(int argc, char * argv[])
{
fd = open(MOTOR_DEV, O_RDWR);
if (-1 == fd)
{
perror("open");
return -1;
}
printf("open success!\n");
motor_direction('L', NUMBER);
motor_off();
return 0;
}