一、地址映射
#include <asm/io.h>
#define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE)
#define iounmap __arm_iounmap
二、内存访问
#include <asm/io.h>
#define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; })
#define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
#define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
#define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); })
#define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); })
#define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })
三、应用源码
ledAPP.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
int main(int argc, char *argv[])
{
int retvalue = 0;
int fd;
char *filename;
unsigned char databuf[2];
if(argc != 4)
{
printf(" Usage Error!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0)
{
printf("file %s open failed~\r\n",filename);
return -1;
}
databuf[0] = atoi(argv[2]);
databuf[1] = atoi(argv[3]);
retvalue = write(fd,databuf,sizeof(databuf));
if(retvalue < 0)
{
printf("LED Control Failed~\r\n");
close(fd);
return -1;
}
retvalue = close(fd);
if(retvalue < 0)
{
printf("file %s close failed!\r\n",filename);
return -1;
}
return 0;
}
四、驱动源码
led.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define LED_MAJOR 200
#define LED_NAME "led"
#define LED_ON 1
#define LED_OFF 0
#define CLK_PCLKEN0_BASE (0xB0000218)
#define GPIOB_DIR_BASE (0XB8003040)
#define GPIOBDATAOUT_BASE (0xB8003044)
static void __iomem *CLK_PCLKEN0;
static void __iomem *GPIOB_DIR;
static void __iomem *GPIOBDATAOUT;
static void led_switch(char led1_sw, char led2_sw)
{
u32 val = 0;
val = readl(GPIOBDATAOUT);
//LED1
if(led1_sw == LED_ON)
{
val &= ~(1<<4);
}else if(led1_sw == LED_OFF)
{
val |= (1<<4);
}else
{
printk("LED1 Control cmd error!\r\n");
}
//LED2
if(led2_sw == LED_ON)
{
val &= ~(1<<5);
}else if(led2_sw == LED_OFF)
{
val |= (1<<5);
}else
{
printk("LED2 Control cmd error!\r\n");
}
writel(val,GPIOBDATAOUT);
}
static int led_open(struct inode *inode, struct file *filp){
return 0;
}
static int led_release(struct inode *inode, struct file *filp){
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos){
int retvalue = 0;
char databuf[2];
copy_from_user(databuf,buf,count);
if(retvalue < 0)
{
printk("Control LED Failed!\r\n");
return -1;
}
led_switch(databuf[0],databuf[1]);
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.release = led_release,
};
static int __init led_init(void)
{
int retvalue = 0;
u32 val = 0;
/* init led */
CLK_PCLKEN0 = ioremap(CLK_PCLKEN0_BASE,4);
GPIOB_DIR = ioremap(GPIOB_DIR_BASE,4);
GPIOBDATAOUT= ioremap(GPIOBDATAOUT_BASE,4);
val = readl(CLK_PCLKEN0);
val |= (1<<3);
writel(val, CLK_PCLKEN0);
val = readl(GPIOB_DIR);
val |= ((1<<4)|(1<<5)); //PB4 and PB5
writel(val, GPIOB_DIR);
val = readl(GPIOBDATAOUT);
val &= ~(unsigned int)((1<<4)|(1<<5));
retvalue = register_chrdev(LED_MAJOR,LED_NAME,&led_fops);
if(retvalue < 0){
printk("register chrdev failed!\r\n");
return -1;
}
printk("led_init\r\n");
return 0;
}
static void __exit led_exit(void)
{
u32 val = 0;
/* close the led */
val = readl(GPIOBDATAOUT);
val |= (1<<4)|(1<<5);
writel(val, GPIOBDATAOUT);
iounmap(CLK_PCLKEN0);
iounmap(GPIOB_DIR);
iounmap(GPIOBDATAOUT);
unregister_chrdev(LED_MAJOR,LED_NAME);
printk("led_exit\r\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("mx");
五、实验总结
1.和操作单片机寄存器不同,写驱动涉及到的单片机,需要将物理寄存器地址映射到虚拟内存地址,通过操作虚拟内存地址来修改物理地址的值;
2.提供了内存操作函数readl 和writel,读和写4字节数据,对应32位寄存器数据;
3.按照正点原子视频教程实现点灯操作,可同时控制两个LED灯的独立亮灭。
./ledAPP /dev/led 0 0
./ledAPP /dev/led 0 1