Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2374006
  • 博文数量: 298
  • 博客积分: 7876
  • 博客等级: 准将
  • 技术积分: 5500
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-23 13:39
文章存档

2013年(2)

2012年(142)

2011年(154)

分类: LINUX

2011-03-24 13:09:51

12mini2440pwm驱动(混杂设备驱动

 

 

注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方

1.本文所介绍的程序平台

开发板:arm9-mini2440

虚拟机为:Red Hat Enterprise Linux 5

开发板上系统内核版本:linux-2.6.32.2

 

混杂设备驱动

定义 :在Linux系统中,存在一类字符设备,它们共享一个主设备号(10),但次设备号不同,我们称这类设备为混杂设备(miscdevice)。所有的混杂设备形成一个链 ,对设备访问时内核根据次设备号查找到相应的miscdevice设备。

 

设备描述

Linux内核使用struct miscdevice来描述 一个混杂设备。

struct miscdevice  {

int minor;   /* 次设备号*/

const char *name; /* 设备名*/

const struct file_operations *fops; /*文件操作*/

struct list_head list;

struct device *parent;

struct device *this_device;

};

 

设备注册

Linux内核使用misc_register函数来注册 一个混杂设备驱动。

int misc_register(struct miscdevice * misc)

 

设备注销

misc_deregister(struct miscdevice * misc); 

 

2.程序清单

本次实验程序为网络代码,本人作了改动和较为详细的注释,如有错误请指出。

Pwm设置其频率和寄存器需要参考数据手册

 

mini2440_pwm.c

 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

#include /*s3c2410_gpio_cfgpin等函数*/

#include /*端口定义*/

 

#define DEVICE_NAME     "pwm"                        //设备名 pwm

 

#define PWM_IOCTL_SET_FREQ   1                 //定义宏常量,用于后面的ioctl中的switch case

#define PWM_IOCTL_STOP    2

 

static struct semaphore lock;                            //定义信号量 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 )         

  //设置pwm的频率,配置各个寄存器

{

    unsigned long tcon;

    unsigned long tcnt;

    unsigned long tcfg1;

    unsigned long tcfg0;

    struct clk *clk_p;

    unsigned long pclk;

 

   

    //set GPB0 as tout0, pwm output              设置GPB0为tout0,pwm输出

    s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0);

    tcon = __raw_readl(S3C2410_TCON);           //读取寄存器TCON到tcon

    tcfg1 = __raw_readl(S3C2410_TCFG1);        //读取寄存器TCFG1到tcfg1

    tcfg0 = __raw_readl(S3C2410_TCFG0);        //读取寄存器TCFG0到tcfg0

   

    //prescaler = 50

    //清除prescaler 0 [7:0]位

    tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;  // S3C2410_TCFG_PRESCALER0_MASK定时器0和

    //设置prescaler 0 [7:0]位                            //   1的预分频值的掩码,TCFG[0~8]

    tcfg0 |= (50 - 1);   // 预分频为50HZ

 

   

    //mux = 1/16 详细请看数据手册

    tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;     //S3C2410_TCFG1_MUX0_MASK定时器0分割值的掩

                                                                             //码:TCFG1[0~3]

    tcfg1 |= S3C2410_TCFG1_MUX0_DIV16;         //定时器0进行16分割

    __raw_writel(tcfg1, S3C2410_TCFG1);                  //把tcfg1的值写到分割寄存器S3C2410_TCFG1中

    __raw_writel(tcfg0, S3C2410_TCFG0);                  //把tcfg0的值写到预分频寄存器S3C2410_TCFG0中

 

    clk_p = clk_get(NULL, "pclk");                                    //得到pclk

    pclk = clk_get_rate(clk_p);

    tcnt = (pclk/50/16)/freq;                                             //得到定时器的输入时钟,进而设置PWM的调制频率

 

    __raw_writel(tcnt, S3C2410_TCNTB(0));        //PWM脉宽调制的频率等于定时器的输入时钟  

    __raw_writel(tcnt/2, S3C2410_TCMPB(0));    //占空比是50% ,自己认为是高电平时间

       

    tcon &= ~0x1f;//清空前4位

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

    __raw_writel(tcon, S3C2410_TCON);

 

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

    __raw_writel(tcon, S3C2410_TCON);               //把tcon写到计数器控制寄存器S3C2410_TCON中

}

 

 

void PWM_Stop( void )

{

    //s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_OUTP);   //设置GPB0为输出

    s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPIO_OUTPUT);   //设置GPB0为输出

    s3c2410_gpio_setpin(S3C2410_GPB(0), 0);                                       //设置GPB0为低电平,使蜂鸣器停止

}

 

 

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

{

    if (!down_trylock(&lock))           

                          //是否获得信号量,是down_trylock(&lock)=0,否则非0

       return 0;

    else

       return -EBUSY;                                    

                 //返回错误信息:请求的资源不可用

}

 

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

{

       up(&lock);                     

                                   //释放信号量lock

        return 0;

}

 

 

/*cmd 是1,表示设置频率;cmd 是2 ,表示停止pwm*/

static int s3c24xx_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{

    switch (cmd) {

       case PWM_IOCTL_SET_FREQ:        

                   //if cmd=1 即进入case PWM_IOCTL_SET_FREQ

       if (arg == 0)                     

                                    //如果设置的频率参数是0

         return -EINVAL;                   

                               //返回错误信息,表示向参数传递了无效的参数

        PWM_Set_Freq(arg);                   

                     //否则设置频率

        break;

       case PWM_IOCTL_STOP:                   

             // if cmd=2 即进入case PWM_IOCTL_STOP

        PWM_Stop();                                     

               //停止蜂鸣器

        break;

    }

   

    return 0;                                             

                //成功返回

}

 

 

/*初始化设备的文件操作的结构体*/

static struct file_operations dev_fops = {

    .owner   =   THIS_MODULE,

    .open    =   s3c24xx_pwm_open,

    .release =   s3c24xx_pwm_close,

    .ioctl   =   s3c24xx_pwm_ioctl,

};

 

 

static struct miscdevice misc = {

.minor = MISC_DYNAMIC_MINOR,

.name = DEVICE_NAME,

.fops = &dev_fops,

};

 

 

static int __init dev_init(void)

{

    int ret;

    init_MUTEX(&lock);                     //初始化一个互斥锁

    ret = misc_register(&misc);          //注册一个misc设备

    printk (DEVICE_NAME"\tinitialized\n");

    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("S3C2410/S3C2440 PWM Driver");

 

pwm_test.c

 

#include                      //标准输入输出定义

#include                   //POSIX终端控制定义

#include                      //Unix 标准函数定义

#include                       //标准函数库定义

#define PWM_IOCTL_SET_FREQ   1

#define PWM_IOCTL_STOP    2

#define ESC_KEY   0x1b               //定义ESC_KEY 为ESC按键的键值

/* int getch(void)

{         

    struct termios tm, tm_old;    

    int fd = STDIN_FILENO, c;      

    if(tcgetattr(fd, &tm) < 0)           

        return -1;       

    tm_old = tm;       

    cfmakeraw(&tm);       

    if(tcsetattr(fd, TCSANOW, &tm) < 0)       

        return -1;      

    c = fgetc(stdin);     

    if(tcsetattr(fd, TCSANOW, &tm_old) < 0)        

        return -1;      

    return c;

} */

static int getch(void)                    //定义函数在终端上获得输入,并把输入的量(int)返回

{

struct termios oldt,newt;             //终端结构体struct termios

int ch;

if (!isatty(STDIN_FILENO)) {                       //判断串口是否与标准输入相连

   fprintf(stderr, "this problem should be run at a terminal\n");           

   exit(1);

}

// save terminal setting

if(tcgetattr(STDIN_FILENO, &oldt) < 0) {        //获取终端的设置参数

   perror("save the terminal setting");

   exit(1);

}

// set terminal as need

newt = oldt;

newt.c_lflag &= ~( ICANON | ECHO );                    //控制终端编辑功能参数ICANON 表示使用标准输入模

                                                                              //式;参数ECH0表示显示输入字符

if(tcsetattr(STDIN_FILENO,TCSANOW, &newt) < 0) {   //保存新的终端参数

   perror("set terminal");

   exit(1);

}

ch = getchar();

// restore termial setting

if(tcsetattr(STDIN_FILENO,TCSANOW,&oldt) < 0) {           //恢复保存旧的终端参数

   perror("restore the termial setting");

   exit(1);

}

return ch;

}

static int fd = -1;

static void close_buzzer(void);

static void open_buzzer(void)                   //打开蜂鸣器

{

fd = open("/dev/pwm", 0);                       //打开pwm设备驱动文件

if (fd < 0) {

   perror("open pwm_buzzer device");

   exit(1);                                                  //打开错误,则终止进程。退出参数为1

}

// any function exit call will stop the buzzer

atexit(close_buzzer);                             //退出回调close_buzzer

}

static void close_buzzer(void)                       //关闭蜂鸣器

{

if (fd >= 0) {

   ioctl(fd, PWM_IOCTL_STOP);                  //停止蜂鸣器

   close(fd);                                                     //关闭设备驱动文件

   fd = -1;

}

}

static void set_buzzer_freq(int freq)

{

// this IOCTL command is the key to set frequency

int ret = ioctl(fd, PWM_IOCTL_SET_FREQ, freq);            //设置频率

if(ret < 0) {                                                                       //如果输入的频率错误

   perror("set the frequency of the buzzer");

   exit(1);                                                                  //退出,返回1

}

}

static void stop_buzzer(void)

{

int ret = ioctl(fd, PWM_IOCTL_STOP);          //关闭蜂鸣器

if(ret < 0) {                                                     //如果无法关闭蜂鸣器

   perror("stop the buzzer");

   exit(1);                                                          //退出返回1

}

}

int main(int argc, char **argv)

{

int freq = 1000 ;

 

open_buzzer();                                 //打开蜂鸣器

printf( "\nBUZZER TEST ( PWM Control )\n" );

printf( "Press +/- to increase/reduce the frequency of the BUZZER\n" ) ;

printf( "Press 'ESC' key to Exit this program\n\n" );

 

 

while( 1 )

{

   int key;

   set_buzzer_freq(freq);                           //设置蜂鸣器频率

   printf( "\tFreq = %d\n", freq );

   key = getch();//不回显的字符读入

   switch(key) {

   case '+':

    if( freq < 20000 )

     freq += 10;

    break;

   case '-':

    if( freq > 11 )

     freq -= 10 ;

    break;

   case ESC_KEY:

   case EOF:

    stop_buzzer();

    exit(0);

   default:

    break;

   }

}

}

 

 

Arm平台PWM实验:

[root@FriendlyARM /mini_driver]# ./pwm_test

 

BUZZER TEST ( PWM Control )

Press +/- to increase/reduce the frequency of the BUZZER

Press 'ESC' key to Exit this program

 

        Freq = 1000

        Freq = 990

        Freq = 980

        Freq = 970

        Freq = 960

        Freq = 950

        Freq = 960

        Freq = 970

        Freq = 980

 

[@FriendlyARM /mini_driver]#

阅读(3061) | 评论(2) | 转发(1) |
给主人留下些什么吧!~~

g_programming2012-12-02 20:59:15

kangear: 最后都这了:"[@FriendlyARM /mini_driver]#" 你没意识到出问题了吗?我就是冲这句话搜到这的我运行pwm_test之后就不能在终端输入任何东西了
[@Frien.....
我已经很久没有做ARM了,这个实验也是原来做的,原来应该没有出问题吧,不然我应该会记录下的

kangear2012-12-01 21:59:57

最后都这了:"[@FriendlyARM /mini_driver]#" 你没意识到出问题了吗?我就是冲这句话搜到这的我运行pwm_test之后就不能在终端输入任何东西了
[@FriendlyARM /]#
[@FriendlyARM /]#
[@FriendlyARM /]#
[@FriendlyARM /]#
[@FriendlyARM /]#
[@FriendlyARM /]#
[@FriendlyARM /]#
[@FriendlyARM /]#
[@FriendlyARM /]# [@FriendlyARM /]#