我的嵌入式学习之路(一)
linux 跑的第一个程序 LED灯
学习嵌入式系统,可能第一个要做的事情就是点亮LED灯,就像写程序的Hello
World一样,虽然是非常简单的一个问题,可是让我鼓捣了两三天,才把等灯点亮。所以写下来,能给初学者一些借鉴的地方。
我的开发板用的是S3C2440,基本上所有的2410/2440开发板都一样的,大部分都是抄的三星公司那个开发板,好了废话不说了,看程序。
led驱动代码如下:
/****************************************Copyright(c)********************************************
** 南京汉德森科技股份有限公司
** 显示研发部
**
** wangdongxu@handson.cc
** http://www.handson.cc
**
**--------------文件信息--------------------------------------------------------------------------------
**文 件
名: LED驱动
**创 建
人: 王东旭
**最后修改日期: 2008年8月27日21:10:50
**描 述: 2440的LED驱动程序,这是我能正确运行的第一个嵌入式程序
** 原作者是《嵌入式Linux应用开发完全手册》的作者韦东山先生
** 我只是在上面又修改了一些内容。
**--------------历史版本信息----------------------------------------------------------------------------
** 创建人: 王东旭
** 版 本: v1.0
** 日 期: 2008年8月27日21:11:37
** 描 述: 原始版本
**
**------------------------------------------------------------------------------------------------------
** 修改人:
** 版 本:
** 日 期:
** 描 述:
**
**--------------当前版本修订------------------------------------------------------------------------------
** 修改人:
** 日 期:
** 描 述:
**
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "leds" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
#define LED_MAJOR 231
/* 主设备号 */
/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define IOCTL_LED_ON 0
#define IOCTL_LED_OFF 1
/* 用来指定LED所用的GPIO引脚 */
static unsigned long led_table [] = {
/*
★★★★这里一定要注意,你用的是那个管脚,我就因为弄错了,一个上午就没有了。
下面这些定义可以在内核根目录
/include/asm/arch/regs-gpio.h中找到
*/
S3C2410_GPF4,
S3C2410_GPF5,
S3C2410_GPF6,
S3C2410_GPF7,
};
/* 用来指定GPIO引脚的功能:输出 */
static unsigned int led_cfg_table [] = {
S3C2410_GPF4_OUTP,
S3C2410_GPF5_OUTP,
S3C2410_GPF6_OUTP,
S3C2410_GPF7_OUTP,
};
/* 应用程序对设备文件/dev/leds执行open(...)时,
* 就会调用s3c24xx_leds_open函数
*/
static int s3c24xx_leds_open(struct inode *inode, struct file
*file)
{
int i;
for (i = 0; i < 4; i++) {
// 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能
/*
★★★★s3c2410_gpio_cfgpin()、s3c2410_gpio_setpin可以在
内核根目录 /include/asm/arch/hardware.h中找到
*/
s3c2410_gpio_cfgpin(led_table[i],
led_cfg_table[i]);
}
return 0;
}
/* 应用程序对设备文件/dev/leds执行ioclt(...)时,
* 就会调用s3c24xx_leds_ioctl函数
*/
static int s3c24xx_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
if (arg > 4) {
return -EINVAL;
}
switch(cmd) {
case IOCTL_LED_ON:
// 设置指定引脚的输出电平为0
s3c2410_gpio_setpin(led_table[arg],
0);
return 0;
case IOCTL_LED_OFF:
// 设置指定引脚的输出电平为1
s3c2410_gpio_setpin(led_table[arg],
1);
return 0;
default:
return -EINVAL;
}
}
/* 这个结构是字符设备驱动程序的核心
* 当应用程序操作设备文件时所调用的open、read、write等函数,
* 最终会调用这个结构中指定的对应函数
*/
static struct file_operations s3c24xx_leds_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = s3c24xx_leds_open,
.ioctl = s3c24xx_leds_ioctl,
};
/*
* 执行“insmod s3c24xx_leds.ko”命令时就会调用这个函数
*/
static int __init s3c24xx_leds_init(void)
{
int ret;
/* 注册字符设备驱动程序
* 参数为主设备号、设备名字、file_operations结构;
* 这样,主设备号就和具体的file_operations结构联系起来了,
* 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
* LED_MAJOR可以设为0,表示由内核自动分配主设备号
*/
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
if (ret < 0) {
printk(DEVICE_NAME " can't register major number/n");
return ret;
}
printk(DEVICE_NAME " initialized/n");
return 0;
}
/*
* 执行”rmmod s3c24xx_leds.ko”命令时就会调用这个函数
*/
static void __exit s3c24xx_leds_exit(void)
{
/* 卸载驱动程序 */
unregister_chrdev(LED_MAJOR, DEVICE_NAME);
}
/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
/* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR("http://www.100ask.net"); // 驱动程序的原作者
MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver"); // 一些描述信息
MODULE_LICENSE("GPL"); // 遵循的协议
驱动写好以后,文件名为s3c24xx_leds.c把这个文件放到 /drivers/char子目录下,修改这个目录的Makefile,其实只需要增加一行,obj
+= s3c24xx_leds.o,然后,退回到这个内核的根目录下(比如内核源码文件夹叫linux-2.6,在tmp目录下,则你的内核根目录就是/tmp/linux-2.6,以下内核根目录,我简称为根目录),执行make modules,执行完你可以在/drivers/char目录下找到s3c24xx_leds.ko这个文件,把这个文件发送到开发板上去,可以在串口运行r z命令,接收文件,把文件拷贝到开发板上的/lib/module目录下,然后在开发板执行insmod s3c24xx_leds.ko,这个时候如果出现leds
initialized,说明加载成功 ,然后,建立设备文件,在开发板上执行mknod /dev/leds c
231 0,
好了我们开始写测试程序了,这个是在PC上写的,代码如下:
#include
#include
#include
#include
#define
IOCTL_LED_ON 0
#define
IOCTL_LED_OFF 1
void usage(char
*exename)
{
printf("Usage:/n");
printf(" %s /n",
exename);
printf(" led_no = 1, 2, 3 or 4/n");
}
int main(int argc,
char **argv)
{
unsigned int led_no;
int fd = -1;
if (argc != 3)
goto err;
fd = open("/dev/leds", 0); // 打开设备
if (fd < 0) {
printf("Can't open
/dev/leds/n");
return -1;
}
led_no = strtoul(argv[1], 0, 0) - 1; // 操作哪个LED?
if (led_no > 3)
goto err;
if (!strcmp(argv[2], "on")) {
ioctl(fd, IOCTL_LED_ON, led_no); // 点亮它
} else if (!strcmp(argv[2],
"off")) {
ioctl(fd, IOCTL_LED_OFF, led_no);
// 熄灭它
} else {
goto err;
}
close(fd);
return 0;
err:
if (fd > 0)
close(fd);
usage(argv[0]);
return -1;
}
保存文件名为led_test.c ,保存到任意一个单独的文件夹下,比如/tmp/led目录,然后写makefile,如下
CROSS=arm-linux-
all: led_test
led_test:
led_test.c
$(CROSS)gcc -o $@ led_test.c -static
clean:
@rm -rf led_test *.o
保存名Makefile ,保存到/tmp/led,然后在这个目录下执行make命令。会生成一个led_test文件,同样传输到开发板上,保存在开发板的/usr/bin目录下
这个时候在超级终端中输入 led_test 1 on 这个时候led应该会亮的,输入led_test 1 off 这个时候会灭的。如果你在使用 led_test这条命令的时候,出现了这样的错误:Permission denied,这是权限问题,用 chmod 777 led_test 就可以解决了。
实验到此结束。希望能给你们帮助。
我的邮箱 wangdongxu@handson.cc