[番外篇]直流电机桥源码分析&LED驱动例程开发
- 直流电机桥测试代码分析
- 直流电机桥驱动代码分析
- 仿写HBLED驱动程序
- 利用芯片手册修改设备树
- 利用原理图寻找引脚
- 仿写代码
- 仿写HBLED测试程序
- 实际测试
直流电机桥测试代码分析
#define DCM_IOCTRL_SETPWM (0x10)
#define DCM_IOCTRL_STATUS (0x20)
#define DCM_IOCTRL_STOP (0x30)
#define DCM_IOCTRL_START (0x40)
定义了对设备的操作命令,类似于枚举类型。在驱动中也同样有所定义。
char *DCM_DEV="/dev/DCMotor";
声明了设备在系统中注册的名字,和驱动要一一对应,在系统中设备统一放在/dev/文件夹下面。
ioctl(dcm_fd, cmd, arg);
调用驱动中定义的函数,对设备进行操作,dcm_fd是设备的节点,cmd是之前定义过操作的类型,arg是对应的参数。
如果仔细阅读,还会发现在主函数中定义的status变量从来没有使用,可以删除。
直流电机桥驱动代码分析
module_init(uptech_dcm_init);
module_exit(uptech_dcm_exit);
在字符型驱动开发中,我们已经讲过,上面两个函数分别是加载驱动和卸载驱动所执行的函数。
#define DEVICE_NAME "DCMotor"
#define DEV_MAJOR 0
驱动中同样定义了设备名称,DEV_MAJOR用于模块被加载时初始化函数调用的创建节点函数,为0表示自动获取设备号。
pwm_request(0, "my_pwm0");
pwm_config(pwm0, 10000000, 100000000);
pwm_disable(pwm0);
pwm_enable(pwm0);
pwm在linux中提供了很多api,pwm_request是申请pwm设备,第一个参数是i.MX6内置的pwmID号,从0到3一共4个,第二个参数是对此设备的简称。
pwm_config是配置pwm设备,第一个参数是pwm设备,第二个参数是一个周期内,高电平的时间,单位是纳秒,第三个参数是周期的长度,单位也是纳秒,在本例中,pwm0对应的引脚将以100000000纳秒为周期,其中前10000000纳秒为高电平,剩余时间都为低电平,对于电机来说,高电平转,低电平不转,但是由于惯性,电机仍有旋转趋势,控制占空比(高电平占周期的时间比)即可控制转速,占空比越大,转速越快。
pwm_disable是关闭pwm设备,停止pwm信号的作用,相反,pwm_enable是使能pwm设备,开始pwm信号,两个函数的参数都是pwm设备。
仿写HBLED驱动程序
为了不和直流电机桥已有的引脚冲突,我们选择pwmID为2的设备作为LED的Signal引脚。HBLED的性能如下。
- 需要5V或3.3V供电。
- 需要GND引脚接地。
- Signal引脚高电平时灯亮,低电平时灯灭。
利用芯片手册修改设备树
接下来我们打开i.MX6的芯片手册,位于/01 创新创客智能硬件平台光盘资料/创新创客智能硬件平台光盘V1.0/04_硬件/0401_数据手册/13_Cortex-A 系列核心板/IMX6/IMX6SDLRM.pdf,搜索pwm3(芯片手册中定义id为1到4,在linux中申请的id为0到3)。
可以看见SW_PAD_CTL_PAD_SD1_DAT1引脚可以用于PWM3_OUT功能。
接下来修改设备树,在虚拟机终端中输入sudo nano /home/uptech/fsl-6dl-source/kernel-3.14.28/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
发现此引脚已经被复用为GPIO,将此引脚注释掉。
找到后面定义pwm引脚复用的地方,在后面添加以下代码。
pinctrl_pwm3: pwm3grp {
fsl,pins = <
MX6QDL_PAD_SD1_DAT1__PWM3_OUT 0x1b0b1
>;
};
找到pwm定义的位置,在后面添加pwm3的定义。
&pwm3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm3>;
status = "okay";
};
如果读者想深入了解0x1b0b1、MX6QDL_PAD_SD1_DAT1__PWM3_OUT等写法的原因,可以前往文件资料中/01 创新创客智能硬件平台光盘资料/创新创客智能硬件平台光盘V1.0/03_系统/0301_Linux/03_linux系统移植/01_doc/03_i.MX6-魔法师inux系统实验指导书V2.2.pdf的第四章继续学习。简单来说前者是引脚复用寄存器的配置,可以在芯片手册中查到,后者是引脚复用后的专用名称,可以在pinfunc.h中找到。
修改完设备树需要重新编译内核和设备树文件,然后重新烧录系统。
利用原理图寻找引脚
打开文件资料中/01 创新创客智能硬件平台光盘资料/创新创客智能硬件平台光盘V1.0/04_硬件/0402_原理图/12_Cortex-A 系列核心板与底板 文件夹内的i.MX6 核心板 V1.2.pdf和魔法师Cortex-A系列底板 V1.3.pdf。
在核心板的原理图中搜索SD1_DAT1,可以看到SD1_DAT1对应的是CSI0 RST B。
打开底板原理图,搜索CSI0 RST B,可以看到对应了J5端口的20号引脚,说明我们LED的signal需要插在此引脚上。
仿写代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/pwm.h>
#include <linux/fs.h>
#define DEVICE_NAME "HBLED"
#define DEV_MAJOR 0
#define DRIVER_AUTHOR "sertonry@obcube.cn"
#define DRIVER_DESCRIPTION "Unknown_HighBrightnessLED"
#define HBLED_IOCTRL_SETPWM (0x10)
#define HBLED_IOCTRL_OFF (0x20)
#define HBLED_IOCTRL_ON (0x30)
static struct cdev hbLEDcDev;
struct class* hbLEDclass;
dev_t devNo;
struct pwm_device* pwm;
static int defaultPWMRate = 50;
static long HBLED_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case HBLED_IOCTRL_SETPWM:
if (arg > 100 || arg < 0)
{
printk("HBLED_IOCTRL_SETPWM arg can't is %ld!\n", arg);
break;
}
pwm_enable(pwm);
pwm_config(pwm, 20000 * arg, 2000000);
break;
case HBLED_IOCTRL_OFF:
pwm_config(pwm, 20000 * 0, 2000000);
pwm_disable(pwm);
break;
case HBLED_IOCTRL_ON:
pwm_enable(pwm);
break;
}
return 0;
}
static struct file_operations HBLED_gpio_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = HBLED_ioctl,
};
static int create_dev_node(void)
{
int ret;
if (DEV_MAJOR)
{
devNo = MKDEV(DEV_MAJOR, 0);
ret = register_chrdev_region(devNo, 1, DEVICE_NAME);
}
else {
ret = alloc_chrdev_region(&devNo, 0, 1, DEVICE_NAME);
}
if (0 < ret) {
return ret;
}
cdev_init(&hbLEDcDev, &HBLED_gpio_fops);
ret = cdev_add(&hbLEDcDev, devNo, 1);
if (ret) {
printk("cdev_add(); failed! ret_num=%d\n", ret);
}
hbLEDclass = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(hbLEDclass)) {
printk("Err: failed in creating class.\n");
return -1;
}
device_create(hbLEDclass, NULL, devNo, NULL, DEVICE_NAME);
return 0;
}
int __init HBLED_init(void)
{
int ret;
pwm = pwm_request(2, "hbLEDpwm");
if (IS_ERR(pwm)) {
printk("ERROR is %ld\n", PTR_ERR(pwm));
return -1;
}
pwm_config(pwm, 20000 * defaultPWMRate, 2000000);
ret = create_dev_node();
if (ret)
return ret;
return 0;
}
void __exit HBLED_exit(void)
{
pwm_disable(pwm);
pwm_free(pwm);
cdev_del(&hbLEDcDev);
device_destroy(hbLEDclass, devNo);
class_destroy(hbLEDclass);
unregister_chrdev_region(devNo, 1);
}
module_init(HBLED_init);
module_exit(HBLED_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
对应的Makefile文件如下
obj-m :=HighBrightnessLED_driver.o
KERNELDIR := /home/uptech/fsl-6dl-source/kernel-3.14.28/
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
仿写HBLED测试程序
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#define HBLED_IOCTRL_SETPWM (0x10)
#define HBLED_IOCTRL_OFF (0x20)
#define HBLED_IOCTRL_ON (0x30)
char* HBLED_DEV = "/dev/HBLED";
int cmd_group[] = { HBLED_IOCTRL_SETPWM, HBLED_IOCTRL_ON, HBLED_IOCTRL_OFF };
void print_menu()
{
printf("-----HBLED_TEST-----\n");
printf("| 1.SETPWM |\n");
printf("| 2.ON |\n");
printf("| 3.OFF |\n");
printf("| 4.EXIT |\n");
printf("-------------------\n");
}
int main(void)
{
int hbled_fd, menu_num, cmd, arg;
hbled_fd = open(HBLED_DEV, O_WRONLY);
if (hbled_fd < 0) {
printf("Error opening %s device\n", HBLED_DEV);
return 1;
}
while (1)
{
print_menu();
scanf("%d", &menu_num);
if (menu_num == 4)
break;
if (menu_num == 1)
{
printf("Input brightness value(0~100)\n");
scanf("%d", &arg);
if (arg > 100 || arg < 0)
{
printf("Brightness can't be %ld!\n", arg);
continue;
}
}
cmd = cmd_group[menu_num - 1];
ioctl(hbled_fd, cmd, arg);
}
close(hbled_fd);
return 0;
}
对应的Makefile文件如下
CC = arm-poky-linux-gnueabi-gcc -march=armv7-a -mthumb-interwork -mfloat-abi=hard -mfpu=neon -mtune=cortex-a9 --sysroot=/opt/poky/1.7/sysroots/cortexa9hf-vfp-neon-poky-linux-gnueabi
TARGET = HighBrightnessLED_test
all:
$(CC) -o $(TARGET) $(TARGET).c
clean:
rm $(TARGET)
实际测试
亮度为0
亮度为5%
亮度为50%
亮度为100%