板子跑起来以后,迫切的愿望就是操作板子上的东西,可是一看代码里全是linux的内容,都不知道从哪下手后来在网上搜资料,发帖子,折腾了好几天,终于能写一个led的程序了(好像不管玩什么板子,点亮led灯程序都是第一个实验的)
首先,需要知道的是,怎么样写驱动,linux系统中的驱动编写好像挺复杂,都是用模块来表示的,并不像其他芯片那样可以直接写,他需要先建一个驱动的模块文件(也就是写个驱动的C文件,然后把这个C文件做成模块,具体怎么做在下面代码里有),然后用的时候都是open这个文件,然后调用里面的函数(好吧,听着也不是那么复杂,可是开始不知道的时候确实花了我很长时间才懂)
我的linux工程文件夹有apps,kernel文件夹等,因为想让工程比较有条理性,开始的时候我把driver_led.c(即led驱动)放在apps下,新建了一个文件夹,然后发现有一堆问题,什么Makefile不对,头文件找不到,等等等等,后来觉得还是跟其他的驱动放在一起吧,直接和其他的驱动放在相同文件夹下,仿照其他驱动的写法,只需要在对应的Makefile里添上自己的驱动文件就好了(我放的位置是kernel/linux-2.6.30/driver/gpio/)
driver_led.c文件
#include <linux/module.h> //这一堆头文件是我从网上的例子上搬下来的,好像有一些也没用
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/pci.h>
#include <mach/map.h>
#include <plat/regs-gpio.h>
#include <plat/gpio-bank-m.h>
#include <plat/gpio-cfg.h>
#define LED_MAJOR 240 //这个是后面要创建一个my_led模块用到的主设备号(具体是不是这么叫,我也不确定,反正就是这么个意思了,可以上网查查)
//执行到这个文件时,首先执行的是init()函数(在下面),接着好像就是open()了,这里就是初始化引脚的功能
int led_open (struct inode *inode,struct file *filp)
{
unsigned tmp;
tmp = readl(S3C64XX_GPMCON); //好像所有的6410的例子都是PM脚,可能是大家的板子都是那几个引脚是输出吧
tmp = (tmp & ~(0xffeeee))|(0x001111U);
writel(tmp, S3C64XX_GPMCON);
tmp = readl(S3C64XX_GPMDAT);
tmp |= (0x0fU);
writel(tmp, S3C64XX_GPMDAT);
tmp = readl(S3C64XX_GPPCON); //好像所有的6410的例子都是PM脚,可能是大家的板子都是那几个引脚是输出吧
tmp &= (~0xfU);
writel(tmp, S3C64XX_GPPCON);
printf("#########open######\n");
return 0;
}
ssize_t led_read (struct file *filp, char __user *buf, unsigned long count,loff_t *f_pos)
{
unsigned long tmp;
char wbuf[10];
copy_from_user(wbuf,buf,count);
switch(wbuf[0])
{
case 0:
tmp = readl(S3C64XX_GPPDAT); //这里我用了PP0作为输入,也就是想控制PP0为1时,PM为1,PP0为0时,PM为0
break;
default :
break;
}
printk("#########read######\n");
return tmp;
}
ssize_t led_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
char wbuf[10];
unsigned tmp;
printk("#########write######\n");
copy_from_user(wbuf,buf,count);//拷贝country个buf数组的值给wbuf
switch(wbuf[0])
{
case 0:
tmp = readl(S3C64XX_GPPDAT);
tmp &= (~0x0f);//PM0~3 = 0
writel(tmp, S3C64XX_GPPDAT);
break;
case 1:
tmp = readl(S3C64XX_GPPDAT);
tmp |= 0x0f;//PM0~3 =
1
writel(tmp, S3C64XX_GPPDAT);
break;
default:
break;
}
return count;
}
//这个具体干嘛的还没研究,看所有的例程上都有,就放着了,以后我会添点东西看看到底其什么作用
int led_release (struct inode *inode, struct file *filp)
{
printk("#########release######\n");
return 0;
}
//这个结构体的具体意思我也是在网上查了很久才大概知道什么意思,反正只要理解成几个函数就行了,我这边看网上的例子后知道:在外面调用open函数,就会操作这个驱
//动文件里的led_open函数,大体上就是这个意思。
struct file_operations led_fops ={
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
//在这里初始化该驱动,我在里面用函数控制在系统初始化的时候就把这个模块加载上去,不然还用手动加载
//里面的函数刚开始用,如果有什么不对的请大家指出来哈
int __init led_init (void)
{
int rc;
printk ("Test led dev\n");
//驱动字符加载函数 LED_MAJOR:主设备号 ,led设备名 led_fops:包含基 本函数入口点的结构体
rc = register_chrdev(LED_MAJOR,"led",&led_fops);
if (rc <0)
{
printk ("register %s char dev error\n","led");
return -1;
}
simple_class= class_create(THIS_MODULE, "my_led");
if(IS_ERR(simple_class))
{
printk("ERR:cannot create a simple_class");
return -1;
}
device_create(simple_class,NULL, MKDEV(LED_MAJOR, 0), 0, "my_led");
printk ("ok!\n");
return 0;
}
//这个当然就是退出了
void __exit led_exit (void)
{
unregister_chrdev(LED_MAJOR,"led");
printk ("module exit\n");
}
module_init(led_init); //装载驱动后第一个执行的就是该函数
module_exit(led_exit);
这些完成后,在个gpio文件夹下的Makefile里添加一句话:
obj-$(CONFIG_GPIO_DRIVER_LED) += driver_led.o
在Kconfig里添加以下内容:
menu "LED Support"
config GPIO_DRIVER_LED
bool "LED Support"
---help---
If you want LED Support, you should say Y here and also to the
specific driver for your bus adapter(s) below.
This LED Support can be built as a module.
endmenu
这样make的时候就会提示是否加载led驱动了
把生成的image到板子里以后,secureCRT软件里,#cd dev/,应该就会出现一个my_led的文件了(这个是后话,在这之前还要建立一个测试的C文件,毕竟写驱动就是为了用嘛)
ledtest.c文件 这个文件我是放在apps里的,没有内核的头文件,不会有头文件编译不过的问题,所以就放这里了。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main (void)
{
int fd;
unsigned tmp;
char buf[10]={0,1};
fd = open("/dev/my_led",O_RDWR); //打开my_led文件 O_RDWR表示可读可写
if (fd < 0)
{
printf ("Open /dev/my_led file error\n");
return -1;
}
while(1)
{
tmp = read(fd,&buf[0],1);//PP10引脚 这里的read()函数实际就是调用的led_read()函数了,对应上面driver_led.c里的结构体
if(((tmp>>0)&0x01) == 0)//如果PP0为0
{
write(fd,&buf[0],2);//P0~3 =0//好简单的功能。。。。
}
else if (((tmp>>0)&0x01) == 1)//如果PP10的引脚为1
{
write(fd,&buf[1],1);//P0~3 =01
}
}
close (fd);
return 0;
}
下面是ledtest.c的Makefile文件(Makefile文件还是没研究透,只能依样画葫芦了,参考别的写的,Makefile文件里每一行开头的缩进都是tab键而不是空格键!!!!!!!!!!!!!!!!!!!!!!!!!!!)
include ../../Rule.mk
CFLAGS += -Os -Wall -s
#INCLUDES = -I. -I../bcmutils -I. -I../nvram
#LIBS= -L../bcmutils -lbcmutils -L../nvram -lnvram
BIN = gpio_main
OBJS = gpio_main.o
all: $(BIN)
$(BIN):$(OBJS)
$(CC) $(CFLAGS) $(OBJS) $(LIBS) -o $@
%.o: %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $<
install:
cp -a led_main ../../target/usr/sbin
clean:
rm -f led_main *.o
这下就可以生成Image文件了。
烧录以后,执行下面的命令:
#cd usr/
#cd sbin/
#./ledtest
就可以操作引脚了,当然,这里需要自己在硬件上把PP0做成输入,有个按键是最好的了