你的位置:首页 > 操作系统

[操作系统]嵌入式Linux驱动开发日记


嵌入式Linux驱动开发日记

主机硬件环境
开发机:虚拟机Ubuntu12.04
内存: 1G
硬盘:80GB

目标板硬件环境
CPU: SP5V210 (开发板:QT210)
SDRAM: 512M  (4片K4T1G164Q )
Nand flash: 512M (K9F4G08)
以太网芯片: SMSC LAN9220

工具介绍
仿真器: 暂无
电缆: 串口线,USB线

Windows 操作系统软件环境
ADS编译工具: 暂无
仿真器软件:暂无
调试软件: 终端(ADB)、eclipse

Linux操作系统软件环境
GNU交叉编译工具: arm-linux-gcc 4.4.1、JDK1.6、git 1.7、gcc 4.5、python 2.7


Section ONE:最简单的驱动程序:hello world


首先我们写一个最简单的模块。编辑hello_module.c

#include<linux/init.h>  //module_init(),module_exit()#include<linux/module.h> //MODULE_AUTHOR(),MODULE_LICENSE()#include<linux/kernel.h> //KERN_EMERGstatic int hello_init(void){	printk(KERN_EMERG "hello world enter\n");	return 0;}void hello_exit(void){	printk(KERN_EMERG "hello world exit\n");}module_init(hello_init);module_exit(hello_exit);MODULE_AUTHOR("CHSRY");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("A simple Hello World Module");MODULE_ALIAS("a simplest module");

编写Makefile 

obj-m := hello.oall:	make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) modulesclean:	make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) clean

运行#make

生成hello.ko,然后使用insmod 命令存入模块:insmod hello.ko

使用lsmod 就能看到你新插入的模块: lsmod | grep hello

使用rmmod移除模块:rmmod hello

最简单的驱动就完成了。

由于Linux内核的级别控制,导致printk打印的内容不一定都能从控制台正常输出

最好的办法是打开另外的一个终端,用一个终端不停地监视并且打印输出当前系统的日志信息:

在终端下输入:

while truedo  sudo dmesg -c  sleep 1done

这样这个终端就会每1秒查看当前系统的日志并清空。

 


Section TWO:LED驱动程序


思维导图设计

2.1 QT210 开发板 LED驱动程序和测试程序(还没补充,待补充……)

image_thumb

adb shell #Mkdir mydev #exit adb push d:\android_led.ko  /mydev adb shell #cd mydev #chmod 777 android_led.ko #insmod android_led.ko #lsmod //查看是否加载上了。卸载命令 rmmod android_led 不要加.ko # cat /proc/devices  //也可以查看设备号和设备名。 #ls -l /dev/myled   //同样。 此时myled 权限需要修改。 #chmod 777 /dev/myled

 

2.2 QT210 开发板 LED驱动程序和android应用程序

目的: 编写一个android应用程序来控制开发板上led灯的亮与灭.

编译环境: Ubuntu12.04

Android系统: android4.0 (linux3.0.8)

一.驱动

1. 查看原理图,QT210开发板上led D5, D6, D7, D8 对应引脚为EINT0, EINT1, EINT2, EINT3.

 

2. 根据底板上的EINT引脚,在核心板上找到与之相对应的引脚,最终对应到了GPIO的GPH0_0, GPH0_1, GPH0_2, GPH0_3

3.  接下来在三星 S5PV210芯片手册上找到相应寄存器.控制led灯实际就是控制相应寄存器.

           S5PV210_EVT1_Usermanual_20100218.pdf

4.  编写驱动文件led.c

#include <linux/module.h> #include <linux/kernel.h> //#include <linux/io.h> #include <asm/irq.h> #include <asm/io.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/miscdevice.h> #include <linux/pci.h> #include <linux/ioctl.h> #include <linux/init.h> #include <linux/delay.h> #define DEVICE_NAME "leds" //设备名(/dev/leds) #define LED_MAJOR 240  unsigned long *gph0con = NULL; unsigned long *gph0dat = NULL; int major;  static int led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  {    printk(KERN_ALERT"\ncmd = %d arg = %d \n", cmd, arg);  switch(cmd)    {  case 0:        printk(KERN_ALERT"led%d off\n", arg);  switch(arg)        {  case 0:                  *gph0dat &= ~0x01; break;  case 1:                  *gph0dat &= ~0x02; break;  case 2:                  *gph0dat &= ~0x04; break;  case 3:                  *gph0dat &= ~0x08; break;  default:  break;        }  break;  case 1:        printk(KERN_ALERT"led%d on\n", arg);  switch(arg)        {  case 0:                  *gph0dat |= 0x01; break;  case 1:                  *gph0dat |= 0x02; break;  case 2:                  *gph0dat |= 0x04; break;  case 3:                  *gph0dat |= 0x08; break;  default:  break;        }  break;  case 11:        printk(KERN_ALERT"led all on\n");              *gph0dat |= 0xf; break;  case 10:        printk(KERN_ALERT"led all off\n");              *gph0dat &= ~0xf; break;  default:  break;      }  return 0;  }  struct file_operations led_fops={    .owner = THIS_MODULE,    .unlocked_ioctl = led_ioctl,  };  static struct miscdevice misc = {    .minor = MISC_DYNAMIC_MINOR, //动态设备号   .name = DEVICE_NAME,    .fops = &led_fops,  };  static int __init led_init(void)  {  int rc;     gph0con = (unsigned long *)ioremap(0xE0200C00, 16);     gph0dat = (unsigned long *)ioremap(0xE0200C04, 8);     *gph0con &= ~0xffff;     *gph0con |= 0x1111;     *gph0dat &= ~0xf;     rc = misc_register(&misc);  if(rc<0)    {        printk(KERN_ALERT"register %s char dev error\n","leds");  return -1;    }  else       printk(KERN_ALERT" lcd module OK!\n");  return 0;  }  static void __exit led_exit(void)   {       unregister_chrdev(LED_MAJOR, "leds");    printk(KERN_ALERT"module exit\n");  }  module_init(led_init);  module_exit(led_exit);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("cw"); 

 

5. 编写led.c的Makefile. 这里将led驱动编译成模块的方式.(注意: 编写Makefile时,行的开头只能用Tab,不能用空格.)

KERNELDIR :=/home/share/210/android4.0/4.0/qt210_ics_kernel3.0.8 PWD :=$(shell pwd) modules:   $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install:   $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install obj-m:=led.o clean:   rm -rf *.o *~core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers

 

6. 编译

编译成功后生成led.ko模块.

7. 将led.ko 拷贝到开发板上,半加载.加载之后会在/dev/目录下生成leds这个设备.

二. Android应用程序

1.  新建一个Android应用程序

2. 打开 src/LedActivity.java, 添加public static native int led_ioctl(int i, int j);

添加一个接口是为了利用java来生成jni的头文件.       src/LedActivity.java:

package com.example.led; import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class LedActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);     setContentView(R.layout.activity_led);   } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present.     getMenuInflater().inflate(R.menu.led, menu); return true;   } public static native int led_ioctl(int i, int j); } 

3. 编译整个android项目,将整个Led项目文件拷贝到ubuntu上.

             新建一个jni目录

4. 利用java文件自动生成jni头文件.

javah -classpath bin/classes -d jni com.example.led.LedActivity

5. jni目录下编写led.c 即led测试程序led.c.

Led/jni/led.c:

#include <jni.h> #include <stdio.h>   #include <stdlib.h>   #include <fcntl.h> #include <unistd.h>   #include <sys/ioctl.h>   #include <android/log.h> #define LOG_TAG "LED"    //android logcat #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__  ) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS_  _) //int main(int argc, char **argv)  jint JNICALL Java_com_example_led_LedActivity_led_1ioctl(JNIEnv *env, jclass thiz, jint led_nu, jint on)   {   int fd;     fd = open("/dev/leds", O_RDWR);  if(fd < 0)     printf("Can't open /dev/leds!\n");     ioctl(fd, on, led_nu);     LOGI("led_nu=%d,state=%d\n", led_nu, on);    close(fd);   return 0;   } 

6.  jni目录下编写Android.mk

Led/jni/Android.mk

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := Led_ctrl LOCAL_SRC_FILES := led.c LOCAL_LDLIBS := -llog LOCAL_C_INCLUDES := $(MY_ANDROID_SOURCE)/frameworks/base/core/jni/android/graphics \   $(MY_ANDROID_SOURCE)/external/skia/include/core \   $(MY_ANDROID_SOURCE)/external/skia/include/images \   $(MY_ANDROID_SOURCE)/frameworks/base/include \   $(MY_ANDROID_SOURCE)/system/core/include include $(BUILD_SHARED_LIBRARY) 

7.  Led目录下运行ndk-bluild, 将led.c文件编译成so库文件.

前提是你已经安装了android-ndk 工具(http://blog.csdn.net/colwer/article/details/8944166)

8.  将生成的libLed_ctrl.so拷贝到eclipse下Led应用程序中的libs/armeabi目录(如果没有armeabi目录需手动创建一个)

9. 在应用程序布局文件中加入4个开关按钮,对应四个led开关,也可以再添加一个总开关来同时控制四个led.

res/layout/activity_led.

<LinearLayout  

10.  编写java文件.

package com.example.led; import android.os.Bundle; import android.app.Activity; import android.util.Log; import android.view.Menu; import android.view.View; import android.widget.Button; import android.widget.ToggleButton; public class LedActivity extends Activity { private static final String TAG = "LED";  private ToggleButton button1;  private ToggleButton button2;  private ToggleButton button3;  private ToggleButton button4; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);     setContentView(R.layout.activity_led);     button1 = (ToggleButton)findViewById(R.id.btn1);       button2 = (ToggleButton)findViewById(R.id.btn2);       button3 = (ToggleButton)findViewById(R.id.btn3);      button4 = (ToggleButton)findViewById(R.id.btn4);      button1.setOnClickListener(new Button.OnClickListener()      {  public void onClick(View v)        {  if (button1.isChecked())             led_ioctl(0, 1);  //led1 on else           led_ioctl(0, 0); //led1 off       }      });       button2.setOnClickListener(new Button.OnClickListener()      {  public void onClick(View v)        {  if (button2.isChecked())             led_ioctl(1, 1); //led2 on else           led_ioctl(1, 0); //led2 off       }      });        button3.setOnClickListener(new Button.OnClickListener()      {  public void onClick(View v)        {  if (button3.isChecked())             led_ioctl(2, 1); //led3 on else           led_ioctl(2, 0); //led3 off       }      });        button4.setOnClickListener(new Button.OnClickListener()      {  public void onClick(View v)        {  if (button4.isChecked())             led_ioctl(3, 1); //led4 on else           led_ioctl(3, 0); //led4 off       }      });    } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present.     getMenuInflater().inflate(R.menu.led, menu); return true;   } public static native int led_ioctl(int i, int j); static   {     System.loadLibrary("Led_ctrl");   //  libs/armeabi/libLed_ctrl.so   } } 

编译生成Led.apk,并安装到开发板上.

至此,整个工作已经完成,开发板上打开应用程序就可以控制led了.但有几点需要注意

    1. 开发板上D5这个灯已经被占用了,所以led1不能控制D5了.

    2. 记住运行应用程序前确保内核中led.ko已被加载,并修改 /dev/leds的权限,否则led灯不受控制.

代码下载地址:http://download.csdn.net/detail/colwer/5403281