题目:使用结构体映射,实现LED1、LED2、LED3循环点亮
实现代码:
#ifndef __LED_H__
#define __LED_H__
/*#define PHY_GPIO_MODER 0x50006000
#define PHY_GPIO_ODR 0x50006014
#define PHY_RCC 0x50000A28*/
typedef struct{
volatile unsigned int MODER;
volatile unsigned int OPTYPER;
volatile unsigned int OSPEEDR;
volatile unsigned int PUPDR;
volatile unsigned int IDR;
volatile unsigned int ODR;
}gpio_t;
#define GPIOE (gpio_t *)0x50006000
#define GPIOF (gpio_t *)0x50007000
#define PHY_RCC 0x50000A28
#endif
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "led.h"
#define CNAME "myled"
int major;
char kbuf[128]={0};
volatile unsigned int* virt_rcc;
volatile unsigned int* virt_gpioe;
volatile unsigned int* virt_gpiof;
//#define VIRT_RCC ((gpio_t *)virt_rcc)
#define VIRT_GPIOE ((gpio_t *)virt_gpioe)
#define VIRT_GPIOF ((gpio_t *)virt_gpiof)
//打开
int mycdev_open(struct inode *inode,struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
//读取
ssize_t mycdev_read(struct file* file,char __user *ubuf,size_t size,loff_t *loffs)
{
int ret;
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
if(size >sizeof(kbuf)) size = sizeof(kbuf);
ret = copy_to_user(ubuf,kbuf,size);
if(ret)
{
printk("copy to user error\n");
return -EIO;
}
printk("copy to user ubug =%s\n",ubuf);
return 0;
}
//写入
ssize_t mycdev_write(struct file* file,const char __user *ubuf,size_t size,loff_t *loffs)
{
int ret;
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
if(size >sizeof(kbuf)) size = sizeof(kbuf);
ret = copy_from_user(kbuf,ubuf,size);
if(ret)
{
printk("copy from user error\n");
return -EIO;
}
switch (kbuf[0])
{
case '1': //PE10
if('1'== kbuf[1])
{
VIRT_GPIOE->ODR |= (0x1 << 10); //led1输出高电平
}
else if('0' == kbuf[1]) //熄灭
{
VIRT_GPIOE->ODR &= (~(0x1 << 10));//led1输出低电平
}
break;
case '2': //PF10
if('1'== kbuf[1])
{
VIRT_GPIOF->ODR |= (0x1 << 10); //led1输出高电平
}
else if('0' == kbuf[1]) //熄灭
{
VIRT_GPIOF->ODR &= (~(0x1 << 10));//led1输出低电平
}
break;
case '3': //PE8
if('1'== kbuf[1])
{
VIRT_GPIOE->ODR|= (0x1 << 8); //led1输出高电平
}
else if('0' == kbuf[1]) //熄灭
{
VIRT_GPIOE->ODR &= (~(0x1 << 8));//led1输出低电平
}
break;
}
return size;
}
//关闭
int mycdev_close(struct inode *inode,struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
const struct file_operations fops ={
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
//入口
static int __init demo_init(void)
{
//注册字符设备驱动
major = register_chrdev(0,CNAME,&fops);
if(major <0)
{
printk("registe chrdec is error\n");
return major;
}
printk("major=%d\n",major);
//映射3个物理地址
virt_rcc = ioremap(PHY_RCC,4);
if(NULL == virt_rcc)
{
printk("rcc ioremap is error\n");
return -ENOMEM;
}
virt_gpioe = ioremap(GPIOE,sizeof(GPIOE));
if(NULL == virt_gpioe)
{
printk("gpio moder ioremap is error\n");
return -ENOMEM;
}
virt_gpiof = ioremap(GPIOF,sizeof(GPIOF));
if(NULL == virt_gpiof)
{
printk("gpio odr ioremap is error\n");
return -ENOMEM;
}
//将rcc、gpio初始化 PE10\PF10\PE8
*virt_rcc |= (0x3<<4);
//PE10初始化
VIRT_GPIOE->MODER &= (~(0x3<<20));
VIRT_GPIOE->MODER |= 0x1<<20;
VIRT_GPIOE->ODR &= (~(0x1<<10));
//PE8初始化
VIRT_GPIOE->MODER &= (~(0x3<<16));
VIRT_GPIOE->MODER |= 0x1<<16;
VIRT_GPIOE->ODR &= (~(0x1<<8));
//PF10初始化
VIRT_GPIOF->MODER &= (~(0x3<<20));
VIRT_GPIOF->MODER |= 0x1<<20;
VIRT_GPIOF->ODR &= (~(0x1<<10));
return 0;
}
static void __exit demo_exit(void)
{
//注销字符设备驱动
iounmap(virt_rcc);
iounmap(virt_gpioe);
iounmap(virt_gpiof);
unregister_chrdev(major,CNAME);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
代码实现原理:
1、使用结构体对GPIO需要使用到的寄存器进行封装;
2、对整个结构体进行映射,返回结构体的首物理地址的虚拟地址A;
3、将该A虚拟地址强转为结构体指针类型;
4、在write函数中使用A指针访问结构体成员变量GPIO的各个寄存器;
5、用switch和if语句判断哪个灯应该被点亮,执行相应操作
测试现象: