[驱动程序] 申请物理内存区、映射和使用

裸机控制硬件的流程:
分析原理图–>找到控制硬件的GPIO–>找GPIO的寄存器—>分析寄存器—>理解寄存器的控制顺序—>通过寄存器的地址来访问该寄存器

注意:
裸机使用的是物理地址,所以直接使用CPU手册查到的地址可以编程。
linux驱动使用的虚拟地址,不能直接使用物理地址。想办法,如果通过CPU手册查到的物理地址找到其对应虚拟地址?

内核一般分成两个过程:
申请物理地址区作为一个资源----->将物理内存区做内存的动态映射,得到虚拟地址。

注意:
资源—有限的,一旦一个物理内存区已经申请了,后面就不能再次申请。

1.申请物理内存区作为资源

struct resource *  request_mem_region(resource_size_t start, resource_size_t n, const char *name)

参数说明:
resource_size_t start  --->物理内存区的开始地址
resource_size_t n --->物理内存区的大小
const char *name --->自定义的物理内存区的名字
返回值:
struct resource * --->物理内存区作为了资源

思考:
LED驱动,申请哪个物理内存区?
D8–>GPIOC17,D9–>GPIOC8,D10–>GPIOC7,D11–>GPIOC12
start address —>0xC001C000
address size —> 结束地址:0xC001CFFF,大小:0x1000
name —>“GPIOC_MEM”

2.释放申请的物理内存区

void release_mem_region(resource_size_t start, resource_size_t n)

3.IO内存动态映射

#include <linux/io.h>

将一段物理地址内存区映射成一段虚拟地址内存区

void __iomem *ioremap(phys_addr_t offset, unsigned long size)
参数说明:
phys_addr_t offset --->要映射的物理内存区开始地址
unsigned long size --->物理内存区的大小
返回值:
void __iomem * --->映射后,虚拟地址内存区的首地址

3.解除IO内存动态映射

void iounmap(void __iomem *addr)

4.使用虚拟地址

①得到虚拟地址

gpioc_base_va = ioremap(phys_addr_t offset, unsigned long size)
if(gpioc_base_va == NULL){
	printk("ioremap error\n");
	release_mem_region(0xC001C000, 0x1000);
	device_destroy(leds_class, led_num);
	class_destroy(leds_class);
	cdev_del(&gec6818_led_cdev);
	unregister_chrdev_region(led_num, 1);
    return -EBUSY;
}
	//得到每个寄存器的虚拟地址
	gpiocout_va = gpioc_base_va + 0x00;
	gpiocoutenb_va = gpioc_base_va + 0x04;
	gpiocaltfn0_va = gpioc_base_va + 0x20;
	gpiocaltfn1_va = gpioc_base_va + 0x24;
	gpiocpad_va = gpioc_base_va + 0x18;

②虚拟地址的类型:void __iomem *
③访问虚拟地址的方法:与访问物理地址的方法一样

//1.访问虚拟地址
//1.1 GPIOC7,8.12,17 --->function1,作为普通的GPIO
*(unsigned int *)gpiocaltfn0_va &=~((3<<14)|(3<<16)|(3<<24));
*(unsigned int *)gpiocaltfn1_va &=~(3<<2);
*(unsigned int *)gpiocaltfn0_va |= ((1<<14)|(1<<16)|(1<<24));
*(unsigned int *)gpiocaltfn1_va |= (1<<2);
//1.2 GPIOC7,8.12,17 --->设置为输出
*(unsigned int *)gpiocoutenb_va |= ((1<<7)|(1<<8)|(1<<12)|(1<<17));
//1.3 GPIOC7,8.12,17 --->设置为输出高电平,D8~D11 off
*(unsigned int *)gpiocout_va |= ((1<<7)|(1<<8)|(1<<12)|(1<<17));

④虚拟地址的访问方法:使用内核提供的函数

u8 readb(const volatile void __iomem *addr)
u16 readw(const volatile void __iomem *addr)
u32 readl(const volatile void __iomem *addr)
void writel(u32 b, volatile void __iomem *addr)
或者:
void __raw_writel(u32 b, volatile void __iomem *addr)
u32 __raw_readl(const volatile void __iomem *addr)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值