日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

嵌入式Linux LED,键盘,AD驱动程序开发

發布時間:2023/12/15 linux 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 嵌入式Linux LED,键盘,AD驱动程序开发 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

????????????? LED,鍵盤,AD驅動程序開發

原文: http://blog.sina.com.cn/s/blog_4083b2d70100bnlf.html

一:硬件平臺及系統平臺

CPU: S3C2410A

SDRAM: 64M

FLASH: K9F1208(64MB)

NET: CS8900

友善之臂2410,優龍2410.參考原理圖,編寫相應的程序.原理是一樣的.只要改下GPIO端口及相應的地址就可以了.后面涉及到的程序是針對優龍2410版子的程序.

二:使用的內核版本:LINUX2.6.24?

交叉編譯工具版本:arm-linux-gcc 3.4.1

?

三:實驗步驟

?

1.?拷貝一個移植好的內核(可以參考陳亮的內核移植筆記或網上的步驟,自己動手做一個內核),放到虛擬機的根目錄下.

2.? 解壓:zxvf linux-2.6.24.tar.gz大概需要2分鐘的時間.

3.? 編譯一次: make?(即:[root@229 linux-2.6.24]# make)然后配置:makemenuconfig??

(即:[root@229 linux-2.6.24]# make menuconfig)

一般在內核移植的時候我們已經加上鍵盤,鼠標的支持了,如果沒有的話,在makemenuconfig??的時候可以自己再加上,以后我們需要什么新的驅動,也可以在這個時候加上對該驅動的支持.對鍵盤,鼠標的支持

?

到此為止編譯驅動的準備工作做好了 ,否則編譯驅動會出現很多錯誤.

?

四:編寫驅動程序.

1.? 驅動程序結構:

?

?

五:實際程序如下:

?

1????????LED 驅動程序

?

#include<linux/module.h>#include<linux/kernel.h>#include<linux/fs.h>#include<linux/errno.h>#include<linux/types.h>#include<linux/fcntl.h>#include<linux/cdev.h>#include<linux/version.h>#include<linux/vmalloc.h>#include<linux/ctype.h>#include<linux/pagemap.h>#include<asm/io.h>#include<asm/irq.h>#include<asm/signal.h>#include<asm/hardware.h>#include<asm/uaccess.h>#include<asm/arch/regs-gpio.h>#include<linux/ioctl.h>#include "demo.h"MODULE_AUTHOR("fgj");MODULE_LICENSE("Dual BSD/GPL");#define LED_SI_OUT__raw_writel(0x5500,S3C2410_GPFCON)#defineLED_SI_H1 __raw_writel(0x80,S3C2410_GPFDAT)#defineLED_SI_L1 __raw_writel(~0x80,S3C2410_GPFDAT)#defineLED_SI_H2 __raw_writel(__raw_readl(S3C2410_GPFDAT)|(1<<6),S3C2410_GPFDAT)#defineLED_SI_L2 __raw_writel(__raw_readl(S3C2410_GPFDAT)&(~(1<<6)),S3C2410_GPFDAT)#defineLED_SI_H3 __raw_writel(__raw_readl(S3C2410_GPFDAT)|(1<<5),S3C2410_GPFDAT)#defineLED_SI_L3 __raw_writel(__raw_readl(S3C2410_GPFDAT)&(~(1<<5)),S3C2410_GPFDAT)#defineLED_SI_H4 __raw_writel(__raw_readl(S3C2410_GPFDAT)|(1<<4),S3C2410_GFBDAT)#defineLED_SI_L4 __raw_writel(__raw_readl(S3C2410_GPFDAT)&(~(1<<4)),S3C2410_GPFDAT)#define LED_OFF __raw_writel(__raw_readl(S3C2410_GPFDAT)|(15<<4),S3C2410_GPFDAT) #define COMMAND_LEDOFF 0#define COMMAND_LEDON 1struct DEMO_dev *DEMO_devices;static unsigned char demo_inc=0;int DEMO_open(struct inode *inode, struct file*filp){structDEMO_dev *dev;if(demo_inc>0)return -ERESTARTSYS;demo_inc++;dev =container_of(inode->i_cdev, struct DEMO_dev,cdev);filp->private_data = dev;return0;}int DEMO_release(struct inode *inode, struct file*filp){demo_inc--;return0;}int DEMO_ioctl(struct inode *inode, struct file*filp,unsigned int line_num, unsigned long arg){switch(line_num){case 7:{ LED_OFF;LED_SI_L1;printk("ioctl1\n");return0; }case 8:{LED_OFF;LED_SI_L2;printk("ioctl2\n");return0; }case 9:{LED_OFF;LED_SI_L3;printk("ioctl3\n");return0; }case 10:{LED_OFF;LED_SI_L4;printk("ioctl4\n");return0; }default:{printk("ioctl error successfully\n");return -EFAULT;}} }struct file_operations DEMO_fops = {.owner= THIS_MODULE,.ioctl= DEMO_ioctl,.open= DEMO_open,.release= DEMO_release,};void DEMO_cleanup_module(void){dev_t devno= MKDEV(DEMO_MAJOR, DEMO_MINOR);if(DEMO_devices){cdev_del(&DEMO_devices->cdev);kfree(DEMO_devices);}unregister_chrdev_region(devno,1);}int DEMO_init_module(void){intresult;dev_t dev =0;dev =MKDEV(DEMO_MAJOR, DEMO_MINOR);result =register_chrdev_region(dev, 1, "DEMO");if (result< 0){printk(KERN_WARNING "DEMO: can't get major %d\n", DEMO_MAJOR);return result;}DEMO_devices= kmalloc(sizeof(struct DEMO_dev), GFP_KERNEL);if(!DEMO_devices){result = -ENOMEM;goto fail;}memset(DEMO_devices, 0, sizeof(struct DEMO_dev));cdev_init(&DEMO_devices->cdev,&DEMO_fops);DEMO_devices->cdev.owner = THIS_MODULE;DEMO_devices->cdev.ops =&DEMO_fops;result =cdev_add (&DEMO_devices->cdev, dev,1);if(result){printk(KERN_NOTICE "Error %d adding DEMO\n", result);goto fail;}LED_SI_OUT;return0;fail:DEMO_cleanup_module();returnresult;}module_init(DEMO_init_module);module_exit(DEMO_cleanup_module);
?

使用說明:我們對內存每個地址的訪問的時候不能直接用writel(__raw_readl(S3C2410_GPFDAT)|(1<<6),S3C2410_GPFDAT)

或者#define S3C2410_ADCCON?0x58000000 等

因為我們對內存的訪問的時候要用虛擬內存映射技術所以要使用如下的程序:

?

__raw_writel(__raw_readl(S3C2410_GPFDAT)|(1<<6),S3C2410_GPFDAT)

?

#define S3C2410_ADCCON (0x58000000)

adccon = ioremap(S3C2410_ADCCON,0x00000004);

0x00000004 是指字節長度.


網上有很多程序是關于linux2.4內核的,與2.6內核的驅動程序寫法不一樣.編譯時有時候通不過,比如當編譯報錯沒有定義:SA_INTERRUPT時候

我們可以使用命令:grep??-R??-n??'SA_INTERRUPT'??/linux-2.6.24/include

因此可以在程序中加上該語句:# define SA_INTERRUPT 0x20000000.

?

?編寫Makefile

?

#linux derive develop#makefileCC= /usr/local/arm/3.4.1/bin/arm-linux-gccifneq ($(KERNELRELEASE),)obj-m :=led.oelseKDIR :=/linux-2.6.24PWD :=$(shell pwd)default:$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modulesendifclean:@rm -rfled_test*.o

編寫不同的驅動程序的時候 Makefile基本不用改變 只需要把該句obj-m :=led.o進行修改就可以了.最后一句是對make clean的支持 清除生成的不必要的文件.

板子上運行結果如下:

?

?

2??????鍵盤驅動程序(頭文件略)

參考資料:《嵌入式Linux驅動程序設計從入門到精通》 馮國進編著,2008.

MODULE_AUTHOR("fgj");MODULE_LICENSE("Dual BSD/GPL");static int irqArray[4]={IRQ_EINT0,IRQ_EINT2,IRQ_EINT11,IRQ_EINT19};void initButton(void){writel((readl(S3C2410_GPGCON)&(~((3<<22)|(3<<6))))|((2<<22)|(2<<6)),S3C2410_GPGCON); //GPG11,3 set EINTwritel((readl(S3C2410_GPFCON)&(~((3<<4)|(3<<0))))|((2<<4)|(2<<0)),S3C2410_GPFCON); //GPF2,0 set EINTwritel((readl(S3C2410_EXTINT0)&(~(6|(6<<8)))),S3C2410_EXTINT0);writel((readl(S3C2410_EXTINT0)|(0|(0<<8))),S3C2410_EXTINT0); //set eint0,2 falling edge intwritel((readl(S3C2410_EXTINT1)&(~(6<<12))),S3C2410_EXTINT1); }void polling_handler(unsigned long data){intcode=-1;writel(readl(S3C2410_SRCPND)&0xffffffda,S3C2410_SRCPND);//clearsrcpnd 0 2 11 19mdelay(1);//掃描按鍵表,根據中斷號,找出所按下的按鍵。writel(readl(S3C2410_GPBDAT)|0x80,S3C2410_GPBDAT);//set GPB76 to10writel(readl(S3C2410_GPBDAT)&0xffffffBf,S3C2410_GPBDAT);//...// }writel(readl(S3C2410_SRCPND)&0xffffffda,S3C2410_SRCPND);//clearsrcpnd 0 2 11 19mdelay(1);writel(readl(S3C2410_GPBDAT)|0x40,S3C2410_GPBDAT);//set GPB76 to01writel(readl(S3C2410_GPBDAT)&0xffffff7f,S3C2410_GPBDAT);//...// }for (i = 0;i <4; i++) {if (request_irq(irqArray[i], &simplekey_interrupt,0x20000000, "simplekey", NULL)) {printk("request button irq failed!\n");return -1;} }

硬件接口電路如下:?


友善之臂用到的GPIO(EINT0…等)端口不一樣,根據硬件接口電路,參照數據手冊,在涉及到的端口上,相應的修改下程序就可以了.此程序還可以注釋掉很多沒必要的部分.涉及到set GPB76 to 01的都可以注釋掉,因為該程序用GPB7GPB6來回置低的辦法,而我們直接讓,鍵盤接地

我們只用到4個鍵盤而,程序用到8個鍵盤,我們也沒必要判斷是哪一列鍵盤按下.修改應該簡單,不過還沒編譯驗證過.所以程序還可以精簡很多.但該程序完全可以實現我們要的功能,只是多做了一些無用功而已.

原始程序有部分錯誤,如:設置下降沿觸發的時候應該用:

writel((readl(S3C2410_EXTINT1)&(~(6<<12))),S3C2410_EXTINT1);//set eint11 falling

writel((readl(S3C2410_EXTINT1)|(0<<12)),S3C2410_EXTINT1);該語句貌似沒用,應該可以注釋掉.可以編譯試試.

?

板子上運行結果如下:

?

?

?

3???????AD轉換程序:(因為文字過長,有刪節)

?

?

?# //預分頻倍數

#define PRESCALER 19

#define S3C2410_ADCCON (0x58000000)

#define S3C2410_ADCTSC (0x58000004)

??

static ssize_t adc_read(struct file *filp, char*buf, size_t count, loff_t *f_pos)

{

???;

??? }

??? //temp1 =(int)filp->private_data;

???local_irq_save(flags);

?

writel((readl(S3C2410_CLKCON) |S3C2410_CLKCON_ADC),S3C2410_CLKCON); //時鐘使能

???//outl(AdcrSave | (1u <_u32 ?temp) | (1u<< 24), S3C2410_ADCCON);

???

printk("temp0=%d\n",temp0);

??? //開始AD轉換

???writel(temp0|0x01, adccon);

???//等待AD轉換完成

???while((readl(adccon) & 0x01) != 0);

???//等待AD數據寫入ADCDAT0

//for(j=0;j<50;j++)

//while((readl(adccon) & (0x1< _u60 ? 15)) == 0);

//mdelay(100);

???printk("adccon = %d, adctsc = %d, adcdly = %d, adcdat0 = %d,adcdat1= %d, adcupdn = %d\n", readl(adccon), readl(adctsc),readl(adcdly), readl(adcdat0), readl(adcdat1), readl(adcupdn));

??? // }

??? temp =readl(adcdat0);

temp2 = temp&0x3ff;

??

);

???

printk("adc device installed, with major %d\n",ADC_MAJOR_NR);

adccon = ioremap(S3C2410_ADCCON,0x00000004);

adctsc = ioremap(S3C2410_ADCTSC,0x00000004);

?

void adc_cleanup(void)

{

iounmap(adccon);

iounmap(adctsc);

iounmap(adcdly);

iounmap(adcupdn);

???unregister_chrdev(ADC_MAJOR_NR, DEVICE_NAME);

}

這個程序我們花的時間最長:參照網上的程序,然后調試,至少花了一個星期的時間.

開始主要是有2點沒弄清楚:

??? * 1.?????內存映射機制.

開始我們直接#define adccon?(0x58000000)

結果編譯的時候就通不過,打印出不能操作內存地址的錯誤.后來我們知道了是沒有利用,內存映射機制.改用如上ioremap(adccon);方式就ok了

?? * 2.?????開啟時間使能

修改了第一部之后,能編譯成.ko文件了,運行結果出現了錯誤.根本沒有進行轉換,不管輸入什么,輸出都為0.

writel((readl(S3C2410_CLKCON) |S3C2410_CLKCON_ADC),S3C2410_CLKCON); //時鐘使能

加上該語句后,AD轉換器才真正的工作.實際運行結果如下:

?

?

?

Temp2 (即adc)為轉換結果.

?

驅動程序和QT結合是一個很好的方向,我們的下一步,可以在QT上實現該驅動程序.

?

查看設備號碼方法:cat /proc/devices

?

總結

以上是生活随笔為你收集整理的嵌入式Linux LED,键盘,AD驱动程序开发的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。