Ok6410开发板LED连接电路图:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZwpmLxAjN481M1ETOykDN0MTMvwFNx8CX4AjMxAjMvw1ckF2bsBXdvwFdl5mLuR2cj5Set1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
从电路图上我们可以看到,发光二极管LED 的一端连接到了ARM 的GPIO,另一端经过一个限流电阻接电源VCC3。当GPIO 口为低电平时,LED 两端产生电压降,这时LED 有电流通过并发光。反之当GPIO 为高电平时,LED 将熄灭。注意亮灭之间要有一定的延时,以便人眼能够区分出来。
4个LED分别连接到核心板上的GPM端:
从上面可以看出4个LED对应的端口:
GPM0->LED1 GPM1->LED2 GPM2->LED3 GPM3->LED4
查看s3c6410芯片手册,端口M对应的三个寄存器地址:
实验相应寄存器
端口配置寄存器
端口数据寄存器
下面我们开始代码部分
I/O 内存访问流程:
1. request_mem_region() 申请IO内存
2. ioremap() 将物理地址映射到虚拟地址
3. ioread8() 、ioread16()、ioread32()、iowrite8()、iowrite16()、iowrite32() 读写
4. iounmap() 释放虚拟内存
5. release_mem_region() 释放IO内存
注意:
1、2 在模块初始化或打开设备时调用
4、5 在模块卸载或关闭设备时调用
request_mem_region()不是必须使用,但建议使用。任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再次申请该资源就会失败。
testled.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#define DEVICE_NAME "liye_led"
/* 定义幻数 */
#define LED_IOC_MAGIC 'k'
/* 定义命令 */
#define LED_ALLOFF _IO(LED_IOC_MAGIC, 0)
#define LED_ALLON _IO(LED_IOC_MAGIC, 1)
#define LED_SELECTON _IOW(LED_IOC_MAGIC, 2, int)
/*定义命令的最大个数*/
#define LED_IOC_MAXNR 2
#define GPMCON 0x7F008820 //寄存器地址(物理地址)
#define GPMDAT 0x7F008824
static volatile unsigned long *gpmcon_addr; //经过ioremap映射后的虚拟地址
static volatile unsigned long *gpmdat_addr;
static void Led_port_init(void)
{
//设置GPM0-GPM3为输出端口
//最后结果相当于…0001000100010001,都是输出状态
*gpmcon_addr &= (1U<<16);
*gpmcon_addr |= (1U<<0)|(1U<<4)|(1U<<8)|(1U<<12);
//全亮 低电平亮
*gpmdat_addr &= (1U<<4);
}
static void led_all_on(void )
{
// *gpmdat_addr &= (1U<<4);这个也是可以的,一样的
*gpmdat_addr &= 16;
}
static void led_all_off(void)
{
*gpmdat_addr |= ~(1U<<4);
}
static void led_select_on(unsigned int led_nu)
{
led_all_off();
*gpmdat_addr &= ~(led_nu);
}
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 int led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
int err = 0;
int ioarg = 0;
/* 检测命令的有效性 */
if (_IOC_TYPE(cmd) != LED_IOC_MAGIC)
return -EINVAL;
if (_IOC_NR(cmd) > LED_IOC_MAXNR)
return -EINVAL;
/* 根据命令类型,检测参数空间是否可以访问 */
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
if (err)
return -EFAULT;
switch (cmd)
{
case LED_ALLOFF://全灭
led_all_off();
break;
case LED_ALLON://全亮
led_all_on();
break;
case LED_SELECTON://根据参数有选择的凉
__get_user(ioarg,(int *)arg);//获得用户空间数据
led_select_on(ioarg);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static const struct file_operations led_fops =
{
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.unlocked_ioctl = led_ioctl,
};
static struct miscdevice led_dev =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &led_fops,
};
static int __init led_init(void)
{
int ret;
//申请IO内存,不是必须的。
if (!request_mem_region(GPMCON, 8, "leds"))
{
ret = -EBUSY;
goto request_mem_failed;
}
gpmcon_addr = ioremap(GPMCON, 4);//将物理地址映射为虚拟地址
if (NULL == gpbcon_addr)
{
ret = -EIO;
printk("gpmcon remap failed\n");
goto con_map_failed;
}
gpmdat_addr = ioremap(GPMDAT, 4);
if (NULL == gpmdat_addr)
{
ret = -EIO;
printk("gpmdat remap failed\n");
goto dat_map_failed;
}
printk("gpmcon_addr remap on %p\n", gpmcon_addr);
printk("gpmdat_addr remap on %p\n", gpmdat_addr);
Led_port_init();//初始化led灯
ret = misc_register(&led_dev);//注册一个混杂设备驱动,主设备号为10
if (ret)
{
printk("misc_register failed\n");
goto failed;
}
printk("leds init\n");
return 0;
failed:
iounmap(gpmdat_addr);
dat_map_failed:
iounmap(gpmcon_addr);
con_map_failed:
release_mem_region(GPMCON, 8);
request_mem_failed:
return ret;
}
static void __exit led_exit(void)
{
iounmap(gpmdat_addr); //取消映射
iounmap(gpmcon_addr);
release_mem_region(GPMCON, 8); //释放I/O内存
misc_deregister(&led_dev);//注册混杂设备,主设备号为10
printk("leds exit\n");
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liye");
module_init(led_init);
module_exit(led_exit);
test.c
#include <stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <linux/ioctl.h>
/* 定义幻数 */
#define LED_IOC_MAGIC 'k'
/* 定义命令 */
#define LED_ALLOFF _IO(LED_IOC_MAGIC, 0)
#define LED_ALLON _IO(LED_IOC_MAGIC, 1)
#define LED_SELECTON _IOW(LED_IOC_MAGIC, 2, int)
int main()
{
int fd = 0;
int cmd;
int arg = 0;
int choice;
int flag=0;
/*打开设备文件*/
fd = open("/dev/liye_led",O_RDWR);
if (fd < 0)
{
printf("Open Dev liye_led Error!\n");
return -1;
}
while(1)
{
printf("\n********MENU**********\n");
printf(" [0] LED all off \n");
printf(" [1] LED all on \n");
printf(" [2] Select LED \n");
printf(" [3] Exit \n");
printf("**********************\n\n");
printf("Enter the choice: ");
scanf("%d",&choice);
switch(choice)
{
case 0:
cmd=LED_ALLOFF;
break;
case 1:
cmd=LED_ALLON;
break;
case 2:
printf("Enter the arg (0=<arg<=15) : ");
scanf("%d",&arg);
cmd=LED_SELECTON;
break;
case 3:
flag=1;
break;
default:
printf("Input Error\n");
flag=2;
break;
}
if(flag==1)
break;
if(flag==2)
{
flag=0;
continue;
}
if(ioctl(fd,cmd,&arg)<0)
{
printf("Call ioctl error!\n");
return -1;
}
}
close(fd);
return 0;
}
Makefile
ifneq ($(KERNELRELEASE),)
obj-m := testled.o
else
KDIR := /home/liye/forlinux/linux-2.6.36
all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
install:
cp testled.ko test /home/liye/forlinux/rootfs/course
clean:
rm -f *.o *.ko *.mod.c *.mod.o *.order *.symvers
endif