s3c2440簡單驅動編程-led

(最近開始學習Linux驅動編程,爲了方便總結就把信息簡單的歸納下來了。由於初學,技術很欠缺,歡迎糾正錯誤的地方)

        OS      :linux

        cpu     :s3c2440

        開發板:TQ2440

先製作一個驅動的基本構架: 

    #define MAYOR 0
    #define DEV_NAME "myled"

    static int DevNum = 0;

    static int leds_open (struct inode *inode, struct file *file);
    {}

    static int leds_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
    {}

    static struct file_operations led_fop= {
     .owner = THIS_MODULE;
     .open = leds_open;
     .ioctrl = leds_ioctl;
    };

    static int __init s3c2440_leds_init(void)
    {
     int ret;
     DevNum = register_chrdev( MAYOR, DEV_NAME, &led_fop);
     if( DevNum<0 )
     {
      printk(DEV_NAME" can't register major number\n");
      return DevNum;
     }
     printk(DEV_NAME" initialzed\n");
     return 0;
    }

    static int __exit s3c2440_leds_exit(void)
    {
     if( DevNum>0)
     {
      unregister_chrdev( DevNum, DEV_NAME);
     }
    }

    module_init(s3c2440_leds_init);
    module_exit(s3c2440_leds_exit);

找到外接硬件的對應管教:

        現在開始根據手冊查找開發板信息(這一步是開發板級信息),由於目標是控制led,故先去“TQ2440的底板原理圖”查找管教信息。根據led的電路連接,查找到其對應的cpu管教爲GPB5~8.意味着  你只需要控制這幾個管教的電平,就可以控制led燈的開關(不考慮負載問題)。

配置管教:

        每個管腳有多重工作狀態,現在對其進行配置。管腳的配置信息來源於芯片級信息,所以與開發板無關。查找芯片手冊,發現這款芯片的管腳有如下幾種配置寄存器

        GPBCON --In S3C2440A, most of the pins are multiplexed pins. So, It is determined which function is selected for each pins.The PnCON(port control register) determines

                           which function is used for each pin。用來配置管腳工作狀態

        GPBDAT --If Ports are configured as output ports, data can be written to the corresponding bit of PnDAT. If Ports are configured as input ports, the data can be read from the

                          corresponding bit of PnDAT.用來收發數據

        GPBUP --The port pull-up register controls the pull-up resister enable/disable of each port group. When the corresponding bit is 0, the pull-up resister of the pin is enabled.

                        When 1, the pull-up resister is disabled.控制上拉電阻使能

        EXTINT--The 24 external interrupts are requested by various signaling methods. The EXTINT register configures the signaling method among the low level trigger, high level

                       trigger, falling edge trigger, rising edge trigger, and both edge trigger for the external interrupt request.中斷控制

    根據資料只需要將GPB5~8腳配置成輸出管腳(配置GPBCON,地址爲0x56000010,這是CPU設計時爲內部寄存器分配的地址)並向數據寄存器(GPBDAT,0x56000014)

    寫數據。通過s3c2440的芯片手冊知道,需要將GPB[17-10]位設置爲(01010101)B.實際操作時的屏蔽碼爲(~0x3fc00),開始填充leds_open函數。

填充leds_open函數:

    static int leds_open (struct inode *inode, struct file *file);
    {
            s3c2410_gpio_cfgpin(unsigned int pin,unsigned int function);
            GCon_V = ioremap( GPBCON_P, 4);
            if( GCon_V==NULL)
            {
                    printk(DEV_NAME" fail to ioremap GPBCON_P\n");
                    goto leds_open_err_GCon_v;
            }
            GDat_V = ioremap( GPBDAT_P, 4);
            if( GDat_V==NULL)
            {
                    printk(DEV_NAME" fail to ioremap GPBDAT_P\n");
                    goto leds_open_err_GDat_v;
            }
            unsigned int data =  ioread32(GCon_V);    //讀取原有管腳數據
            data &= (~0x3fc00);
            data |= (0x15400);
            iowrite32( data, GCon_V);    //寫入新的數據
            return 0;

            leds_open_err_GDat_v:
            iounmap(GCon_V);
            leds_open_err_GCon_v:
            return -1;
    }

填充leds_ioctl函數:

        static int inline _leds_on(int ledNum)

       {

               unsigned int data = ioread32( GDat_V);

               data &= (~(0x20<<ledNum));

               iowrite32( 0, GDat_V);

               return 0;

       }

       static int inline _leds_off(int ledNum)

       {

               unsigned int data = ioread32( GDat_V);

               data |= (0x20<<ledNum);

               iowrite32( data, GDat_V);

               return 0;

       }

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

       {

               if( arg>4 || arg<0)

               {

                       return -EINVAL;

               }

               switch(cmd)

               {

                       case CMD_LED_ON:

                               _leds_on( arg);

                               break;

                       case CMD_LED_OFF:

                               _leds_off( arg);

                               break;

                       default:
                               printk("error\n");

                               return -EINVAL;

               }

               return 0;

       }

將驅動編譯成模塊:

      將驅動編譯成模塊式需要系統的源碼作爲編譯背景,參考另一篇關於驅動Makefile的編寫。

建立設備文件:

        由於沒有驅動沒有自動創建設備,所以手動創建,去"cat /proc/devices"找到自動分配的myled的設備號爲252

        mknod /etc/myled c 252 0

        (有自動創建的方法,暫時不用)

製作測試程序:

        int main()
       {
           int fd;
           fd = open("/dev/myled", 0);
           if( fd<0 )
           {
               perror("error");
               return 0;
           }

           int flag = 0;
           while(1)
           {
               if(flag==0)
               {
                   printf("led on\n");
                   ioctl(fd, CMD_LED_ON, ARG_LED1);
                   flag =1;
               }
               else
               {
                   printf("led off\n");
                   ioctl(fd, CMD_LED_OFF, ARG_LED1);
                   flag = 0;
               }
               sleep(1);
           }
       }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章