Petalinux系列——4.编写字符设备驱动4路PWM控制器

编写字符设备驱动4路PWM控制器

本工程首先搭建一个能够配置占空比和频率的PWM IP,然后给它套上AXI协议使其可以被PS访问。接着在Vivado中使用Block Diagram搭建一个完整的硬件工程,在SDK中编写驱动测试PWM模块的功能,最后在Petalinux中编写内核空间驱动。

  • Vivado工程

首先我们要准备PWM控制器的硬件工程,可以参考米联客
https://www.cnblogs.com/milinker/p/6474727.html

  • 内核空间驱动编写
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>

#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>

#define DEVICE_NAME "PWM_MOUDLE"
#define PWM_MOUDLE_PHY_ADDR 0x43C00000		//This Address is based Vivado
#define PWM_CHANNEL 4

MODULE_AUTHOR("Xilinx XUP");
MODULE_DESCRIPTION("PWM moudle dirver");
MODULE_VERSION("v1.0");
MODULE_LICENSE("GPL");

static int pwm_driver_major;
static struct class* pwm_driver_class = NULL;

struct pwm_dev {
    dev_t devt;
    struct device *device;
    unsigned long freq_addr;
    unsigned long duty_addr;
};
static struct pwm_dev pwm_dev[PWM_CHANNEL];

static long frequency=0;

static struct file_operations pwm_driver_fops = {
    .owner = THIS_MODULE,
};

static ssize_t sys_pwm_frequency_set(struct device* dev, struct device_attribute* attr, const char* buf, size_t count)
{
    long value = 0;
    int i;
    frequency=0;
    iowrite32(value, pwm_dev[MINOR(dev->devt)].freq_addr); //close pwm moudle before we modfiy the frequency

    for (i = 0; i < count-1; i++){
        frequency  *= 10;
        frequency += buf[i] - '0';
    }
    if(value>100000000) value=100000000;
    value=100000000/frequency;  // 100Mhz/frequency 100Mhz is set by Vivado
    iowrite32(value, pwm_dev[MINOR(dev->devt)].freq_addr);

    printk("CH%d freq:%ld, address:%x\n",MINOR(dev->devt), value, pwm_dev[MINOR(dev->devt)].freq_addr);
    return count;
} 

static ssize_t sys_pwm_duty_set (struct device* dev, struct device_attribute* attr, const char* buf, size_t count) //duty cycle 
{
	long value = 0;
	int i;

	iowrite32(value, pwm_dev[MINOR(dev->devt)].duty_addr); //close pwm moudle before we modfiy the duty cycle
	for (i = 0; i < count-1; i++){
		value  *= 10;
		value += buf[i] - '0';
	}
	if (value>100) 
		value=100;
	value = 100000000 / frequency * value / 100;
	iowrite32(value, pwm_dev[MINOR(dev->devt)].duty_addr);

    printk("CH%d duty:%ld, address:%x\n",MINOR(dev->devt), value, pwm_dev[MINOR(dev->devt)].duty_addr);

    return count;
} 

static DEVICE_ATTR(frequency, S_IWUSR, NULL, sys_pwm_frequency_set);
static DEVICE_ATTR(duty, S_IWUSR, NULL, sys_pwm_duty_set);

static int __init pwm_driver_module_init(void)
{
    int i,ret;
    char name[4];
    pwm_driver_major=register_chrdev(0, DEVICE_NAME, &pwm_driver_fops);
    if (pwm_driver_major < 0){
        printk("failed to register device.\n");
        return -1;
    }

    pwm_driver_class = class_create(THIS_MODULE, "pwm");
    if (IS_ERR(pwm_driver_class)){
        printk("failed to create pwm class.\n");
        unregister_chrdev(pwm_driver_major, DEVICE_NAME);
        return -1;
    }

	for(i=0;i<PWM_CHANNEL;i++ )
    {
        pwm_dev[i].devt=MKDEV(pwm_driver_major, i);
        pwm_dev[i].device = device_create(pwm_driver_class, NULL, pwm_dev[i].devt, NULL, "CH%d", MINOR(pwm_dev[i].devt));
        if (IS_ERR(pwm_dev[i].device)){
            printk("failed to create pwm CH%d.\n",MINOR(pwm_dev[i].devt));
            unregister_chrdev(pwm_driver_major, DEVICE_NAME);
            return -1;
        }
        pwm_dev[i].freq_addr = (unsigned long)ioremap(PWM_MOUDLE_PHY_ADDR, 64)+i*8;
        pwm_dev[i].duty_addr = (unsigned long)ioremap(PWM_MOUDLE_PHY_ADDR, 64)+i*8+4;
        printk("pwm channel %d freq_addr : %x, duty_addr : %x\r\n", i,pwm_dev[i].freq_addr,pwm_dev[i].duty_addr);
        
        ret = device_create_file(pwm_dev[i].device, &dev_attr_frequency);
        if (ret < 0)
        printk("failed to create pwm_frequency endpoint\n");
        ret = device_create_file(pwm_dev[i].device, &dev_attr_duty);
        if (ret < 0)
        printk("failed to create pwm_duty endpoint\n");
    }

    printk(" pwm driver initial successfully!\n");
    return 0;
}

static void __exit pwm_driver_module_exit(void)
{
    int i=0;
    for(i=0;i<PWM_CHANNEL;i++ )
    {
        device_remove_file(pwm_dev[i].device, &dev_attr_frequency);
        device_remove_file(pwm_dev[i].device, &dev_attr_duty);
    }
    device_destroy(pwm_driver_class, MKDEV(pwm_driver_major, 0));
    class_unregister(pwm_driver_class);
    class_destroy(pwm_driver_class);
    unregister_chrdev(pwm_driver_major, DEVICE_NAME);
    printk("pwm module exit.\n");
}

module_init(pwm_driver_module_init);
module_exit(pwm_driver_module_exit);
  • 编译ko文件
    进入一个petalinux工程
$ petalinux-create -t modules -n pwm
$ vim project-spec/meta-user/recipes-modules/pwm/files/pwm.c //将pwm.c的内容替换成自己的,可以参照上面的代码pwm.c
$ petalinux-create -c rootfs //检查一下modules项有没有勾选pwm,如果没有勾选就选上

然后编译工程

$ petalinux-build

同样,编译结束后我们可以通过find指令找到pwm.ko文件

$ find . -name pwm.ko
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值