做一个简单的流水灯程序是写单片机或者是Linux嵌入式驱动中最简单最基础的东西了。
本人自学单片机,从事Linux网络开发,现在想转做嵌入式驱动方向,故从现在开始学习更新博客,记录自己的学习内容也当是一个学习笔记和大家一起分享。有什么不正确的地方希望大家给予留言。
开始:
首先要在自己的Linux源码中创建自己的模块文件。我使用的是Linux2.6.23版本。
在drivers下创建自己的文件名为myled。
然后创建myleds.c文件,内容如下:
/*开饭版采用的是友善之臂的s3c2440*/
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#define BASEADDR 0x56000010
#define LED_ON 0
#define LED_OFF 1
#define DEV_NAME "myled"
char *memaddr;
volatile unsigned long *gpbcon, *gpbdat, *gpbhup;
static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long pin);
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.ioctl = led_ioctl,
};
static struct __myled_tag__ {
dev_t devno;
struct cdev cdev;
} myled_t;
static int led_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long pin)
{
switch (cmd) {
case LED_ON:
*gpbdat &= ~(1 << pin);
break;
case LED_OFF:
*gpbdat |= 1 << pin;
break;
}
return 1;
}
//进行地址映射
static void dev_mem_init() {
memaddr = ioremap(BASEADDR, 0x0c);
gpbcon = (unsigned long *)memaddr;
gpbdat = (unsigned long *)(memaddr + 0x04);
gpbhup = (unsigned long *)(memaddr + 0x08);
}
static void set_led_register()
{
*gpbcon |= 0x55 << 10; //设置寄存器的管脚为output模式01.
}
static int __init dev_init(void) {
myled_t.devno = MKDEV(253, 1);
dev_mem_init();
cdev_init(&myled_t.cdev, &dev_fops);
myled_t.cdev.owner = THIS_MODULE;
if (register_chrdev_region(myled_t.devno, 1, DEV_NAME) != 0)
{
alloc_chrdev_region(&myled_t.devno, 1, 1, DEV_NAME);
}
cdev_add(&myled_t.cdev, myled_t.devno, 1);
set_led_register();
printk("myled install!\n");
printk("major; %d, minor: %d\n", MAJOR(myled_t.devno), MINOR(myled_t.devno));
return 0;
}
static void __exit dev_exit(void) {
unregister_chrdev_region(myled_t.devno, 1);
iounmap((void *)memaddr);
cdev_del(&myled_t.cdev);
printk("myled remove!\n");
}
程序是最基本的框架程序,没有什么难度,只是对于刚开始学时有些难度,主要是对源码的不熟悉,相信随着不断的学习是可以越来越熟练的。
以下是Makefile和Kconfig的设置,很多书上都是有说的
在myleds目录下添加Makefile和Kconfig这两个文件,
这是Kconfig
还有Makefile
obj-$(CONFIG_TEST_USER) += myleds.o
就是这么简单的一句
需要在上层目录的drivers目录下修改Makefile和Kconfig
在Makefile中添加:obj-$(CONFIG_TEST_SUPPORT) += myled/
在Kconfig中添加:source “drivers/myled/Kconfig”
然后编译内核模块:使用make modules命令;
在Linux源码的第一级目录下 make menuconfig设置myled模块为编译模块禁止编译进内核。然后使用make modules
在myleds目录下会生成myleds.ko文件。
将文件下载到开发板。
先使用mknod 文件目录 模块类型 主设备号 此设备号, 添加设备
mknod /dev/myled c 253 1 这个设备号是个前面的cdev_init()使用的设备号一致。
使用insmod myleds.ko加载模块,lsmod myleds 查看模块,rmmod myleds卸载模块,modprode myleds是否加载。
以下是应用程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
int pin;
int cmd;
int fd;
int i;
/*打开/dev/leds 设备文件*/
fd = open("/dev/myled", O_RDWR);
if (fd < 0) {
perror("open device myled");
exit(1);
}
/*通过系统调用 ioctl 和输入的参数控制 led*/
while (1) {
for (i = 5; i < 9; i++) {
ioctl(fd, 0, i);
usleep(500000);
}
for (i = 5; i < 9; i++)
ioctl(fd, 1, i);
for (i = 8; i > 4; i--) {
ioctl(fd, 0, i);
usleep(500000);
}
for (i = 5; i < 9; i++)
ioctl(fd, 1, i);
}
/*关闭设备句柄*/
close(fd);
return 0;
}
有点晚了想睡觉,等明天起来再补充一个对相关函数的理解和我在学习中遇到的一些困难
对于如何搭建开发环境和编译内核很多书上都有很详细的说明。在这里就不赘述了。
下一步将和大家一起学习makefile的相关语法,机器汇编指令。