S3C6410的PWM驱动实例

发布时间:2024-09-18  

我们使用PWM来控制蜂鸣器,主要是两种功能,一是使能蜂鸣器,并设置其频率;二是禁止蜂鸣器。这些操作均可通过ioctl来完成。所以在pwm的设备驱动中,主要也就是ioctl这个函数。


首先,我们要定义两个命令,用在ioctl函数中的switch语句中,至于怎么来定义这两个命令呢?其实简单的做法,我们可以将其定义为两个不同的常量,能用在switch语句中即可,但是这样会造成一定的问题。例如,其他的设备也有支持设个命令的,例如串口支持设置波特率等,很有可能在写代码时不小心,写成了串口,结果调用的时候,驱动没报错,因为串口也支持这个命令,解决办法就是不同的设备支持不同的命令号,及时目前有一部分驱动依旧是使用之前的那种简单定义命令号的方式,但是我们要严格要求自己。


那怎么样才能使得自己定义的命令号不和内核已存在的命令号冲突呢?内核定义了几个简单的宏,使得我们使用这些宏来定义自己的命令号就肯定没问题了。


在include/asm/ioctl.h文件里,这些宏是:

/* used to create numbers */

#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)

#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))

#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

#define _IOR_BAD(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))

#define _IOW_BAD(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))

#define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))


其实主要使用的就是上面四个,可以看到这个宏定义中还有一个_IOC宏定义,我们来看看是啥:

#define _IOC(dir,type,nr,size)

(((dir)  << _IOC_DIRSHIFT) |

((type) << _IOC_TYPESHIFT) |

((nr)   << _IOC_NRSHIFT) |

((size) << _IOC_SIZESHIFT)) 

       可以看到其实是对四个因素的结合,第一个是传输方向,第二个是幻数,第三个是序数,第四个是数据大小。

       首先是传输方向,以应用为中心考察,如果数据从驱动传输到应用,那么就是读取操作,如果数据从应用传输到驱动,那就是写入操作;

       再者是幻数(IMAGENUMBER),这个是可以自己定义的,有8bits宽,整个驱动都使用这个数字;

       然后就是序数,这个算得是幻数大类里面的小类了,因为一个ioctl里面一般不止一个命令,所以,这个主要是用来区别各个命令的,上面的幻数在各个命令你都是一样的。

       最后就是传输数据的大小,可以通过对参数使用sizeof运算符获取。

       下面来举个例子,在我们的pwm驱动里面有两个命令,一个是启动并设置频:PWM_START,另外一个是PWM_STOP。在这个过程中不涉及数据的传输,所以使用上面的第一个宏_IO(type, nr)。

       一个参数type是一个幻数,我们可以定义为3825:

      #define PWM_MAGIC

3825

      然后是定义命令序列号,这里有两个命令,序号我分别选为0和1,因此整个可以这样定义:

      #define PWM_START _IO(PWM_MAGIC, 0)

      #define PWM_STOP _IO(PWM_MAGIC, 1)

      有了这两个定义,我们就可以在switch中如下使用:

    switch(cmd)

    {

    case PWM_START:

    ...

    break;

    case PWM_STOP:

    ...

    break;

    default:

    ...

    }

    再来说说实现使能pwm输出控制蜂鸣器和禁止pwm输出,其实都是通过控制GPF14、15脚的功能。GPF14是PWM0,GPF15是PWM1.看你的开发板使用那一路PWM输出驱动蜂鸣器的。那么设置频率怎么实现呢?其实pwm是内部定时器的功能,我们只需要设置几个相关的特殊寄存器即可,这涉及都PWM内容,上一节有讲到哦,具体每个寄存器给什么值,这个看一下芯片手册,看一下上一篇博文就知道了,这里不再细说。

    接下来就看看一个pwm的驱动程序,通过上面的内容,我相信这个驱动一目了然,呵呵!对照上面内容看,收获更大。

    

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include


#include

#include

#include


#include

#include

#include

#include


#define DEVICE_NAME     'pwm'


#define PWM_IOCTL_SET_FREQ 1

#define PWM_IOCTL_STOP 0


static struct semaphore lock;


/* freq:  pclk/50/16/65536 ~ pclk/50/16 

  * if pclk = 50MHz, freq is 1Hz to 62500Hz

  * human ear : 20Hz~ 20000Hz

  */

static void PWM_Set_Freq( unsigned long freq )

{

unsigned long tcon;

unsigned long tcnt;

unsigned long tcfg1;

unsigned long tcfg0;


struct clk *clk_p;

unsigned long pclk;


unsigned tmp;


tmp = readl(S3C64XX_GPFCON);

tmp &= ~(0x3U << 28);

tmp |=  (0x2U << 28);

writel(tmp, S3C64XX_GPFCON);


tcon = __raw_readl(S3C_TCON);

tcfg1 = __raw_readl(S3C_TCFG1);

tcfg0 = __raw_readl(S3C_TCFG0);


//prescaler = 50

tcfg0 &= ~S3C_TCFG_PRESCALER0_MASK;

tcfg0 |= (50 - 1); 


//mux = 1/16

tcfg1 &= ~S3C_TCFG1_MUX0_MASK;

tcfg1 |= S3C_TCFG1_MUX0_DIV16;


__raw_writel(tcfg1, S3C_TCFG1);

__raw_writel(tcfg0, S3C_TCFG0);


clk_p = clk_get(NULL, 'pclk');

pclk  = clk_get_rate(clk_p);

tcnt  = (pclk/50/16)/freq;

__raw_writel(tcnt, S3C_TCNTB(0));

__raw_writel(tcnt/2, S3C_TCMPB(0));

tcon &= ~0x1f;

tcon |= 0xb; //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0

__raw_writel(tcon, S3C_TCON);

tcon &= ~2; //clear manual update bit

__raw_writel(tcon, S3C_TCON);

}


void PWM_Stop( void )

{

unsigned tmp;

tmp = readl(S3C64XX_GPFCON);

tmp &= ~(0x3U << 28);

writel(tmp, S3C64XX_GPFCON);

}


static int s3c64xx_pwm_open(struct inode *inode, struct file *file)

{

if (!down_trylock(&lock))

return 0;

else

return -EBUSY;

}



static int s3c64xx_pwm_close(struct inode *inode, struct file *file)

{

up(&lock);

return 0;

}



static long s3c64xx_pwm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)

{

switch (cmd) {

case PWM_IOCTL_SET_FREQ:

if (arg == 0)

return -EINVAL;

PWM_Set_Freq(arg);

break;


case PWM_IOCTL_STOP:

default:

PWM_Stop();

break;

}


return 0;

}



static struct file_operations dev_fops = {

    .owner = THIS_MODULE,

    .open = s3c64xx_pwm_open,

    .release = s3c64xx_pwm_close, 

    .unlocked_ioctl = s3c64xx_pwm_ioctl,

};


static struct miscdevice misc = {

.minor = MISC_DYNAMIC_MINOR,

.name = DEVICE_NAME,

.fops = &dev_fops,

};


static int __init dev_init(void)

{

int ret;


sema_init(&lock, 1);

ret = misc_register(&misc);


printk (DEVICE_NAME'tinitializedn');

    return ret;

}


static void __exit dev_exit(void)

{

misc_deregister(&misc);

}


module_init(dev_init);

module_exit(dev_exit);

MODULE_LICENSE('GPL');

MODULE_AUTHOR('FriendlyARM Inc.');

MODULE_DESCRIPTION('S3C6410 PWM Driver');


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>