一、重要知識點
1.I/O端口和I/O內存
寄存器和常規內存的區別:寄存器和RAM主要不同在于寄存器有邊際效果,讀取某個地址時可能導致該地址的內容發生變化,比如說很多設備的中斷狀態寄存器只要一讀取,便自動清0。所以硬件寄存器不能直接訪問,而要通過I/O端口和I/O內存兩種方式訪問。
在硬件層,I/O內存區域和I/O端口區域沒有概念上的區別:它們都是通過向地址總線和控制總線發生電平信號進行訪問,再通過數據總線讀寫數據。
a.I/O端口:
一些CPU制造廠在它們的芯片中使用單一的地址空間,而一些則為外設保留獨立的地址空間,以便和內存區間分開來,這段獨立與內存地址空間的地址空間就叫I/O端口。在/proc/ioport中可以看到。嵌入式處理器大部分不支持I/O端口。
訪問I/O端口有兩步:1.申請I/O端口2.讀寫I/O端口
申請I/O端口:
structresource *request_region(unsigned long first, unsigned long n, const char *name)
申請從first開始的n個端口。參數name為設備名稱。如果分配成功則返回非NULL值。
釋放I/O端口:
voidrelease_region(unsigned long start, unsigned long n)
讀寫I/O端口:
讀寫一個字節
unsignedinb(unsigned port)
voidoutb(usigned char byte, unsigned port)
讀寫二個字節
unsignedinb(unsigned port)
voidoutb(usigned short byte, unsigned port)
讀寫四個字節
unsignedinb(unsigned port)
voidoutb(usigned long byte, unsigned port)
b.I/O內存
通過將外設寄存器映射到內存空間來進行訪問叫做I/O內存,嵌入式大多只支持這種操作。
訪問I/O內存有三步:1.申請I/O內存區域2.映射I/O內存區域3.讀寫I/O內存
申請I/O內存區域
structresource *request_mem_region(unsigned long start, unsigned long len, char *name)
申請訪問從start(I/O物理地址)開始的len長度的I/O內存區域,如成功則返回非NULL值。在/proc/iomem中可可以查看到已經被申請的I/O內存區域。在后面的我寫的驅動程序中并沒用使用申請這一步,是因為我使用的GPIO內存區域已經被申請,如果在申請會導致失敗。但是這樣做法是不安全的做法,因為同一I/O內存區域域被多個模塊使用。
釋放I/O內存區域
voidrelease_mem_region(unsigned long start, unsigned long len)
映射I/O內存區域
void*ioremap(unsigned long phy_addr, unsigned long size)
映射從物理地址phy_addr開始的size長度的的地址空間。返回可以訪問I/O內存地址。由ioreamp返回的地址不應該直接引用,必須通過下面一些列的讀寫函數完成。
讀寫操作I/O內存
讀1、2、4個字節:
unsignedint read8(void *addr);
unsignedint read16(void *addr)
unsignedint read32(void *addr)
寫讀1、2、4個字節:
voidiowrite8(u8 value, void *addr)
voidiowrite16(u8 value, void *addr)
voidiowrite32(u8 value, void *addr)
2.混雜設備驅動
在Linux系統中,存在一類字符設備,他們共享一個主設備號(10),但此設備號不同,我們稱這類設備為混雜設備(miscdeivce),查看/proc/device中可以看到一個名為misc的主設備號為10。所有的混雜設備形成一個鏈表,對設備訪問時內存根據次設備號找到對應的miscdevice設備。
Linux內核使用structmiscdeivce來描述一個混雜設備
structmiscdevice{
???????? int minor;
conststruct file_opreations *fops;
structlist_head list;
structdevice *parent;
structdevice *this_device;?????
}
使用時只需填寫minor次設備號,*name設備名,*fops文件操作函數集即可。
Linux內核使用misc_register函數注冊一個混雜設備。注冊成功后,linux內核為自動為該設備創建設備文件。
intmisc_register(struct miscdevice *misc)
二、驅動代碼
1.驅動代碼一
這段LED驅動代碼采用手動I/O內存映射的方式訪問。沒有使用申請內存區域函數,這樣使不安全的。直接訪問I/O內存地址而不是通過讀寫函數訪問也是不安全。同時驅動代碼包含硬件相關代碼也是移植性不好的。寫這段代碼是為了幫助理解I/O內存映射的過程。
#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?
#include?
#include?
volatile?unsigned?int?long?*gpb_con?=?NULL;??
volatile?unsigned?int?long?*gpb_data?=?NULL;??
static?int?leds_ioctl(struct?inode?*inode,?struct?file?*file,??
unsigned?int?cmd,?unsigned?long?arg)??
{??
if((cmd>1)?|(arg>3))??
return-EINVAL;??
switch(cmd)??
{??
case?0:??
*gpb_data&=?~(1<
break;??
case?1:??
*gpb_data|=?(1<
break;??
default:??
return-EINVAL;??
}??
return?0;??
}??
static?const?struct?file_operations?leds_fops?=?{??
.owner?=?THIS_MODULE,??
.ioctl?=?leds_ioctl,??
};??
static?struct?miscdevice?misc?=?{??
.minor?=MISC_DYNAMIC_MINOR,??
.name?="my_leds",??
.fops?=&leds_fops,??
};??
static?int?__init?leds_init(void)??
{??
int?ret;??
//注冊混雜設備??
ret?=misc_register(&misc);??
//映射I/O內存??
gpb_con?=?(volatileunsigned?long?*)ioremap(0x56000010,?16);?//0x56000010為GPIOB控制寄存器的物理地址??
gpb_data?=?gpb_con+1;??
//配置LED對應的GPIOB?5、6、7、8口為輸出并初始化為1,LED滅??
*gpb_con?|=(1<<5*2)|(1<<6*2)|(1<<7*2)|(1<<8*2);??
*gpb_data?|=(1<<5)?|?(1<<6)?|?(1<<7)?|?(1<<8);??
printk("ledsinit.\n");??
return?ret;??
}??
static?void?leds_exit(void)??
{??
misc_deregister(&misc);??????????
printk("leds_exit\n");??
}??
module_init(leds_init);??
module_exit(leds_exit);??
MODULE_AUTHOR("Y-Kee");??
MODULE_LICENSE("GPL");??
#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 #include #include volatile unsigned int long *gpb_con = NULL;volatile unsigned int long *gpb_data = NULL;static int leds_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg){if((cmd>1) |(arg>3))return-EINVAL;switch(cmd){case 0:*gpb_data&= ~(1<
2.驅動代碼二:
采用內核定義好的GPIO接口(S3C2410_GPB5和S3C2410_GPB5_OUTP)和GPIO操作函數(s3c2410_gpio_setpin和s3c2410_gpio_cfgpin)。可移植性好,也是正確的做法。內核的GPIO操作函數也是通過一些的運算將GPIO接口換算成虛擬內存地址然后進行訪問的。
#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?
static?unsigned?long?led_table[]?=???{??
S3C2410_GPB5,??
S3C2410_GPB6,??
S3C2410_GPB7,??
S3C2410_GPB8,??
};??
static?unsigned?long?led_cfg_table[]?=????{??
S3C2410_GPB5_OUTP,??
S3C2410_GPB6_OUTP,??
S3C2410_GPB7_OUTP,??
S3C2410_GPB8_OUTP,??
};??
static?int?leds_ioctl(struct?inode?*inode,?struct?file?*file,??
unsigned?int?cmd,?unsigned?long?arg)??
{??
if((cmd>1)?|(arg>3))??
return-EINVAL;??
switch(cmd)??
{??
case?0:??
s3c2410_gpio_setpin(led_table[arg],0);??
break;??
case?1:??
s3c2410_gpio_setpin(led_table[arg],1);??
break;??
default:??
return-EINVAL;??
}??
return?0;??
}??
static?const?struct?file_operations?leds_fops?=?{??
.owner?=?THIS_MODULE,??
.ioctl?=?leds_ioctl,??
};??
static?struct?miscdevice?misc?=?{??
.minor?=MISC_DYNAMIC_MINOR,??
.name?="my_leds",??
.fops?=&leds_fops,??
};??
static?int?__init?leds_init(void)??
{??
int?ret,?i;??
//注冊混雜設備??
ret?=misc_register(&misc);??
//配置LED對應的GPIOB?5、6、7、8口為輸出并初始化為1,LED滅??
for(i=0;?i<4;?i++)??
{??
s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);??
s3c2410_gpio_setpin(led_table[i],1);??
}??
printk("ledsinit.\n");??
return?ret;??
}??
static?void?leds_exit(void)??
{??
misc_deregister(&misc);??
printk("leds_exit\n");??
}??
module_init(leds_init);??
module_exit(leds_exit);??
MODULE_AUTHOR("Y-Kee");??
MODULE_LICENSE("GPL");??
#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 static unsigned long led_table[] = {S3C2410_GPB5,S3C2410_GPB6,S3C2410_GPB7,S3C2410_GPB8,};static unsigned long led_cfg_table[] = {S3C2410_GPB5_OUTP,S3C2410_GPB6_OUTP,S3C2410_GPB7_OUTP,S3C2410_GPB8_OUTP,};static int leds_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg){if((cmd>1) |(arg>3))return-EINVAL;switch(cmd){case 0:s3c2410_gpio_setpin(led_table[arg],0);break;case 1:s3c2410_gpio_setpin(led_table[arg],1);break;default:return-EINVAL;}return 0;}static const struct file_operations leds_fops = {.owner = THIS_MODULE,.ioctl = leds_ioctl,};static struct miscdevice misc = {.minor =MISC_DYNAMIC_MINOR,.name ="my_leds",.fops =&leds_fops,};static int __init leds_init(void){int ret, i;//注冊混雜設備ret =misc_register(&misc);//配置LED對應的GPIOB 5、6、7、8口為輸出并初始化為1,LED滅for(i=0; i<4; i++){s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);s3c2410_gpio_setpin(led_table[i],1);}printk("ledsinit.\n");return ret;}static void leds_exit(void){misc_deregister(&misc);printk("leds_exit\n");}module_init(leds_init);module_exit(leds_exit);MODULE_AUTHOR("Y-Kee");MODULE_LICENSE("GPL");
?
評論
查看更多