zoukankan      html  css  js  c++  java
  • 嵌入式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_EMERG
    
    static int hello_init(void){
    	printk(KERN_EMERG "hello world enter
    ");
    	return 0;
    }
    
    void hello_exit(void){
    	printk(KERN_EMERG "hello world exit
    ");
    }
    
    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.o
    all:
    	make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) modules
    clean:
    	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 true
    do
        sudo dmesg -c
        sleep 1
    done

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

     

     


    Section TWO:LED驱动程序


    思维导图设计

    2.1 QT210 开发板 LED驱动程序和测试程序

    驱动文件:hello.c

    /************************************************
     LED的驱动,在Real210A开发板上做测试
     
    维护记录:  2011-10-31  V1.0    
     
    linux内核:2.6.35.7
     
    驱动用法:
             设备名称:Real210-led
             点亮一个灯:LED_ON
             熄灭一个灯:LED_OFF
             点亮所有灯:ALL_LED_ON
             熄灭所有灯:ALL_LED_OFF
     *************************************************/
     #include<linux/init.h>
     #include<linux/module.h>
     
     #include <linux/kernel.h>
     #include <linux/fs.h>
     #include <linux/delay.h>
     #include <asm/irq.h>
     #include <mach/regs-gpio.h>
     #include <mach/hardware.h>
     #include <linux/device.h>
     #include <linux/gpio.h>
     
     #define DEVICE_NAME    "Real210-led"    /* 设备名称 */        
     static int LED_Major = 0;            /* 主设备号 ,系统自动分配*/
     
     #define LED_OFF             0
     #define LED_ON             1
     #define ALL_LED_OFF      3
     #define ALL_LED_ON       4
     
    /* 用来指定LED所用的GPIO引脚 */
     static unsigned long led_table [] =
     {
         //S5PV210_GPH0(_nr);
         //在头文件“~/kernel/arch/arm/mach-s5pv210/include/mach/”
     
         S5PV210_GPH0(6),
         S5PV210_GPH0(7),
         S5PV210_GPH0(4),
         S5PV210_GPH0(5),
     };
     
    static int Real210_led_open(struct inode *inode, struct file *file)
     {
     //    MOD_INC_USE_COUNT;
         printk("Real210-LED Driver Open Called!
    ");
         return 0;
     }
     
    static int Real210_led_release(struct inode *inode, struct file *file)
     {
     //    MOD_DEC_USE_COUNT;
         printk("Real210-LED Driver Release Called!
    ");
         return 0;
     }
     
    static int Real210_led_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
     {
         int i;
         if (arg > 4)
         {
             return -EINVAL;
         }
         switch(cmd)
         {
             case LED_ON:  //set the pin
                 gpio_set_value (led_table[arg], 0);
                 break;
     
             case LED_OFF:  //clr the pin
                 gpio_set_value (led_table[arg], 1);
                 break;
                 
             case ALL_LED_ON:  //set all pin
                 for (i = 0; i < 4; i++)
                     gpio_set_value (led_table[i], 0);
                 break;
                 
             case ALL_LED_OFF:  //clr all pin
                 for (i = 0; i < 4; i++)
                     gpio_set_value (led_table[i], 1);
                 break;
     
             default:
                 return -EINVAL;
         }
     }
     
    static struct file_operations Real210_led_fops =
     {
         .owner  =   THIS_MODULE,
         .open   =   Real210_led_open, 
         .release =  Real210_led_release,
         .ioctl  =   Real210_led_ioctl,
     };
     
    static struct class *led_class;
     
    static int __init Real210_led_init(void)
     {
     
        printk("Real210 LED DRIVER MODULE INIT
    ");
     
        LED_Major = register_chrdev(0, DEVICE_NAME, &Real210_led_fops);
         if (LED_Major < 0)
         {
             printk(DEVICE_NAME " can't register major number
    ");
             return LED_Major;
         }
         printk("register Real210-LED Driver OK! Major = %d
    ", LED_Major);
     
         led_class = class_create(THIS_MODULE, DEVICE_NAME);
         if(IS_ERR(led_class))
         {
             printk("Err: failed in Real210-LED class. 
    ");
             return -1;
         }
     
         device_create(led_class, NULL, MKDEV(LED_Major, 0), NULL, DEVICE_NAME);
     
         //IO初始化
         
         //IO方向配置
         gpio_direction_output (S5PV210_GPH0(6), 1);
         gpio_direction_output (S5PV210_GPH0(7), 1); 
         gpio_direction_output (S5PV210_GPH0(4), 1); 
         gpio_direction_output (S5PV210_GPH0(5), 1);  
         //IO初始化
         gpio_set_value (S5PV210_GPH0(6), 1); 
         gpio_set_value (S5PV210_GPH0(7), 0); 
    
         printk(DEVICE_NAME " initialized
    ");
         return 0;
     }
     
    static void __exit Real210_led_exit(void)
     {
         printk("Real210 LED DRIVER MODULE EXIT
    ");
         unregister_chrdev(LED_Major, DEVICE_NAME);
         device_destroy(led_class, MKDEV(LED_Major, 0));
         class_destroy(led_class);
     }
     
     module_init(Real210_led_init);
     module_exit(Real210_led_exit);
     
     //MODULE_LICENSE("Dual BSD/GP");
     
     MODULE_LICENSE("GPL");
     MODULE_AUTHOR("wzl");
     MODULE_DESCRIPTION("This is an example of hello drivers");
     MODULE_ALIAS("A simplest module.");

     

    Makefile文件:

    obj-m := hello.o 
    KDIR := /home/work/QT210/qt210_ics_kernel3.0.8 
    all:
    	make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-eabi- 
    clean:
    	rm -f *.ko *.o *.mod.o *.mod.c *.symvers

     

    测试文件:led.c

     #include<stdio.h>
     #include<stdlib.h>
     #include<unistd.h>
     #include <sys/ioctl.h>
     
     int main(int argc, char **argv)
     {
         unsigned int on;
         unsigned int led_num;
         int fd;
         printf("Enter the test led !
    ");
         fd = open("/dev/Real210-led", 0);
         if (fd < 	0)
         {
             perror("open device led");
             exit(1);
         }
     
    
        ioctl(fd, 1, 0);    //可修改本句代码
     
    
        close(fd);
         return 0;
     }

     

    Makefile文件:

    all: led
    led:led.c
         arm-none-linux-gnueabi-gcc -o led led.c -static
         arm-none-linux-gnueabi-strip led
    clean:
         @rm -vf led *.o *~

     

    下面是调试信息:

    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流水灯 驱动程序

    water_led.c的代码

    #include <linux/device.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <asm/uaccess.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <linux/workqueue.h>
    
    static struct delayed_work my_work;
    
    //static int watercount=0;
    volatile unsigned long *gph0con = NULL;
    volatile unsigned long *gph0dat = NULL;
    static void waterfun(void)
    {
    	static int watercount=0;
    	*gph0dat = 0x1 << ((watercount++)%4);//流水灯
    	//printk("waterfun:watercount:%d
    ",watercount);
    	schedule_delayed_work(&my_work,msecs_to_jiffies(1000));
    }
    
    static int __init waterinit(void)
    {
    
    	int ret;
    	gph0con = (volatile unsigned long *)ioremap(0xE0200c00, 16);
    	gph0dat = gph0con + 1;
    	*gph0con |=0x1111 ;
    	*gph0con &=~0xf;
    	INIT_DELAYED_WORK(&my_work,waterfun);
    	ret = schedule_delayed_work(&my_work,msecs_to_jiffies(1000));
    	printk("water init:ret:%d
    ",ret);
    	
    	return 0;
    	
    	
    }
    static void __exit waterexit(void)
    {
    		cancel_delayed_work(&my_work);
    		//destroy_wor(my_work);
    		//_work(&my_work);
    		iounmap(gph0con);
    		
    }
    
    module_init(waterinit);
    module_exit(waterexit);
    MODULE_LICENSE("GPL");

    Makefile的代码:

    obj-m :=water_led.o
    
    #KDIR :=/home/kernal_driver/linux-tiny6410/
    KDIR :=/home/work/QT210/qt210_ics_kernel3.0.8
    PWD :=$(shell pwd)
    all:
    	$(MAKE) -C $(KDIR) M=$(PWD) modules 
    clean:
    	rm -f *.o *.bak *.order *.symvers

     

    可以看到开发板子的 D6 D7 D8呈现流水灯(D5无法控制),证明驱动成功了。

     

     

    2.3 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"
    cmd = %d  arg = %d 
    ", cmd, arg);    
    switch(cmd)    
        {    
    case 0:    
                printk(KERN_ALERT"led%d off
    ", 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
    ", 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
    ");    
                            *gph0dat |= 0xf;  
    break;    
    case 10:    
                printk(KERN_ALERT"led all off
    ");    
                            *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
    ","leds");    
    return -1;    
        }    
    else 
                printk(KERN_ALERT" lcd module OK!
    ");    
    
    return 0;    
    }    
    
    static void __exit led_exit(void)     
    {      
            unregister_chrdev(LED_MAJOR, "leds");    
        printk(KERN_ALERT"module exit
    ");    
    }    
    
    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!
    ");      
    
        ioctl(fd, on, led_nu);      
        LOGI("led_nu=%d,state=%d
    ", 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.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    tools:context=".LedActivity" > 
    
    <ToggleButton 
    android:id="@+id/btn1" 
    android:layout_width="140dip" 
    android:layout_height="wrap_content" 
    android:textOn="led1 on" 
    android:textOff="led1 off" 
    android:layout_gravity="center_horizontal" 
    /> 
    <ToggleButton 
    android:id="@+id/btn2" 
    android:layout_width="140dip" 
    android:layout_height="wrap_content" 
    android:textOn="led2 on" 
    android:textOff="led2 off" 
    android:layout_gravity="center_horizontal" 
    /> 
    <ToggleButton 
    android:id="@+id/btn3" 
    android:layout_width="140dip" 
    android:layout_height="wrap_content" 
    android:textOn="led3 on" 
    android:textOff="led3 off" 
    android:layout_gravity="center_horizontal" 
    /> 
    <ToggleButton 
    android:id="@+id/btn4" 
    android:layout_width="140dip" 
    android:layout_height="wrap_content" 
    android:textOn="led4 on" 
    android:textOff="led4 off" 
    android:layout_gravity="center_horizontal" 
    /> 
    
    </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

  • 相关阅读:
    CPU、io、mem之间的关系
    SSH交互式脚本StrictHostKeyChecking选项 benchmode=yes
    Linux学习笔记:fuser和lsof
    /proc/sys/kernel/sysrq /proc/sysrq-trigger----强制重启/触发器
    Android UI学习
    Android之TabHost布局(转)
    android AsyncTask介绍(转)
    android:imeOptions属性(转)
    Android:dimen尺寸资源文件的使用(转)
    解决Android解析图片的OOM问题!!!(转)
  • 原文地址:https://www.cnblogs.com/chsry/p/4781798.html
Copyright © 2011-2022 走看看