0
点赞
收藏
分享

微信扫一扫

设计模式之创建型模式---建造者模式

小北的爹 04-10 15:00 阅读 2

目录

一、电机驱动模块介绍

1.1 特性

1.2 应用范围

1.3 概述

1.4 电气属性

1.5 经典应用线路

二、电机介绍(本节内容来自韦东山老师课程资料)

三、 代码编写思路

四、 驱动程序

五、应用程序


一、电机驱动模块介绍

1.1 特性

 低待机电流 (小于 0.1uA)
 低导通内阻 MOSFET 功率开关管
— 采用 MOS 工艺设计功率管
— 1 通道 1500 毫安功率管内阻 0.36 欧姆
— 2 通道 800 毫安功率管内阻 0.47 欧姆
 内部集成续流二极管
— 无需外接续流二极管
 较小的输入电流
— 集成约 15K 对地下拉电阻
— 3V 驱动信号平均 200uA 输入电流
 内置带迟滞效应的过热保护电路 (TSD)
 抗静电等级: 3KV (HBM)
 

1.2 应用范围

 2-6 节 AA/AAA 干电池供电的玩具马达驱动
 2-6 节镍-氢/镍-镉充电电池供电的玩具马达驱

 1-2 节锂电池供电的马达驱动

1.3 概述

        该产品采用 H 桥电路结构设计,采用高可靠性功率管工艺,特别适合驱动线圈、马达等感性负载。电路内部集成 N 沟道和 P 沟道功率 MOSFET,工作电压范围覆盖 2V 到 9.6V。 27℃, VDD=6.5V,两个通道同时工作的条件下, 2 通道最大持续输出电流达到 0.8A,最大峰值输出电流达到 1.5A; 1 通道最大持续输出电流达到 1.5A,最大峰值输出电流达到2.5A。
        该电路为功率器件,本身具备一定内阻,电路的发热与负载电流、功率管导通内阻以及环境温度密切相关。电路设计有芯片级温度检测电路,实时监控芯片内部发热,当芯片内部温度超过设定值时(典型值 150℃),产生功率管关断信号,关闭负载电流,避免因异常使用导致的温度持续升高,进而造成塑料封装冒烟、起火等严重安全事故。芯片内置的温度迟滞电路,确保电路恢复到安全温度后,才允许重新对功率管进行控制。

1.4 电气属性

1.5 经典应用线路

特别注意事项:
图 1 中电容 C1 为功率电源与地之间的去耦电容,应用时电容 C1 的容值大小根据应用条件的不同可以有不同的选择,具体规定如下:
A、在 VDDx 电压小于 7.2V(4 节全新干电池),峰值电流不超过 1.5A 的应用条件下,电容 C1 可以省掉。
B、在 VDDx 电压 7.2V-9.6V 之间,峰值电流超过 1.5A 的应用条件下,电容 C1 不能省掉,需要根据实际
电机的情况,电容 C1 的值在 47uF-100uF 之间选择。
C、电容 C1 的类型不限制,可以是瓷片电容也可以是电解电容。逻辑电源 VCCx 对地电容 C2 必须至少需要 4.7uF,实际应用时不需要靠近芯片单独添加一个电容,可以与其它控制芯片(RX2、 MCU)等共用。如果 VCCx 对地没有任何电容,当电路因过载进入过热保护模式后,电路可能会进入锁定状态。进入锁定状态后,必须重新改变一次输入信号的状态,电路才能恢复正常。只要VCCx 对地有超过 4.7uF 电容,电路就不会出现锁定状态。图 1 中驱动电路 OUTAx 与 OUTBx 之间的 0.1uF 电容(C3、 C4)是表示接在马达两端的电容,不需要单独添加。

 

应用说明
1、基本工作模式
a)待机模式
        在待机模式下, INAx=INBx=L。包括驱动功率管在内的所有内部电路都处于关断状态。电路消耗极低极低的电流。此时马达输出端 OUTAx 和 OUTBx 都为高阻状态。


b)正转模式
        正转模式的定义为: INAx=H, INBx=L,此时马达驱动端 OUTAx 输出高电平,马达驱动端 OUTBx 输出低电平时,马达驱动电流从 OUTAx 流入马达,从 OUTBx 流到地端,此时马达的转动定义为正转模式。


c)反转模式
        反转模式的定义为: INAx=L, INBx=H,此时马达驱动端 OUTBx 输出高电平,马达驱动端 OUTAx 输出低电平时,马达驱动电流从 OUTBx 流入马达,从 OUTAx 流到地端,此时马达的转动定义为反转模式。


d)刹车模式
        刹车模式的定义为: INAx=H, INBx=H,此时马达驱动端 OUTAx 以及 OUTBx 都输出低电平,马达内存储的能量将通过 OUTAx 端 NMOS 管或者 OUTBx 端 NMOS 快速释放,马达在短时间内就会停止转动。注意在刹车模式下电路将消耗静态功耗。

e)PWM 模式 A
        当输入信号 INAx 为 PWM 信号, INBx=0 或者 INAx=0, INBx 为 PWM 信号时,马达的转动速度将受 PWM信号占空比的控制。在这个模式下,马达驱动电路是在导通和待机模式之间切换,在待机模式下,所有功率管都处于关断状态,马达内部储存的能量只能通过功率 MOSFET 的体二极管缓慢释放。

注意:由于工作状态中存在高阻状态,因此马达的转速不能通过 PWM 信号的占空比精确控制。如果PWM 信号的频率过高,马达会出现无法启动的情况。

二、电机介绍(本节内容来自韦东山老师课程资料)

28BYJ-48 是一款常见的步机电机,其名称的含义为外径 28 毫米四相八拍
式永磁减速型步进电机。 型号的含义如下:
① 28:步进电机的有效最大外径是 28 毫米
② B:表示是步进电机
③ Y:表示是永磁式
④ J:表示是减速型(减速比 1:64)
⑤ 48:表示四相八拍
先说什么是“4 相永磁式”的概念, 28BYJ-48 的内部结构示意图如下所示:

        先看里圈,它上面有 6 个齿,分别标注为 0~5,这个叫做转子,顾名思义,它是要转动的,转子的每个齿上都带有永久的磁性,是一块永磁体,这就是“永磁式”的概念。
        再看外圈,这个就是定子,它是保持不动的,实际上它是跟电机的外壳固定在一起的,它上面有 8 个齿,而每个齿上都缠上了一个线圈绕组,正对着的 2 个齿上的绕组又是串联在一起的,也就是说正对着的 2 个绕组总是会同时导通或关断的,如此就形成了 4 相,在图中分别标注为 A-B-C-D,这就是“ 4 相”的概念。每组绕组各有一端连接到公共端,另外一端由独立的引线引出,如下图:步进电机一共有 5 根线引出,红色是公共端,接 5v 电源,其他四根分别对应 A,B,C ,D 四个绕组的另外一端。
        怎样让电机转动起来呢?
        首先,假设我们让 B 线导通,此时转子 0 和 3 都对应 B 有一个吸引力;可以看到的是, A 和 2 之间有一个很小的夹角,当我们截断 B,导通 A 时,转子就会顺时针转动对齐 A。此时,我们 D 和 4 之间的夹角也减到了最小(再小就是正对),为了让转子再旋转一点,我们可以先关闭 A,导通 D,此时 4 和 D 之间产生的吸引力,使电机又顺时针转动了一点。
        当我们依次单独导通 BADC 时,电动机就顺时针转到起来了。
        那么很明显,当完成一个 B-A-D-C 的四节拍操作后,转子的 3 号齿原来对准 B 定子,现在对准 C 定子,即转子转过了八分之一圈。依此类推, 8 个四节拍以后转子将转过完整的一圈,而其中单个节拍使转子转过的角度就很容易计算出来了,即 360 度/(8*4)=11.25 度,这个值就叫做步进角度。 而上述这种工作模式就是步进电机的单四拍模式-单相绕组通电四节拍。同样的道理,如果想让转子逆时针转动,可以依次单独导通 BCDA。我们再来介绍一种具有更优性能的工作模式,那就是在单四拍的每两个节拍之间再插入一个双绕组导通的中间节拍,组成八拍模式。
        比如,在逆时针转动过程中,从 B 相导通到 C 相导通的过程中,加入一个 B 相和 C 相同时导通的节拍,这个时候,由于 B、 C 两个绕组的定子齿对它们附近的转子齿同时产生相同的吸引力,这将导致这两个转子齿的中心线对比到 B、 C 两个绕组的中心线上,也就是新插入的这个节拍使转子转过了上述单四拍模式中步进角度的一半,即 5.625 度。这样一来,就使转动精度增加了一倍,而转子转动一圈则需要 8*8=64 拍了。另外,新增加的这个中间节拍, 还会在原来单四拍的两个节拍引力之间又加了一把引力,从而可以大大增加电机的整体扭力输出,使电机更“有劲”了,而且更平顺。下表给出八拍模式下电机绕组激励时序(电机引线颜色可能因厂家不同而不同):

本文将以八拍模式展开编程演示,当按照下表的数值,连续给电机提供激励时,电机就转了起来。

        另外, 28BYJ-48 为减速电机,电机输出的转速并不等于转子的转速。 下图是这个 28BYJ-48 步进电机的拆解图:

        从图中可以看到,位于最中心的那个白色小齿轮才是步进电机的转子输出,64 个节拍只是让这个小齿轮转了一圈,然后它带动那个浅蓝色的大齿轮,这就是一级减速。
        大家看一下右上方的白色齿轮的结构,除电机转子和最终输出轴外的 3 个传动齿轮都是这样的结构,由一层多齿和一层少齿构成,而每一个齿轮都用自己的少齿层去驱动下一个齿轮的多齿层,这样每 2 个齿轮都构成一级减速,一共就有了 4 级减速。

三、 代码编写思路

        因为电机驱动需要大电流,单片机直接供电驱动不起来,并且惯性导致电机继续运转产生的反向电流也会烧毁电机所以驱动电机需要单独的驱动模块上面模块介绍就是在介绍这个模块。

这是原理图

        J1 连接到 100ASK_IMX6ULL 基板的扩展接口, J4 连接到 28BYJ-48 电机的接线插座,这样 100ASK_IMX6ULL 就通过驱动芯片 MX1508 与电机建立了控制关系。
        MX1508 芯片采用 H 桥电路结构设计,采用高可靠性功率管工艺,特别适合驱动线圈、马达等感性负载,内置两路驱动电路,可同时驱动两个线圈马达,工作电压范围覆盖 2V 到 9.6V。电路设计有芯片级温度检测电路,实时监控芯片内部发热,当芯片内部温度超过设定值时(典型值 150℃ ),启动保护功能,芯片内置的温度迟滞电路,确保电路恢复到安全温度后,才允许重新对功率管进行控制。

从扩展板原理图和电机驱动板原理图,可以得到 GPIO 和相位的关系如下:

        我们知道,按照下图的时序就可以驱动电机转动,那么可以沿着信号传输线路推导出 100ASK_IMX6ULL 的 GPIO 的信号序列,这样我们就可以开始编程

        可以看出 OUTB1、 OUTA1 不能同时输出 1(只能同时为高阻态),因步进电机的公共端是接到 5V 的,可以用高阻态代替 1,OUTB2、 OUTA2 也是如此。根据电机激励序列推出驱动芯片的输出序列;根据 MX1508 输入输出真值表,可 以 从 输 出 信 号 序 列 推 出 输 入 序 列 ; 由 电 机 驱 动 芯 片 的 输 入 推 导 出100ASK_IMX6ULL 的 GPIO 的信号序列:

        以第 1 个节拍为例,想让 D 输出 0, ABC 输出高电平或是高阻态,怎么办?换句话说,想让 OUTB2=L, OUTA2、 OUTA1、 OUTB1 等于 H 或 Z,怎么办?

所以,要让 D 输出 0, ABC 输出高电平或是高阻态时,需要: INA1=L、 INB1=L、
INA2=H、 INB2=L。
即: GPIO4_19=0、 GPIO4_20=0、 GPIO4_21=1、 GPIO4_22=0,用二进制表示即为: 0b0100,即 0x04。将信号序列即 8 个节拍对应的 GPIO 值存到数组中:

        配置好 GPIO 后,将数组中的数据循环写到 GPIO 输出寄存器,就可以驱动电机转动起来。
按照反向的顺序循环写,就可以实现电机反转。 调节两个节拍间的周期,可以改变电机转速。
根据电机的参数:空载牵入频率≥600Hz 可知,两个节拍之间的时间间隔不宜小于 1.6ms。 下面开始写程序

四、 驱动程序

#include "asm-generic/gpio.h"
#include "asm/gpio.h"
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>

struct gpio_desc{
	int gpio;
	int irq;
    char *name;
    int key;
	struct timer_list key_timer;
} ;

static struct gpio_desc gpios[] = {
    {115, 0, "motor_gpio0", },
    {116, 0, "motor_gpio1", },
    {117, 0, "motor_gpio2", },
    {118, 0, "motor_gpio3", },
};

/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_class;

/* 马达引脚设置数字 */
static int g_motor_pin_ctrl[8]= {0x2,0x3,0x1,0x9,0x8,0xc,0x4,0x6};
static int g_motor_index = 0;

void set_pins_for_motor(int index)
{
	int i;
	for (i = 0; i < 4; i++)
	{
		gpio_set_value(gpios[i].gpio, g_motor_pin_ctrl[index] & (1<<i) ? 1 : 0);
	}
}

void disable_motor(void)
{
	int i;
	for (i = 0; i < 4; i++)
	{
		gpio_set_value(gpios[i].gpio, 0);
	}
}

/* int buf[2];
 * buf[0] = 步进的次数, > 0 : 逆时针步进; < 0 : 顺时针步进
 * buf[1] = mdelay的时间
 */
static ssize_t motor_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int ker_buf[2];
    int err;
	int step;


    if (size != 8)
        return -EINVAL;

    err = copy_from_user(ker_buf, buf, size);
    
    
	if (ker_buf[0] > 0)
	{
		/* 逆时针旋转 */
		for (step = 0; step < ker_buf[0]; step++)
		{
			set_pins_for_motor(g_motor_index);
			mdelay(ker_buf[1]);
			g_motor_index--;
			if (g_motor_index == -1)
				g_motor_index = 7;

		}
	}
	else
	{
		/* 顺时针旋转 */
		ker_buf[0] = 0 - ker_buf[0];
		for (step = 0; step < ker_buf[0]; step++)
		{
			set_pins_for_motor(g_motor_index);
			mdelay(ker_buf[1]);
			g_motor_index++;
			if (g_motor_index == 8)
				g_motor_index = 0;

		}
	}

	/* 改进:旋转到位后让马达不再消耗电源 */
	disable_motor();
    return 8;    
}

/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {
	.owner	 = THIS_MODULE,
	.write   = motor_write,
};

/* 在入口函数 */
static int __init motor_init(void)
{
    int err;
    int i;
    int count = sizeof(gpios)/sizeof(gpios[0]);
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	for (i = 0; i < count; i++)
	{
		err = gpio_request(gpios[i].gpio, gpios[i].name);
		gpio_direction_output(gpios[i].gpio, 0);
	}

	/* 注册file_operations 	*/
	major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv);  /* /dev/gpio_desc */

	gpio_class = class_create(THIS_MODULE, "100ask_gpio_key_class");
	if (IS_ERR(gpio_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "100ask_gpio_key");
		return PTR_ERR(gpio_class);
	}

	device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "motor"); /* /dev/motor */
	
	return err;
}

/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 */
static void __exit motor_exit(void)
{
    int i;
    int count = sizeof(gpios)/sizeof(gpios[0]);
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	device_destroy(gpio_class, MKDEV(major, 0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "100ask_gpio_key");

	for (i = 0; i < count; i++)
	{
		gpio_free(gpios[i].gpio);
	}
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(motor_init);
module_exit(motor_exit);

MODULE_LICENSE("GPL");


五、应用程序


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <stdlib.h>

static int fd;

/*
 * ./button_test /dev/motor -100  1
 *
 */
int main(int argc, char **argv)
{
	int buf[2];
	int ret;
	
	/* 1. 判断参数 */
	if (argc != 4) 
	{
		printf("Usage: %s <dev> <step_number> <mdelay_number>\n", argv[0]);
		return -1;
	}


	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	buf[0] = strtol(argv[2], NULL, 0);
	buf[1] = strtol(argv[3], NULL, 0);

	ret = write(fd, buf, 8);
	close(fd);
	
	return 0;
}


用法和以前一样用我们的makefile就好啦。

举报

相关推荐

0 条评论