(最近开始学习Linux驱动编程,为了方便总结就把信息简单的归纳下来了。由于初学,技术很欠缺,欢迎纠正错误的地方)
OS :linux
cpu :s3c2440
开发板:TQ2440
先制作一个驱动的基本构架:
#define MAYOR 0
#define DEV_NAME "myled"
static int DevNum = 0;
static int leds_open (struct inode *inode, struct file *file);
{}
static int leds_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
{}
static struct file_operations led_fop= {
.owner = THIS_MODULE;
.open = leds_open;
.ioctrl = leds_ioctl;
};
static int __init s3c2440_leds_init(void)
{
int ret;
DevNum = register_chrdev( MAYOR, DEV_NAME, &led_fop);
if( DevNum<0 )
{
printk(DEV_NAME" can't register major number\n");
return DevNum;
}
printk(DEV_NAME" initialzed\n");
return 0;
}
static int __exit s3c2440_leds_exit(void)
{
if( DevNum>0)
{
unregister_chrdev( DevNum, DEV_NAME);
}
}
module_init(s3c2440_leds_init);
module_exit(s3c2440_leds_exit);
找到外接硬件的对应管教:
现在开始根据手册查找开发板信息(这一步是开发板级信息),由于目标是控制led,故先去“TQ2440的底板原理图”查找管教信息。根据led的电路连接,查找到其对应的cpu管教为GPB5~8.意味着 你只需要控制这几个管教的电平,就可以控制led灯的开关(不考虑负载问题)。
配置管教:
每个管脚有多重工作状态,现在对其进行配置。管脚的配置信息来源于芯片级信息,所以与开发板无关。查找芯片手册,发现这款芯片的管脚有如下几种配置寄存器
GPBCON --In S3C2440A, most of the pins are multiplexed pins. So, It is determined which function is selected for each pins.The PnCON(port control register) determines
which function is used for each pin。用来配置管脚工作状态
GPBDAT --If Ports are configured as output ports, data can be written to the corresponding bit of PnDAT. If Ports are configured as input ports, the data can be read from the
corresponding bit of PnDAT.用来收发数据
GPBUP --The port pull-up register controls the pull-up resister enable/disable of each port group. When the corresponding bit is 0, the pull-up resister of the pin is enabled.
When 1, the pull-up resister is disabled.控制上拉电阻使能
EXTINT--The 24 external interrupts are requested by various signaling methods. The EXTINT register configures the signaling method among the low level trigger, high level
trigger, falling edge trigger, rising edge trigger, and both edge trigger for the external interrupt request.中断控制
根据资料只需要将GPB5~8脚配置成输出管脚(配置GPBCON,地址为0x56000010,这是CPU设计时为内部寄存器分配的地址)并向数据寄存器(GPBDAT,0x56000014)
写数据。通过s3c2440的芯片手册知道,需要将GPB[17-10]位设置为(01010101)B.实际操作时的屏蔽码为(~0x3fc00),开始填充leds_open函数。
填充leds_open函数:
static int leds_open (struct inode *inode, struct file *file);
{
s3c2410_gpio_cfgpin(unsigned int pin,unsigned int function);
GCon_V = ioremap( GPBCON_P, 4);
if( GCon_V==NULL)
{
printk(DEV_NAME" fail to ioremap GPBCON_P\n");
goto leds_open_err_GCon_v;
}
GDat_V = ioremap( GPBDAT_P, 4);
if( GDat_V==NULL)
{
printk(DEV_NAME" fail to ioremap GPBDAT_P\n");
goto leds_open_err_GDat_v;
}
unsigned int data = ioread32(GCon_V); //读取原有管脚数据
data &= (~0x3fc00);
data |= (0x15400);
iowrite32( data, GCon_V); //写入新的数据
return 0;
leds_open_err_GDat_v:
iounmap(GCon_V);
leds_open_err_GCon_v:
return -1;
}
填充leds_ioctl函数:
static int inline _leds_on(int ledNum)
{
unsigned int data = ioread32( GDat_V);
data &= (~(0x20<<ledNum));
iowrite32( 0, GDat_V);
return 0;
}
static int inline _leds_off(int ledNum)
{
unsigned int data = ioread32( GDat_V);
data |= (0x20<<ledNum);
iowrite32( data, GDat_V);
return 0;
}
static int leds_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
if( arg>4 || arg<0)
{
return -EINVAL;
}
switch(cmd)
{
case CMD_LED_ON:
_leds_on( arg);
break;
case CMD_LED_OFF:
_leds_off( arg);
break;
default:
printk("error\n");
return -EINVAL;
}
return 0;
}
将驱动编译成模块:
将驱动编译成模块式需要系统的源码作为编译背景,参考另一篇关于驱动Makefile的编写。
建立设备文件:
由于没有驱动没有自动创建设备,所以手动创建,去"cat /proc/devices"找到自动分配的myled的设备号为252
mknod /etc/myled c 252 0
(有自动创建的方法,暂时不用)
制作测试程序:
int main()
{
int fd;
fd = open("/dev/myled", 0);
if( fd<0 )
{
perror("error");
return 0;
}
int flag = 0;
while(1)
{
if(flag==0)
{
printf("led on\n");
ioctl(fd, CMD_LED_ON, ARG_LED1);
flag =1;
}
else
{
printf("led off\n");
ioctl(fd, CMD_LED_OFF, ARG_LED1);
flag = 0;
}
sleep(1);
}
}