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.设备树

  1. 修改设备树,与驱动程序进行匹配 {.compatible = “fs,mybee”},然后驱动程序,才能通过设备树获取硬件资源
  2. 设备树修改后需要重新编译,并下载到开发板运行,通过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 TCFG0      0x139D0000
#define TCFG1      0x139D0004
#define TCON       0x139D0008
#define TCNTB0     0x139D000C
#define TCMPB0     0x139D0010
unsigned int *gpd0con;
unsigned int *tcfg0;
unsigned int *tcfg1;
unsigned int *tcon;
unsigned int *tcntb0;
unsigned int *tcmpb0;

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

//实现platform_driver
int 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");//模块信息

Linux利用platform_driver和设备树实现PWM驱动相关推荐

  1. 【基于Linux系统设备树的SPI驱动编写方法】

    文章目录 前言 一.SPI驱动编写 1.修改设备树  a.设备树文件是什么?  b.设备树怎么改? 2.编写驱动 二.完善和测试 1.编译和应用程序  a.编译 && 拷贝到开发板命令 ...

  2. Linux dts设备树和platform驱动详解

    概念 小麦大叔 2019-05-06 22:56:31 12603 收藏 135 什么是设备树 dts(device tree)? 设备树(Device Tree)是描述计算机的特定硬件设备信息的数据 ...

  3. Linux驱动_设备树下LED驱动

    前言 学习完设备树基础知识后,完成设备树下LED驱动实验 一.修改设备树文件 在设备书根/节点下添加子节点led信息: alphaled {status = "okay";comp ...

  4. linux设备和驱动匹配的方法,Linux使用设备树的i2c驱动与设备匹配方式

    Linux使用设备树的i2c驱动与设备匹配有3种方式: of_driver_match_device acpi_driver_match_device i2c_match_id 源码: static ...

  5. 使用错误的设备树导致linux,Petalinux工程中设备树的介绍

    设备树是 Petalinux kernel 的关键组件,接下来以 2020.1 版本为例,为大家介绍一下在Xilinx Petalinux 工程中的设备树是如何产生,配置以及修改的. Petalinu ...

  6. 第五十四讲 设备树实现RGB驱动

    第五十四讲 设备树实现RGB驱动 文章目录 第五十四讲 设备树实现RGB驱动 一.基础知识 1.GPIO Write Mode 2.硬件连接 3.重要寄存器(参考IMXULL用户手册) RGB_R R ...

  7. iTOP-iMX6开发板-设备树内核-注册驱动例程

    本文档主要讲解在迅为iTOP-iMX6Q/D/PLUS 开发板的设备树内核(4.1.15)源码中,设备树注册 驱动和非设备树的类似. 1 注册驱动源码分析 设备树的内核驱动中,platform_dri ...

  8. linux内核机制之设备树

    1. 设备树(Device  Tree)基本概念及作用 在内核源码中,存在大量对板级细节信息描述的代码.这些代码充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目录,对 ...

  9. linux dts 语法格式,设备树DTS格式解析

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 宿主机: ubuntu16.04 开发板: tq-imx6ull 内核版本: linux-4.1.15 用实例讲解下设备 ...

最新文章

  1. rman备份常用命令
  2. CKfinder2.0.2版本破解
  3. 常见mysql性能优化方法
  4. 点滴积累【C#】---操作xml,将xml数据显示到treeview
  5. 数据库系统原理(第5章:数据库编程)
  6. Android访问瓦片地图 费流量,瓦片地图服务在线资源访问总结
  7. jquery ajax缓存问题解决方法小结
  8. 机器学习初学者入门实践:怎样轻松创造高精度分类网络
  9. gg修改器ios版下载
  10. 计算机预览正常打印乱码,打印机打印文件显示乱码该怎么办?
  11. 计算机execl必背知识点,【分享】Excel必备基础知识(1)
  12. 如何学好游戏编程 二
  13. 解析ICMAX国产存储芯片eMMC和UFS的区别
  14. 图片像素问题:如何保持图片大小不变而图片容量缩小
  15. java 通过 冰蓝 word 转pdf ,最大程度包装pdf 样式和word接近
  16. 1100 校庆 (25 分)
  17. 做一条USB A转Type C 数据线 和OTG线
  18. 新手小白一行代码快速生成HTML代码块
  19. DPDK in KVM
  20. 旧文备份:FFTW介绍

热门文章

  1. ArcGIS如何获取地理要素的几何属性
  2. 信息安全等级合规测评
  3. 来来来!关于iOS基础面试咱俩好好唠唠
  4. Django Web 开发极简实战
  5. axure转化成代码_​教大家如何查看Axure页面的代码
  6. 机器学习中,正态分布为何如此重要?
  7. css3实现3d效果的立方体动画
  8. 电子面单打印机设置 天元打印机 佳博1324D错位校准自检
  9. 漏洞复现:通过CVE-2022-30190上线CS
  10. 现役大学生必看!干货满满!