700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Linux利用platform_driver和设备树实现PWM驱动

Linux利用platform_driver和设备树实现PWM驱动

时间:2024-01-28 11:27:11

相关推荐

Linux利用platform_driver和设备树实现PWM驱动

Linux利用platform_driver和设备树实现PWM驱动

字符设备PWM驱动一、PWM驱动的硬件资源1.PWM工作原理2.PWM电路原理3.PWM内部结构二、具体代码1.设备树2.应用程序代码3.驱动程序代码头文件和命令提前准备好硬件相关的寄存器定义相关的函数和需要用到的变量对蜂鸣器相关的操作函数平台驱动相关函数

字符设备PWM驱动

Linux环境下利用platform_driver和设备树实现PWM驱动,此文以开发板FS4412为例。

一、PWM驱动的硬件资源

PWM,脉宽调制器,顾名思义就是一个输出脉冲宽度可以调整的硬件器件,它不仅脉冲宽度可调,频率也可以调整,它的核心部件是一个硬件定时器。

1.PWM工作原理

PWM管脚默认输出高电平,在图1中的时刻1将计数值设为 109,比较值设为109,在时刻2启动定时器,PWM立即输出低电平,在时钟的作用下,计数器开始做减法计数,当计数值减到和比较值一致时(时刻3),输出翻转,之后一直输出高电平。当计数到达0后(时刻4),再完成一次计数,在时刻5重新从109开始计数,输出再次变成低电平,如此周而复始就形成一个矩形波。波形的周期由计数值决定,占空比由比较值决定。在图1中,占空比为110/160,如果用于计数的时钟频率为freq,那么波形的频率就为freq/160。

2.PWM电路原理

通过查找原理图,FS4412使用了其中一路PWM输出(PWM0,对应管脚为GPD0.0)接蜂鸣器,如图2

3.PWM内部结构

PWM内部结构图如图3,PWM的输入时钟是PCLK,经讨8位的预分频后再经过第二次分频的时钟最终给到PWM0所对应的计数器0.TCNTB0是计数值寄存器,用于控制PWM输出波形的频率,

TCMPB0是比较寄存器,用于控制PWM输出波形的占空比。

二、具体代码

1.设备树

修改设备树,与驱动程序进行匹配 {.compatible = “fs,mybee”},然后驱动程序,才能通过设备树获取硬件资源设备树修改后需要重新编译,并下载到开发板运行,通过ls /proc/device-tree/ 可以在系统中查看获取的设备树信息

/linux-3.14/arch/arm/boot/dts下对应的设备树文件添加之后重新编译

mybee@11000ca0{compatible ="fs,mybee";reg = <0x114000A0 0x4>,<0x139d0000 0x14>;};

2.应用程序代码

代码如下(示例):

#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <string.h>#include <sys/ioctl.h>#define BUZZER_ON _IO('B',1)#define BUZZER_OFF _IO('B',2)int main(void){int fd;int ret;fd = open("/dev/buzzer",O_RDWR);if(fd==-1){perror("open");return -1;}//printf("open device success %d\n",fd);while(1){ioctl(fd,BUZZER_ON);sleep(1);ioctl(fd,BUZZER_OFF);sleep(1);} close(fd);return 0;}

3.驱动程序代码

头文件和命令

#include <linux/init.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/cdev.h>#include <linux/fs.h>#include <linux/err.h>#include <asm/io.h>#define BUZZER_ON _IO('B',1)#define BUZZER_OFF _IO('B',2)//需要与应用程序一致

提前准备好硬件相关的寄存器

#define GPD0CON 0x114000A0 #define TCFG00x139D0000 #define TCFG10x139D0004 #define TCON 0x139D0008#define TCNTB00x139D000C#define TCMPB00x139D0010unsigned int *gpd0con;unsigned int *tcfg0;unsigned int *tcfg1;unsigned int *tcon;unsigned int *tcntb0;unsigned int *tcmpb0;

定义相关的函数和需要用到的变量

//实现platform_driverint my_probe(struct platform_device *pdev);int my_remove(struct platform_device *pdev);long my_ioctl(struct file *pf, unsigned int cmd, unsigned long args);static struct resource *rescon;static struct resource *resdata;//设备资源static dev_t devnum;//申请设备号时使用static char *name ="mybuzzer";//设备名static struct cdev mycdev ;//字符设备static struct class * myclass;static struct device * mydevice;//设备节点static struct file_operations myops={.unlocked_ioctl = my_ioctl,};int buzzer_init(void);//操作函数int buzzer_on(void);int buzzer_off(void);//定义platform_driver对象struct of_device_id of_matches[]={{.compatible="fs,mybee"}, //修改匹配规则,从设备树 {}, //获取buzzer相关硬件信息};static struct platform_driver mydriver ={.probe = my_probe,.remove = my_remove,.driver = {.name = "mytest",.of_match_table = of_matches, //通过设备树匹配}, };

对蜂鸣器相关的操作函数

int buzzer_init(void)//注册{u32 tmp;tmp = readl(gpd0con);tmp &= ~0xf;tmp |= 0x2;writel(tmp,gpd0con);tmp = readl(tcfg0);tmp |= 0xff;writel(tmp,tcfg0);tmp = readl(tcfg1);tmp &= ~0xf;tmp |= 0x3;writel(tmp,tcfg1);writel(110,tcntb0);writel(110/2,tcmpb0);tmp = readl(tcon);tmp |= 0x1<<3;tmp |= 0x1<<1;writel(tmp,tcon);tmp = readl(tcon);tmp &= ~(0x1<<1);writel(tmp,tcon);return 0;}int buzzer_on(void)//打开{u32 tmp;printk("buzzer_on\n");tmp = readl(tcon);tmp |= 0x1;writel(tmp,tcon);return 0;}int buzzer_off(void)//关闭{u32 tmp;printk("buzzer_off\n");tmp = readl(tcon);tmp &= ~0x1;writel(tmp,tcon);return 0;}

平台驱动相关函数

int my_probe(struct platform_device *pdev)//与设备树匹配成功执行{int ret;//通过设备树获取 硬件资源printk("match\n");rescon = platform_get_resource(pdev,IORESOURCE_MEM,0);if(rescon==NULL){return -1;}printk("%#x\n",rescon->start);gpd0con = ioremap(rescon->start,4);resdata = platform_get_resource(pdev,IORESOURCE_MEM,1);if(resdata==NULL){return -1;}printk("%#x\n",resdata->start);tcfg0 = ioremap(resdata->start,4);//gpd0con = ioremap(GPD0CON,4);//tcfg0 = ioremap(TCFG0,4);tcfg1 = ioremap(resdata->start+4,4);tcon = ioremap(resdata->start+8,4);tcntb0 = ioremap(resdata->start+12,4);tcmpb0 = ioremap(resdata->start+16,4);//字符设备注册ret = alloc_chrdev_region(&devnum,0,1,name);//1.申请设备号if(ret!=0){goto failed_alloc;}cdev_init(&mycdev,&myops); //2.cdev初始化ret = cdev_add(&mycdev,devnum,1); //3.cdev添加到内核if(ret!=0){goto failed_add;}printk("register success %d,%d\n",MAJOR(devnum),MINOR(devnum));myclass = class_create(THIS_MODULE,"myclass");if(IS_ERR(myclass)){goto failed_class;}mydevice = device_create(myclass,NULL,devnum,NULL,"buzzer");if(IS_ERR(mydevice)){goto failed_device;}//硬件操作buzzer_init();buzzer_off();return 0;failed_device://失败之后的对应操作class_destroy(myclass);failed_class:cdev_del(&mycdev);failed_add:unregister_chrdev_region(devnum,1);failed_alloc:return -1;}int my_remove(struct platform_device *pdev)//卸载时执行{printk("driver remove\n");iounmap(gpd0con);//取消映射iounmap(tcfg0);iounmap(tcfg1);iounmap(tcon);iounmap(tcntb0);iounmap(tcmpb0);device_destroy(myclass,devnum);class_destroy(myclass);cdev_del(&mycdev);unregister_chrdev_region(devnum,1);return 0;}long my_ioctl(struct file *pf, unsigned int cmd, unsigned long args)//当应用层调用IO操作时就会调用到此函数{switch (cmd){case BUZZER_ON: buzzer_on(); break;case BUZZER_OFF: buzzer_off();break;default: return -1;}return 0;}static int mod_init(void)//模块加载函数{return platform_driver_register(&mydriver); //平台驱动注册}static void mod_exit(void)//模块卸载函数{platform_driver_unregister(&mydriver); //平台驱动注销}module_init(mod_init);//注册到内核module_exit(mod_exit);//卸载到内核MODULE_LICENSE("GPL");//模块信息

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。