在嵌入式開發(fā)中,所有芯片對外設(shè)進(jìn)行處理都是通過讀寫設(shè)備上的寄存器進(jìn)行的奖恰。外設(shè)的寄存器在內(nèi)存中單獨分出一部分作為特殊功能寄存器進(jìn)行編址崭闲。在低級嵌入式設(shè)備中茬斧,我們通過直接操作外設(shè)寄存器即可控制外設(shè)的工作。在高級設(shè)備中玻粪,設(shè)備加載了操作系統(tǒng)隅津,操作系統(tǒng)中的內(nèi)存管理單元(MMU)對設(shè)備內(nèi)存進(jìn)行重新管理,從而無法直接進(jìn)行操作劲室。目前市面上伦仍,根據(jù)不同CPU體系架構(gòu),CPU對外設(shè)端口的編址方式一般有兩種:IO映射方式(IO mapped)和內(nèi)存映射方式(memory mapped)很洋。
IO映射方式充蓝,主要指外設(shè)地址空間和內(nèi)存地址空間是獨立開的,根據(jù)不同的訪問指令進(jìn)行對應(yīng)操作喉磁。典型的如x86處理器的設(shè)備谓苟。
內(nèi)存映射方式,主要指內(nèi)存地址空間和外設(shè)地址空間的訪問時一樣的协怒。只是訪問的地址不同涝焙。典型的如RISC指令系統(tǒng)的嵌入式設(shè)備CPU(如ARM、PowerPC等)斤讥。
通常情況纱皆,外設(shè)的IO內(nèi)存資源的物理地址在硬件設(shè)計的時候已經(jīng)確定,并且該物理地址是已知的芭商。但是操作系統(tǒng)通過內(nèi)存管理單元(MMU)管理系統(tǒng)的虛擬地址到物理地址的訪問派草。在設(shè)備運(yùn)行時,對于驅(qū)動開發(fā)人員來說铛楣,可以被直接用于編程的是虛擬地址近迁,開發(fā)人員需要使用內(nèi)存映射機(jī)制將物理地址映射到虛擬地址,從而通過虛擬地址訪問外設(shè)IO簸州。
Linux內(nèi)核源碼中io.h文件聲明了函數(shù)ioremap()和iounmap()鉴竭,該函數(shù)主要用于將IO內(nèi)存資源的物理地址到虛擬地址的映射和解除映射。
// ioremap宏定義在asm/io.h內(nèi):
#define? ioremap(cookie,size)? ? __ioremap(cookie,size,0)
// __ioremap函數(shù)原型為(arm/mm/ioremap.c):
void__iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);
參數(shù):
phys_addr:映射的起始IO地址
size:映射的空間大小
flags:映射的IO空間和權(quán)限有關(guān)的標(biāo)志
返回值:映射后的內(nèi)核虛擬地址(3G-4G)
iounmap函數(shù)用于解除ioremap()所做的映射岸浑,原型如下:
void iounmap(void * addr);
? ? 在將I/O外設(shè)的寄存器物理地址映射成虛擬地址后搏存,我們就可以象讀寫內(nèi)存那樣直接對I/O內(nèi)存資源進(jìn)行讀寫了。Linux內(nèi)核為了保證驅(qū)動程序的跨平臺的可移植性矢洲,提供了特定的讀寫函數(shù)來訪問I/O內(nèi)存資源璧眠。
讀寫I/O的函數(shù)如下所示:
// IO內(nèi)存讀取函數(shù),參數(shù)p為讀取的虛擬地址。分別可以讀取8位责静、16位和32位
#define ioread8(p)? ? ? ({ unsigned int __v = __raw_readb(p); __iormb(); __v; })
#define ioread16(p)? ? ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __iormb(); __v; })
#define ioread32(p)? ? ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __iormb(); __v; })
// IO內(nèi)存寫入函數(shù)袁滥,參數(shù)p為寫入的虛擬地址,v為寫入值灾螃。分別可以寫入8位题翻、16位和32位
#define iowrite8(v,p)? ({ __iowmb(); __raw_writeb(v, p); })
#define iowrite16(v,p)? ({ __iowmb(); __raw_writew((__force __u16)cpu_to_le16(v), p); })
#define iowrite32(v,p)? ({ __iowmb(); __raw_writel((__force __u32)cpu_to_le32(v), p); })
以下示例程序以tiny4412開發(fā)板為例,對開發(fā)板LED燈進(jìn)行IO控制腰鬼。
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <asm/io.h>
MODULE_LICENSE("GPL");
//? 內(nèi)核IO映射后的虛擬地址
unsigned int virt ;
int test_init()
{
// 映射的物理地址范圍:0x11000 000? - 0x11000 fff
virt = ioremap( 0x11000000, 4096 );?
// 通過指針讀寫內(nèi)存
// unsigned int *gpm4con = (unsigned int *)(virt + 0x02e0);
// unsigned int *gpm4dat = (unsigned int *)(virt + 0x02e4);
// *gpm4con = 0x1111;
// *gpm4dat &= ~0xf;
// *gpm4dat |= 0xf;
// 通過IO函數(shù)操作
iowrite32( 0x1111, virt + 0x02e0 );
unsigned int val = ioread32( virt + 0x02e4 );
iowrite32( val | 0xf , virt +0x02e4 );
return 0;
}
void test_exit()
{
iounmap( virt );
}
module_init( test_init );
module_exit( test_exit );
文章來源:學(xué)到牛牛 www.xuedaon.com