十年網(wǎng)站開(kāi)發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無(wú)憂售后,網(wǎng)站問(wèn)題一站解決
arm裸機(jī)下讀寫寄存器很容易,各個(gè)寄存器和內(nèi)存的地址是單一地址空間,他們是用相同的指令進(jìn)行讀寫操作的.而在linux下就要復(fù)雜很多,因?yàn)閘inux支持多個(gè)體系架構(gòu)的CPU。比如arm和x86就不一樣,具體的差別我暫時(shí)也說(shuō)不上來(lái),這個(gè)涉及到CPU體系的設(shè)計(jì)。目前我只關(guān)心:linux為了支持多個(gè)硬件體系,在IO訪問(wèn)上做了自己的接口??梢酝ㄟ^(guò)IO內(nèi)存和IO端口這兩種方式進(jìn)行IO訪問(wèn)。在LED的例子上給出這兩種方式的具體實(shí)現(xiàn):
10多年建站經(jīng)驗(yàn), 成都網(wǎng)站建設(shè)、做網(wǎng)站客戶的見(jiàn)證與正確選擇。創(chuàng)新互聯(lián)公司提供完善的營(yíng)銷型網(wǎng)頁(yè)建站明細(xì)報(bào)價(jià)表。后期開(kāi)發(fā)更加便捷高效,我們致力于追求更美、更快、更規(guī)范。
1.利用IO Port的方式:
[cpp] view plain copy
#include linux/module.h
#include linux/moduleparam.h
#include linux/init.h
#include linux/kernel.h /* printk() */
#include linux/slab.h /* kmalloc() */
#include linux/fs.h /* everything... */
#include linux/errno.h /* error codes */
#include linux/types.h /* size_t */
#include linux/proc_fs.h
#include linux/fcntl.h /* O_ACCMODE */
#include linux/seq_file.h
#include linux/cdev.h
#include linux/ioport.h
#include mach/regs-gpio.h
#include asm/system.h /* cli(), *_flags */
#include asm/uaccess.h /* copy_*_user */
#include asm/io.h
#define LED_NUM 4
struct led_dev
{
struct cdev dev;
unsigned port;
unsigned long offset;
};
struct led_dev led[4];
dev_t dev = 0;
static struct resource *led_resource;
int led_open(struct inode *inode, struct file *filp)
{
struct led_dev *led; /* device information */
led = container_of(inode-i_cdev, struct led_dev, dev);
filp-private_data = led; /* for other methods */
return 0; /* success */
}
int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
return 0;
}
ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
char data;
struct led_dev *led;
u32 value;
printk(KERN_INFO "debug by baikal: led dev write\n");
led = (struct led_dev *)filp-private_data;
copy_from_user(data,buf,count);
if(data == '0')
{
printk(KERN_INFO "debug by baikal: led off\n");
value = inl((unsigned)(S3C2410_GPBDAT));
outl(value | 1led-offset,(unsigned)(S3C2410_GPBDAT));
//value = ioread32(led-base);
//iowrite32( value | 1led-offset, led-base);
}
else
{
printk(KERN_INFO "debug by baikal: led on\n");
value = inl((unsigned)(S3C2410_GPBDAT));
outl(value ~(1led-offset),(unsigned)(S3C2410_GPBDAT));
//value = ioread32(led-base);
//iowrite32( value ~(1led-offset), led-base);
}
}
struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
//.ioctl = led_ioctl,
.open = led_open,
.release = led_release,
};
static int led_init(void)
{
int result, i;
result = alloc_chrdev_region(dev, 0, LED_NUM,"LED");
if (result 0) {
printk(KERN_WARNING "LED: can't get major %d\n", MAJOR(dev));
return result;
}
led_resource = request_region(0x56000014,0x4,"led");
if(led_resource == NULL)
{
printk(KERN_ERR " Unable to register LED I/O addresses\n");
return -1;
}
for(i = 0; i LED_NUM; i++)
{
cdev_init( led[i].dev, led_fops);
//led[i].port = ioport_map(0x56000014,0x4);
//led[i].base = ioremap(0x56000014,0x4);
led[i].offset = i + 5; //leds GPB5\6\7\8
led[i].dev.owner = THIS_MODULE;
led[i].dev.ops = led_fops;
result = cdev_add(led[i].dev,MKDEV(MAJOR(dev),i),1);
if(result 0)
{
printk(KERN_ERR "LED: can't add led%d\n",i);
return result;
}
}
return 0;
}
static void led_exit(void)
{
int i;
release_region(0x56000014,0x4);
for( i = 0; i LED_NUM; i++)
{
//iounmap(led[i].base);
cdev_del(led[i].dev);
}
unregister_chrdev_region(dev, LED_NUM);
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("Baikal");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple LED Driver");
2.利用IO Mem的方式:
[cpp] view plain copy
#include linux/module.h
#include linux/moduleparam.h
#include linux/init.h
#include linux/kernel.h /* printk() */
#include linux/slab.h /* kmalloc() */
#include linux/fs.h /* everything... */
#include linux/errno.h /* error codes */
#include linux/types.h /* size_t */
#include linux/proc_fs.h
#include linux/fcntl.h /* O_ACCMODE */
#include linux/seq_file.h
#include linux/cdev.h
#include linux/ioport.h
#include asm/system.h /* cli(), *_flags */
#include asm/uaccess.h /* copy_*_user */
#include asm/io.h
#define LED_NUM 4
struct led_dev
{
struct cdev dev;
void __iomem *base;
unsigned long offset;
};
struct led_dev led[4];
dev_t dev = 0;
int led_open(struct inode *inode, struct file *filp)
{
struct led_dev *led; /* device information */
led = container_of(inode-i_cdev, struct led_dev, dev);
filp-private_data = led; /* for other methods */
return 0; /* success */
}
int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
return 0;
}
ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
char data;
struct led_dev *led;
u32 value;
printk(KERN_INFO "debug by baikal: led dev write\n");
led = (struct led_dev *)filp-private_data;
copy_from_user(data,buf,count);
if(data == '0')
{
printk(KERN_INFO "debug by baikal: led off\n");
value = ioread32(led-base);
iowrite32( value | 1led-offset, led-base);
}
else
{
printk(KERN_INFO "debug by baikal: led on\n");
value = ioread32(led-base);
iowrite32( value ~(1led-offset), led-base);
}
}
struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
//.ioctl = led_ioctl,
.open = led_open,
.release = led_release,
};
static int led_init(void)
{
int result, i;
result = alloc_chrdev_region(dev, 0, LED_NUM,"LED");
if (result 0) {
printk(KERN_WARNING "LED: can't get major %d\n", MAJOR(dev));
return result;
}
for(i = 0; i LED_NUM; i++)
{
cdev_init( led[i].dev, led_fops);
request_mem_region(0x56000014,0x4,"led");
led[i].base = ioremap(0x56000014,0x4);
led[i].offset = i + 5; //leds GPB5\6\7\8
led[i].dev.owner = THIS_MODULE;
led[i].dev.ops = led_fops;
result = cdev_add(led[i].dev,MKDEV(MAJOR(dev),i),1);
if(result 0)
{
printk(KERN_ERR "LED: can't add led%d\n",i);
return result;
}
}
return 0;
}
static void led_exit(void)
{
int i;
release_mem_region(0x56000014,0x4);
for( i = 0; i LED_NUM; i++)
{
iounmap(led[i].base);
cdev_del(led[i].dev);
}
unregister_chrdev_region(dev, LED_NUM);
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("Baikal");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple LED Driver");
這一問(wèn)題來(lái)自項(xiàng)目中一個(gè)實(shí)際的需求:
我需要在Linux啟動(dòng)之后,確認(rèn)我指定的芯片寄存器是否與我在uboot的配置一致。
舉個(gè)例子:
寄存器地址:0x20000010負(fù)責(zé)對(duì)DDR2的時(shí)序配置,該寄存器是在uboot中設(shè)置,現(xiàn)在我想在Linux運(yùn)行后,讀出改寄存器的值,再來(lái)檢查該寄存器是否與uboot的配置一致。
Linux應(yīng)用程序運(yùn)行的是虛擬空間,有沒(méi)有什么機(jī)制可以是完成我提到的這一需求。若行,還請(qǐng)附些測(cè)試代碼。
謝謝!
這個(gè)需要用mmap()函數(shù)將寄存器物理地址映射為用戶空間的虛擬地址,即將寄存器的那段內(nèi)存映射到用戶空間,函數(shù)介紹如下:
void*
mmap(void
*
addr,
size_t
len,
int
prot,
int
flags,
int
fd,
off_t
offset);
該函數(shù)映射文件描述符
fd
指定文件的
[offset,
offset
+
len]
物理內(nèi)存區(qū)至調(diào)用進(jìn)程的
[addr,
addr
+
len]
的用戶空間虛擬內(nèi)存區(qū),通常用于內(nèi)存共享或者用戶空間程序控制硬件設(shè)備,函數(shù)的返回值為最后文件映射到用戶空間的地址,進(jìn)程可直接操作該地址。下面是測(cè)試代碼(僅供參考):
#define
DDR2_REG_BASE
(0x20000000)
#define
MAP_SIZE
4096UL
#define
MAP_MASK
(MAP_SIZE
-
1)
static
unsigned
int
pTestRegBase;
static
int
dev_fd;
dev_fd
=
open("/dev/mem",
O_RDWR
|
O_NDELAY);
if
(dev_fd
/SPAN
0)
{
LOGE("open(/dev/mem)
failed.");
return;
}
pTestRegBase
=
(void
*)mmap(NULL,
MAP_SIZE,
PROT_READ
|
PROT_WRITE,
MAP_SHARED,
dev_fd,DDR2_REG_BASE
~MAP_MASK);
if
(MAP_FAILED
==
pTestRegBase)
{
printf("mmap
failed.
fd(%d),
addr(0x%x),
size(%d)\n",
dev_fd,
DDR2_REG_BASE,
MAP_SIZE);
}
else
{
unsigned
int
reg_value
=
*((volatile
unsigned
int
*)(pTestRegBase
+
10));
printf("reg_value
=
0xx\n",
reg_value);
munmap((void*)pTestRegBase,
MAP_SIZE);
}
pTestRegBase
=
0;
if(dev_fd)
close(dev_fd);
這里將DDR2_REG_BASE開(kāi)始大小為1個(gè)page的物理地址映射到了用戶空間,然后就可以用pTestRegBase作為起始地址操作寄存器了。
不是很明白你的意思。寄存器是個(gè)硬件的結(jié)構(gòu),存在CPU中,比如EAX,EBX,ECX,EDX這些通用寄存器。硬件設(shè)備也會(huì)有寄存器,用來(lái)給軟件提供控制的方法。比如顯卡肯定有個(gè)寄存器來(lái)啟用或者禁用。讀寫寄存器標(biāo)準(zhǔn)的使用IN,OUT指令(IA架構(gòu))。當(dāng)然也會(huì)有把寄存器映射到內(nèi)存空間,想讀寫內(nèi)存一樣讀寫寄存器。用戶態(tài)程序一般是無(wú)法訪問(wèn)寄存器的,除非驅(qū)動(dòng)程序把寄存器映射到用戶進(jìn)程空間
1.列出文件清單命令:ls
ls命令能夠列出當(dāng)前目錄下的所有內(nèi)容。ls 命令的執(zhí)行方式為:
# ls [-選項(xiàng)] [文件名或者目錄名]
進(jìn)入到Linux命令行中后,我們至少要知道當(dāng)前所處的位置有哪些內(nèi)容,這些信息就可以使用ls命令來(lái)獲得。
在Linux中,ls命令是最常使用的命令之一,因?yàn)樵诿钚邢乱S時(shí)查看目錄內(nèi)容。如果不加任何選項(xiàng)的話,ls命令僅列出當(dāng)前目錄下的文件和目錄名,例如,想要查看/etc目錄下的內(nèi)容,可以使用下列命令:
# ls /etc
如果想要列出當(dāng)前目錄下所有文件,則可以使用下列命令:
# ls -a
2、cat命令
功能:在標(biāo)準(zhǔn)輸出上顯示文件。
語(yǔ)法:cat [-vTEuAte] 文件
例子: cat example.txt
cat -A exam2.txt
cat file1 file2 file2
3、more命令
功能:在終端屏幕按屏顯示文本文件。
語(yǔ)法: more [-pcdls] 文件
例子: more example.c
more -dc example.c
more -c -10 example.c
4、less命令
less命令的功能幾乎和more命令一樣,也是用來(lái)按頁(yè)顯示文件,不同之處在于less命令在顯示文件時(shí)允許用戶既
可以向前又可以向后翻閱文件。
5、head命令
功能:顯示指定文件的前若干行。缺省設(shè)置為顯示10行
語(yǔ)法:head [-n] 文件
例子: head example.c
head -3 example.c
6、tail命令
功能:顯示指定文件的末尾若干行。缺省設(shè)置為顯示10行
語(yǔ)法:tail [+ / - num ] [參數(shù)] 文件
+num 從第num行以后開(kāi)始顯示。- num 從距文件尾num行處開(kāi)始顯示。
例子: tail example.c
tail -4 example.c
7、grep、fgrep和egrep命令
功能:
這組命令以指定模式搜索文件,并通知用戶在什么文件中搜索到與指定的模式匹配的字符串,并打印出所有包含該字符串的文本行,在該文本行的最前面是該行所在的文件名。grep命令一次只能搜索一個(gè)指定的模式;egrep命令檢索擴(kuò)展的正則表達(dá)式(包括表達(dá)式組和可選項(xiàng));fgrep命令檢索固定字符串,它不識(shí)別正則表達(dá)式,是快速搜索命令。
語(yǔ)法:
grep [-EFbcihlnvxef] [查找模式] [文件名1,文件名2,……]
egrep [選項(xiàng)] [查找模式] [文件名1,文件名2,……]
fgrep [選項(xiàng)] [查找模式] [文件名1,文件名2,……]
例子: grep "text file" example
grep data *
grep goto *.c
設(shè)備驅(qū)動(dòng)? 如果是:可以寫一個(gè) 字符設(shè)備驅(qū)動(dòng)實(shí)現(xiàn)。 驅(qū)動(dòng),最簡(jiǎn)單的是:用 ioremap(),把GPIO的地址映射到 linux內(nèi)核空間。 然后操作該gpio的寄存器。 之后很簡(jiǎn)單,和裸板控制gpio的方法一樣。只是字符設(shè)備方面的實(shí)現(xiàn)不一樣。
處理概要: 通過(guò)制定類型(int,char等)的指針變量,把rw的地址給這個(gè)指針。 通過(guò)指針操作,取得含有07位的數(shù)值,然后通過(guò)移位運(yùn)算即可取得07位的值。 僅供參考。