一、Led点灯实验
我们肯定是需要往寄存器内些数据的,Linux下也可以操作寄存器,但是不能直接对物理地址进行直接读写操作。因为linux下会使能MMU,MMU是内存管理单元,在老Linux中要求处理器必须有MMU,它主要完成功能:
①、虚拟空间到物理空间的映射
②、内存保护、设置存储器的访问权限。设置虚拟存储空间的缓冲特性。
1.地址映射
操作4G的虚拟地址,对虚拟地址使用ioremap函数,写在驱动使用iounmap函数。函数作用:将物理地址传入以后指向虚拟地址。
phys_addr:物理地址的其实大小。
size:转化的字节数量。
void __iomem *ioremap (unsigned long phys_addr, unsigned long size);
void iounmap (volatile void __iomem *addr);
过程创建:
-
定义物理地址
-
定义映射后的虚拟指针,如下
static void __iomem *IMX6U_CCM_CCGR1;3. 使用ioremap进行字节对齐,映射到虚拟地址上。 4. 使用虚拟地址进行读写操作寄存器。 5. 释放程序之前记得iounmap释放掉地址。
2.LED驱动
1.框架搭建
2.初始化LED -->
/*
1.映射地址
2.初始化时钟
3.复用IO,电气特性配置
4.DR寄存器写入
注:调试的时候,这个地方出了问题:
具体原因是按照裸机编程的时候使用的是NXP官方提供的接口,所以只需要IO的MUX地址,从而忽略了手册当中使用的PAD地址。
*/
3.补充fops的函数指针。
根据Led应用程序,写出相应的驱动read , write接口函数。
3.led_driver.c程序
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include <asm/uaccess.h>
#include<linux/io.h>
/*
load module : mknod /dev/led c 200 0
*/
#define LED_MAJOR 200
#define LED_NAME "led"
#define LED_OFF 0
#define LED_ON 1
#define LED_RED 0
#define LED_GREEN 1
#define LED_BLUE 2
#define CLEAR_TMP(x) (x = 0)
/*寄存器物理地址*/
#define CCM_CCGR1_BASE (0x020C406C) //GPIO1的CCM1
#define CCM_CCGR3_BASE (0x020C4074) //GPIO4的CCM3
#define IOMUXC_GPIO1_IO04_GPIO1_IO04_BASE (0x020E006C) //红灯的复用寄存器
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04_BASE (0x020E02F8) //红灯的电气特性寄存器
#define IOMUXC_CSI_HSYNC_GPIO4_IO20_BASE (0x020E01E0)
#define IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC_BASE (0X020E046C)
#define IOMUXC_CSI_VSYNC_GPIO4_IO19_BASE (0x020E01DC)
#define IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC_BASE (0X020E0468)
#define GPIO1_DR_BASE (0x0209C000) //GPIO1_DR
#define GPIO1_GDIR_BASE (0x0209C004) //GPIO1_GDIR
#define GPIO4_DR_BASE (0x020A8000) //GPIO4_DR
#define GPIO4_GDIR_BASE (0x020A8004) //GPIO4_GDIR
/*地址映射后的虚拟地址指针*/
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *IMX6U_CCM_CCGR3;
static void __iomem *IOMUXC_GPIO1_IO04_GPIO1_IO04;
static void __iomem *IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04;
static void __iomem *IOMUXC_CSI_HSYNC_GPIO4_IO20;
static void __iomem *IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC;
static void __iomem *IOMUXC_CSI_VSYNC_GPIO4_IO19;
static void __iomem *IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
static void __iomem *GPIO4_DR;
static void __iomem *GPIO4_GDIR;
unsigned char readbuff[10];
unsigned char writebuff[1];
/*
LED模块化程序
*/
void led_on(unsigned char led_num){
u32 tmp = 0;
switch(led_num){
case LED_RED:
tmp = readl(GPIO1_DR); /*DR寄存器 , 低电平打开LED*/
tmp &= ~(1 << 4);
writel(tmp , GPIO1_DR);
break;
case LED_GREEN:
tmp = readl(GPIO4_DR); /*DR寄存器 , 低电平打开LED*/
tmp &= ~(1 << 20);
writel(tmp , GPIO4_DR);
break;
case LED_BLUE:
tmp = readl(GPIO4_DR); /*DR寄存器 , 低电平打开LED*/
tmp &= ~(1 << 19);
writel(tmp , GPIO4_DR);
break;
default:
printk(" invalid commond \r\n");
break;
}
}
void led_off(unsigned char led_num){
u32 tmp = 0;
switch(led_num){
case LED_RED:
tmp = readl(GPIO1_DR); /*DR寄存器 , 高电平关闭LED*/
tmp |= (1 << 4);
writel(tmp , GPIO1_DR);
break;
case LED_GREEN:
tmp = readl(GPIO4_DR); /*DR寄存器 , 高电平关闭LED*/
tmp |= (1 << 20);
writel(tmp , GPIO4_DR);
break;
case LED_BLUE:
tmp = readl(GPIO4_DR); /*DR寄存器 , 高电平关闭LED*/
tmp |= (1 << 19);
writel(tmp , GPIO4_DR);
break;
default:
printk(" invalid commond \r\n");
break;
}
}
void Led_Init(void){
u32 tmp = 0;
/* 1.初始化LED地址映射 */
IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE , 4);
IMX6U_CCM_CCGR3 = ioremap(CCM_CCGR3_BASE , 4);
IOMUXC_GPIO1_IO04_GPIO1_IO04 = ioremap(IOMUXC_GPIO1_IO04_GPIO1_IO04_BASE , 4);
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04 = ioremap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04_BASE , 4);
IOMUXC_CSI_HSYNC_GPIO4_IO20 = ioremap(IOMUXC_CSI_HSYNC_GPIO4_IO20_BASE , 4);
IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC = ioremap(IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC_BASE , 4);
IOMUXC_CSI_VSYNC_GPIO4_IO19 = ioremap(IOMUXC_CSI_VSYNC_GPIO4_IO19_BASE , 4);
IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC = ioremap(IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC_BASE , 4);
GPIO1_DR = ioremap(GPIO1_DR_BASE , 4);
GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE , 4);
GPIO4_DR = ioremap(GPIO4_DR_BASE , 4);
GPIO4_GDIR = ioremap(GPIO4_GDIR_BASE , 4);
/* 2.初始化时钟 */
tmp = readl(IMX6U_CCM_CCGR1); /*[27,26]GPIO1的时钟*/
tmp &= ~(3 << 26);
tmp |= (3 << 26);
writel(tmp , IMX6U_CCM_CCGR1);
CLEAR_TMP(tmp);
tmp = readl(IMX6U_CCM_CCGR3); /*[13,12]GPIO4的时钟*/
tmp &= ~(3 << 12);
tmp |= (3 << 12);
writel(tmp , IMX6U_CCM_CCGR3);
CLEAR_TMP(tmp);
/* 3.电气特性设置 */
writel(0x5 , IOMUXC_GPIO1_IO04_GPIO1_IO04); /*复用GPIO*/
writel(0x10b0 , IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04); /*电气特性设置*/
writel(0x5 , IOMUXC_CSI_HSYNC_GPIO4_IO20);
writel(0x10b0 , IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC);
writel(0x5 , IOMUXC_CSI_VSYNC_GPIO4_IO19);
writel(0x10b0 , IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC);
/* 4.寄存器设置 */
tmp = readl(GPIO1_GDIR); /*GDIR寄存器为输出*/
tmp |= (1 << 4);
writel(tmp , GPIO1_GDIR);
led_off((unsigned char)LED_RED);
CLEAR_TMP(tmp);
tmp = readl(GPIO4_GDIR); /*GDIR寄存器为输出*/
tmp |= (1 << 20);
writel(tmp , GPIO4_GDIR);
led_off((unsigned char)LED_GREEN);
CLEAR_TMP(tmp);
tmp = readl(GPIO4_GDIR); /*GDIR寄存器为输出*/
tmp |= (1 << 19);
writel(tmp , GPIO4_GDIR);
led_off((unsigned char)LED_BLUE);
CLEAR_TMP(tmp);
}
void led_switch(u8 status , u8 led_num){
if(status == LED_ON){
switch(led_num){
case LED_RED:
led_on((unsigned int)LED_RED);
break;
case LED_GREEN:
led_on((unsigned int)LED_GREEN);
break;
case LED_BLUE:
led_on((unsigned int)LED_BLUE);
break;
}
}else if(status == LED_OFF){
switch(led_num){
case LED_RED:
led_off((unsigned int)LED_RED);
break;
case LED_GREEN:
led_off((unsigned int)LED_GREEN);
break;
case LED_BLUE:
led_off((unsigned int)LED_BLUE);
break;
}
}
}
/*
fileoperation的实现
*/
static int led_open(struct inode *inode, struct file *filp){
printk("------led_open-------\r\n");
led_on((unsigned int)LED_RED);
led_on((unsigned int)LED_GREEN);
led_on((unsigned int)LED_BLUE);
return 0;
}
int led_release(struct inode *inode, struct file *filp){
printk("------led_release--------\r\n");
led_off((unsigned int)LED_RED);
led_off((unsigned int)LED_GREEN);
led_off((unsigned int)LED_BLUE);
return 0;
}
ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos){
unsigned int ret = 0;
printk("---------led_read--------\r\n");
ret = copy_to_user(buf, readbuff, count);
if(ret == 0){
printk("kernel sendata:%s \r\n" , readbuff);
}else{
}
return 0;
}
ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos){
int ret = 0 ;
unsigned char databuf[1]; //接受数据
printk("---------led_write--------\r\n");
ret = copy_from_user(databuf , buf , count);
printk("databuf : %d\n" , databuf[0]);
if(ret == 0){
if(databuf[0] == LED_ON){
led_on((unsigned int)LED_RED);
led_on((unsigned int)LED_GREEN);
led_on((unsigned int)LED_BLUE);
}else if(databuf[0] == LED_OFF){
led_off((unsigned int)LED_RED);
led_off((unsigned int)LED_GREEN);
led_off((unsigned int)LED_BLUE);
}
}else{
printk("app write error\r\n");
return -EFAULT;
}
return 0;
}
static const struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.read = led_read,
.write = led_write,
};
/*
注册与注销设备
*/
static int __init led_init(void){
int major = 0;
printk("-----led_init-----\n");
Led_Init(); //LED初始化
// /*注册设备号*/
if( (major = register_chrdev(LED_MAJOR, LED_NAME, &led_fops)) < 0){
printk("LED设备号申请错误!\n");
return -EIO;
}
return 0;
}
static void __exit led_exit(void){
printk("-----Led_exit-----\n");
/*取消LED地址映射*/
iounmap(IMX6U_CCM_CCGR1);
iounmap(IMX6U_CCM_CCGR3);
iounmap(IOMUXC_GPIO1_IO04_GPIO1_IO04);
iounmap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04);
iounmap(IOMUXC_CSI_HSYNC_GPIO4_IO20);
iounmap(IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC);
iounmap(IOMUXC_CSI_VSYNC_GPIO4_IO19);
iounmap(IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);
iounmap(GPIO4_DR);
iounmap(GPIO4_GDIR);
unregister_chrdev(LED_MAJOR, LED_NAME);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
4.led_app.c
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
要求命令 ./led_app <dev_nod> <read/write>
*/
int main(int argc, char *argv[])
{
int fd = 0 , ret = 0 ;
int tmp = atoi(argv[2]);
char read_buff[10] , write_buff[1];
if(argc != 3){
printf("Error : argc at list 3!\r\n");
return 0;
}
fd = open("/dev/led" , O_RDWR);
if(fd < 0){
printf("打开LED设备结点失败\n");
return -1;
}
while(1){
printf("Please enter commond:\r\n");
printf("LED open? close?\r\n");
printf("Open :1 \nClose:0\n");
scanf("%d" , write_buff);
if(tmp == 0){
printf("-------->read\n");
ret = read(fd , read_buff ,10);
if(ret < 0){
printf("read error!\n");
}
else{
}
}
else if(tmp == 1){
printf("-------->write\n");
ret = write(fd , write_buff ,sizeof(write_buff));
if(ret < 0){
printf("write error!\n");
}
else{
}
}
printf("writebuff : %d\n\n\n" , write_buff);
}
close(fd);
return 0;
}
5.测试程序
1、在控制台中创建设备结点
2、加载驱动
3、使用应用程序
mknod /dev/led c 200 0
cd mydrivers
insmod led_driver.c
./led_app /dev/led 1