嵌入式Linux设备驱动程序开发指南9(平台设备驱动)——读书笔记

九、平台设备驱动

9.1 平台设备驱动概述

在嵌入式系统中,设备通常并不通过总线连接,将字符设备转为平台设备。在Linux系统中有一类特殊总线被构建被称为平台总线,平台设备驱动是被静态枚举的,而非动态发现。

驱动定义了platform_driver数据结构使用举例:
/* Define platform driver structure */
static struct platform_driver my_platform_driver = {
	.probe = my_probe,
	.remove = my_remove,
	.driver = {
	.name = "hellokeys",
	.of_match_table = my_of_ids,
	.owner = THIS_MODULE,
	}
};

...

platform_driver_register(&my_platform_driver)

修改设备树,目录如下:

/arch.arn.boot/dts/

添加内容如下:

    hellkeys{
					compatible = "arrow, hellokeys";
	};

hellokeys_sam.c平台设备代码:
定义了字符设备驱动、杂项字符设备驱动、平台设备驱动:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>

static int my_dev_open(struct inode *inode, struct file *file)
{
	pr_info("my_dev_open() is called.\n");
	return 0;
}

static int my_dev_close(struct inode *inode, struct file *file)
{
	pr_info("my_dev_close() is called.\n");
	return 0;
}

static long my_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	pr_info("my_dev_ioctl() is called. cmd = %d, arg = %ld\n", cmd, arg);
	return 0;
}

static const struct file_operations my_dev_fops = {
	.owner = THIS_MODULE,
	.open = my_dev_open,
	.release = my_dev_close,
	.unlocked_ioctl = my_dev_ioctl,
};

static struct miscdevice helloworld_miscdevice = {
		.minor = MISC_DYNAMIC_MINOR,
		.name = "mydev",
		.fops = &my_dev_fops,
};

/* Add probe() function */
static int __init my_probe(struct platform_device *pdev)
{
	int ret_val;
	pr_info("my_probe() function is called.\n");
	ret_val = misc_register(&helloworld_miscdevice);

	if (ret_val != 0) {
		pr_err("could not register the misc device mydev");
		return ret_val;
	}

	pr_info("mydev: got minor %i\n",helloworld_miscdevice.minor);
	return 0;
}

/* Add remove() function */
static int __exit my_remove(struct platform_device *pdev)
{
	pr_info("my_remove() function is called.\n");
	misc_deregister(&helloworld_miscdevice);
	return 0;
}

/* Declare a list of devices supported by the driver */
static const struct of_device_id my_of_ids[] = {
	{ .compatible = "arrow,hellokeys"},
	{},
};

MODULE_DEVICE_TABLE(of, my_of_ids);

/* Define platform driver structure */
static struct platform_driver my_platform_driver = {
	.probe = my_probe,
	.remove = my_remove,
	.driver = {
	.name = "hellokeys",
	.of_match_table = my_of_ids,
	.owner = THIS_MODULE,
	}
};

/* Register our platform driver */
module_platform_driver(my_platform_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR(" ");
MODULE_DESCRIPTION("This is the simplest platform driver");

测试调试:

insmod hellkey_imx.ko
find /sys -name "hellokeys"
ls -l /sys/bus/platform/drivers/hellokeys
ls -l /sys/module/hellykeys_imx/drivers
ls -l /sys/class/misc
ls -l /dev
rmmod hellkey_imx.ko

9.2 GPIO驱动

9.2.1 简介

在开发驱动时候,需要操作不同硬件,需要使用处理器外设寄存器,参考的文档有:

1、Applications Processor Reference Manial
2、SAMA5D2 Series Datasheet
3、Xplained Ultra User Guide
4、硬件原理图

9.2.2 硬件名称

焊点			:印在电路板上的裸片的特定表面,如D12焊点;
引脚复用			:单个引脚在内部进行复杂配置实现不同的功能;
逻辑/规范名称	:通常对应于焊点的首要功能;
网络标号			:描述功能线的实际用途;

9.2.3 引脚控制器

参阅datasheet.

9.2.4 引脚控制子系统

实现在driver/pinctl中,如pinctl-imx7d.c
设备树提供引脚、节点配置,pinctl驱动(/drivers/pinctl-imx.c)列出引脚和引脚组、配置引脚,gpio驱动实现gpiochip(driver/gpio)和irq_gpio(kernel/irq),pinctl子系统核心(请求引脚服用设备驱动)。

解析设备树的函数:

imx_pinctl_probe_dt()

引脚复用文件:

drivers/pinctl/pinmux.c

9.2.5 GPIO控制器驱动

GPIO控制器最主要数据结构是gpiochip,定义在:

include/linux/gpio/driver.h

GPIO控制器还提供了中断,头文件如下:

#include <linux/irq.h>

GPIO中断芯片通常分类:
1、链式GPIO中断芯片

chained_irq_enter()
generic_handle_irq()
chained_irq_exit()

2、通用链式GPIO中断芯片

generic_handle_irq()

3、嵌套的线程化GPIO中断芯片
片外GPIO扩展,

handle_nested_irq()

使用GPIO:

gpio_direction_input()
gpio_direction_output()
gpio_get_value()
gpio_set_value()

gpio_to_irq()    //获取给定的GPIO对应的Linux IO号。

内核态与用户态交换数据:
当进程执行系统调用时,内核将在调用者的进程上下文中执行;
当内核响应中断时,内核中断处理程序将异步运行在中断上下文。
1、单变量访问

get_user()    //kernel -> app
put_user()	  //app    -> kernel

2、数组访问

copy_to_user()    //kernel -> app	
copy_from_user()  //app    -> kernel

内存IO映射
外围设备的控制是通过操作其寄存器实现,设备具有多个寄存器,通过内存地址空间(MMIO)或者I/O地址空间(PIO)的连续地址来访问这些设备寄存器。
Linux驱动程序无法直接访问物理I/O地址,而是需要MMU映射
将I/O内存映射到虚拟内存方法:

ioremap()
iounmap()

#include <linux/io.h>

devm_ioremap()
devm_ioremap()

物理地址即外设寄存器地址,要映射到虚拟地址,SAMA5D2地址映射,如下图:
请添加图片描述

9.3 RGB LED平台设备模块

9.3.1 简介

通过控制几个LED灯,将多个Soc外设寄存器地址从物理地址映射到虚拟机地址,并且使用杂项框架为每个LED创建字符设备(misc),通过write和read来调用控制LED在内核态和用户态之间的数据交换。
SAMA5D2处理器有四组I/O,分别是PA\PB\PC\PD,PIO每条I/O线均具有常见的GPIO操作功能,如配置IO输入输出,配置上下拉,输入模式的触发模式,

9.3.2 设备树

/*
 * at91-sama5d2_xplained_common.dtsi - Device Tree file for SAMA5D2 Xplained board
 *
 *  Copyright (C) 2016 Atmel,
 *                2016 Nicolas Ferre <nicolas.ferre@atmel.com>
 *		  2016 Ludovic.Desroches <ludovic.desroches@atmel.com>
 *
 * This file is dual-licensed: you can use it either under the terms
 * of the GPL or the X11 license, at your option. Note that this dual
 * licensing only applies to this file, and not this project as a
 * whole.
 *
#include "sama5d2.dtsi"
//#include "at91-sama5d2_xplained_ov7670.dtsi"
#include "sama5d2-pinfunc.h"
#include <dt-bindings/mfd/atmel-flexcom.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/at91.h>
				
				pinctrl_led_gpio_default: led_gpio_default {
					pinmux = <PIN_PB0__GPIO>,
						 <PIN_PB5__GPIO>,
						 <PIN_PB6__GPIO>;
					bias-pull-up;
				};


/ {
	model = "Atmel SAMA5D2 Xplained";
	compatible = "atmel,sama5d2-xplained", "atmel,sama5d2", "atmel,sama5";

	chosen {
		stdout-path = "serial0:115200n8";
	};


	ledred {
		compatible = "arrow,RGBleds";
		label = "ledred";
		pinctrl-0 = <&pinctrl_led_gpio_default>;
	};

	ledgreen {
		compatible = "arrow,RGBleds";
		label = "ledgreen";
	};

	ledblue {
		compatible = "arrow,RGBleds";
		label = "ledblue";
	};

	ledclassRGB {
		compatible = "arrow,RGBclassleds";
		reg = <0xFC038000 0x4000>;	
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led_gpio_default>;
		status = "okay";

		red {
			label = "red";
		};

		green {
			label = "green";
		};

		blue {
			label = "blue";
			linux,default-trigger = "heartbeat";
		};
	};

9.3.3 ledRGB代码分析:

驱动包含的头文件:

#include <linux/module.h>
#include <linux/fs.h> /* struct file_operations */
/* platform_driver_register(), platform_set_drvdata() */
#include <linux/platform_device.h>
#include <linux/io.h> /* devm_ioremap(), iowrite32() */
#include <linux/of.h> /* of_property_read_string() */
#include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */
#include <linux/miscdevice.h> /* misc_register() */

定义寄存器掩码:

/* Declare masks to configure the different registers */
#define PIO_PB0_MASK (1 << 0) /* blue */
#define PIO_PB5_MASK (1 << 5) /* green */
#define PIO_PB6_MASK (1 << 6) /* red */
#define PIO_CFGR1_MASK (1 << 8) /* masked bits direction (output), no PUEN, no PDEN */
#define PIO_MASK_ALL_LEDS (PIO_PB0_MASK | PIO_PB5_MASK | PIO_PB6_MASK)

定义物理寄存器地址:

/* Declare physical addresses */
static int PIO_SODR1 = 0xFC038050;
static int PIO_CODR1 = 0xFC038054;
static int PIO_MSKR1 = 0xFC038040;
static int PIO_CFGR1 = 0xFC038044;

申明devm_iomem指针,用于存放devm_ioremap()返回的虚拟地址。

定义私有数据结构:
保存每个设备特定信息

/* declare a private structure */
struct led_dev
{
	struct miscdevice led_misc_device; /* assign device for each led */
	u32 led_mask; /* different mask if led is R,G or B */
	const char *led_name; /* assigned value cannot be modified */
	char led_value[8];
};

file_operation:

static const struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.read = led_read,
	.write = led_write,
};

misc杂项定义:

led_device->led_misc_device.fops = &led_fops;

led_write:
iowrite32()写寄存器。

static ssize_t led_write(struct file *file, const char __user *buff,
						 size_t count, loff_t *ppos)
{
	const char *led_on = "on";
	const char *led_off = "off";
	struct led_dev *led_device;

	pr_info("led_write() is called.\n");

	led_device = container_of(file->private_data,
				  struct led_dev, led_misc_device);

	/*
	 * terminal echo add \n character.
	 * led_device->led_value = "on\n" or "off\n after copy_from_user"
	 * count = 3 for "on\n" and 4 for "off\n"
	 */
	if(copy_from_user(led_device->led_value, buff, count)) {
		pr_info("Bad copied value\n");
		return -EFAULT;
	}

	/*
	 * Replace \n for \0 in led_device->led_value
	 * char array to create a char string
	 */
	led_device->led_value[count-1] = '\0';

	pr_info("This message is received from User Space: %s\n",
			led_device->led_value);

	/* compare strings to switch on/off the LED */
	if(!strcmp(led_device->led_value, led_on)) {
		iowrite32(led_device->led_mask, PIO_CODR1_W);
	}
	else if (!strcmp(led_device->led_value, led_off)) {
		iowrite32(led_device->led_mask, PIO_SODR1_W);
	}
	else {
		pr_info("Bad value\n");
		return -EINVAL;
	}

	pr_info("led_write() is exit.\n");
	return count;
}

申明与设备树匹配的compatible:

static const struct of_device_id my_of_ids[] = {
	{ .compatible = "arrow,RGBleds"},
	{},
};
MODULE_DEVICE_TABLE(of, my_of_ids);

static struct platform_driver led_platform_driver = {
	.probe = led_probe,
	.remove = led_remove,
	.driver = {
		.name = "RGBleds",
		.of_match_table = my_of_ids,
		.owner = THIS_MODULE,
	}
};

9.3.4 RGBled全部代码

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/leds.h>

#define PIO_SODR1_offset 0x50
#define PIO_CODR1_offset 0x54
#define PIO_CFGR1_offset 0x44
#define PIO_MSKR1_offset 0x40

#define PIO_PB0_MASK (1 << 0)
#define PIO_PB5_MASK (1 << 5)
#define PIO_PB6_MASK (1 << 6)
#define PIO_CFGR1_MASK (1 << 8)

#define PIO_MASK_ALL_LEDS (PIO_PB0_MASK | PIO_PB5_MASK | PIO_PB6_MASK)

struct led_dev
{
	u32 led_mask; /* different mask if led is R,G or B */
	void __iomem *base;
	struct led_classdev cdev;
};

static void led_control(struct led_classdev *led_cdev, enum led_brightness b)
{

	struct led_dev *led = container_of(led_cdev, struct led_dev, cdev);

	iowrite32(PIO_MASK_ALL_LEDS, led->base + PIO_SODR1_offset);

	if (b != LED_OFF)	/* LED ON */
		iowrite32(led->led_mask, led->base + PIO_CODR1_offset);
	else
		iowrite32(led->led_mask, led->base + PIO_SODR1_offset); /* LED OFF */
}

static int __init ledclass_probe(struct platform_device *pdev)
{

	void __iomem *g_ioremap_addr;
	struct device_node *child;
	struct resource *r;
	struct device *dev = &pdev->dev;
	int count, ret;

	dev_info(dev, "platform_probe enter\n");

	/* get our first memory resource from device tree */
	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!r) {
		dev_err(dev, "IORESOURCE_MEM, 0 does not exist\n");
		return -EINVAL;
	}
	dev_info(dev, "r->start = 0x%08lx\n", (long unsigned int)r->start);
	dev_info(dev, "r->end = 0x%08lx\n", (long unsigned int)r->end);

	/* ioremap our memory region */
	g_ioremap_addr = devm_ioremap(dev, r->start, resource_size(r));
	if (!g_ioremap_addr) {
		dev_err(dev, "ioremap failed \n");
		return -ENOMEM;
	}

	count = of_get_child_count(dev->of_node);
	if (!count)
		return -EINVAL;

	dev_info(dev, "there are %d nodes\n", count);

	/* Enable all leds and set dir to output */
	iowrite32(PIO_MASK_ALL_LEDS, g_ioremap_addr + PIO_MSKR1_offset);
	iowrite32(PIO_CFGR1_MASK, g_ioremap_addr + PIO_CFGR1_offset);

	/* Switch off all the leds */
	iowrite32(PIO_MASK_ALL_LEDS, g_ioremap_addr + PIO_SODR1_offset);

	for_each_child_of_node(dev->of_node, child){

		struct led_dev *led_device;
		struct led_classdev *cdev;
		led_device = devm_kzalloc(dev, sizeof(*led_device), GFP_KERNEL);
		if (!led_device)
			return -ENOMEM;

		cdev = &led_device->cdev;

		led_device->base = g_ioremap_addr;

		of_property_read_string(child, "label", &cdev->name);

		if (strcmp(cdev->name,"red") == 0) {
			led_device->led_mask = PIO_PB6_MASK;
			led_device->cdev.default_trigger = "heartbeat";
		}
		else if (strcmp(cdev->name,"green") == 0) {
			led_device->led_mask = PIO_PB5_MASK;
		}
		else if (strcmp(cdev->name,"blue") == 0) {
			led_device->led_mask = PIO_PB0_MASK;
		}
		else {
			dev_info(dev, "Bad device tree value\n");
			return -EINVAL;
		}

		/* Disable timer trigger until led is on */
		led_device->cdev.brightness = LED_OFF;
		led_device->cdev.brightness_set = led_control;

		ret = devm_led_classdev_register(dev, &led_device->cdev);
		if (ret) {
			dev_err(dev, "failed to register the led %s\n", cdev->name);
			of_node_put(child);
			return ret;
		}
	}
	
	dev_info(dev, "leds_probe exit\n");

	return 0;
}

static int __exit ledclass_remove(struct platform_device *pdev)
{

	dev_info(&pdev->dev, "leds_remove enter\n");
	dev_info(&pdev->dev, "leds_remove exit\n");

	return 0;
}

static const struct of_device_id my_of_ids[] = {
	{ .compatible = "arrow,RGBclassleds"},
	{},
};

MODULE_DEVICE_TABLE(of, my_of_ids);

static struct platform_driver led_platform_driver = {
	.probe = ledclass_probe,
	.remove = ledclass_remove,
	.driver = {
		.name = "RGBclassleds",
		.of_match_table = my_of_ids,
		.owner = THIS_MODULE,
	}
};

static int ledRGBclass_init(void)
{
	int ret_val;
	pr_info("demo_init enter\n");

	ret_val = platform_driver_register(&led_platform_driver);
	if (ret_val !=0)
	{
		pr_err("platform value returned %d\n", ret_val);
		return ret_val;

	}

	pr_info("demo_init exit\n");
	return 0;
}

static void ledRGBclass_exit(void)
{
	pr_info("led driver enter\n");

	platform_driver_unregister(&led_platform_driver);

	pr_info("led driver exit\n");
}

module_init(ledRGBclass_init);
module_exit(ledRGBclass_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR(" ");
MODULE_DESCRIPTION("This is a driver that turns on/off RGB leds \
		           using the LED subsystem");

测试调试:

insmod ledRGB_sam_platform.ko
ls /dev/led*
echo on > /dev/ledblue
echo on > /dev/ledred
echo on > /dev/ledgreen
echo off > /dev/ledgreen
cat /dev/ledred
rmmod ledRGB_sam_platform.ko

感谢阅读,祝君成功!
-by aiziyou

资源大于15MB分2次上传。 清晰度一般。加到11章 第12,13章没有。 第1章 嵌入式系统基础. 1.1 嵌入式系统简介 1.1.1 嵌入式系统定义 1.1.2 嵌入式系统与PC 1.1.3 嵌入式系统的特点 1.2 嵌入式系统的发展 1.2.1 嵌入式系统现状 1.2.2 嵌入式系统发展趋势 1.3 嵌入式操作系统与实时操作系统 1.3.1 Linux 1.3.2 uC/OS 1.3.3 Windows CE 1.3.4 VxWorks 1.3.5 Palm OS 1.3.6 QNX 1.4 嵌入式系统选型 第2章 基于ARM9处理器的硬件开发平台 2.1 ARM处理器简介 2.1.1 ARM公司简介 2.1.2 ARM微处理器核 .2.2 ARM9微处理器简介 2.2.1 与ARM7处理器的比较 2.2.2 三星S3C2410X处理器详解 2.3 FS2410开发平台 第3章 创建嵌入式系统开发环境 3.1 嵌入式Linux开发环境 3.2 Cygwin 3.3 虚拟机 3.4 交叉编译的预备知识 3.4.1 Make命令和Makefile文件 3.4.2 binutils工具包 3.4.3 gcc编译器 3.4.4 Glibc库 3.4.5 GDB 3.5 交叉编译 3.5.1 创建编译环境 3.5.2 编译binutils 3.5.3 编译bootstrap_gcc 3.5.4 编译Glibc 3.5.5 编译完整的gcc 3.5.6 编译GDB 3.5.7 成果 3.5.8 其他交叉编译方法 3.6 通过二进制软件包创建交叉编译环境 3.7 开发套件 第4章 调试嵌入式系统程序 4.1 嵌入式系统调试方法 4.1.1 实时在线仿真 4.1.2 模拟调试 4.1.3 软件调试 4.1.4 BDM/JTAG调试 4.2 ARM仿真器 4.2.1 techorICE ARM仿真器 4.2.2 ARM仿真器工作原理 4.2.3 ARM仿真器的系统功能层次 4.2.4 使用仿真器和ADS Debugger调试ARM开发板 4.3 JTAG接口 4.3.1 JTAG引脚定义 4.3.2 通过JTAG烧写Flash 4.3.3 烧写Flash技术内幕 第5章 Bootloader 5.1 嵌入式系统的引导代码 5.1.1 初识Bootloader 5.1.2 Bootloader的启动流程 5.2 Bootloader之vivi 5.2.1 vivi简介 5.2.2 vivi的配置与编译 5.2.3 vivi代码导读 5.3 Bootloader之U-Boot 5.3.1 U-Boot代码结构分析 5.3.2 编译U-Boot代码 5.3.3 U-Boot代码导读 5.3.4 U-Boot命令 5.4 FS2410的Bootloader 第6章 Linux系统在ARM平台的移植 6.1 移植的概念 6.2 Linux内核结构 6.3 Linux-2.4内核向ARM平台的移植 6.3.1 根目录 6.3.2 arch目录 6.3.3 arch/arm/boot目录 6.3.4 arch/arm/def-configs目录 6.3.5 arch/arm/kernel目录 6.3.6 arch/arm/mm目录 6.3.7 arch/arm/mach-s3c2410目录 6.4 Linux-2.6内核向ARM平台的移植 6.4.1 定义平台和编译器 6.4.2 arch/arm/mach-s3c2410/devs.c 6.4.3 arch/arm/mach-s3c2410/mach-fs2410.c 6.4.4 串口输出 6.5 编译Linux内核 6.5.1 代码成熟等级选项 6.5.2 通用的一些选项 6.5.3 和模块相关的选项 6.5.4 和块相关的选项 6.5.5 和系统类型相关的选项 6.5.6 和总线相关的选项 6.5.7 和内核特性相关的选项 6.5.8 和系统启动相关的选项 6.5.9 和浮点运算相关的选项 6.5.10 用户空间使用的二进制文件格式的选项 6.5.11 和电源管理相关的选项 6.5.12 和网络协议相关的选项 6.5.13 和设备驱动程序相关的选项 6.5.14 和文件系统相关的选项 6.5.15 和程序性能分析相关的选项 6.5.16 和内核调试相关的选项 6.5.17 和安全相关的选项 6.5.18 和加密算法相关的选项 6.5.19 库选项 6.5.20 保存内核配置 第7章 Linux设备驱动程序开发 7.1 设备驱动概述 7.1.1 设备驱动和文件系统的关系 7.1.2 设备类型分类 7.1.3 内核空间和用户空间.. 7.2 设备驱动基础 7.2.1 设备驱动中关键数据结构 7.2.2 字符设备驱动开发 第8章 网络设备驱动程序开发 8.1 网络设备驱动程序简介 8.1.1 device数据结构 8.1.2 sk_buff数据结构 8.1.3 内核的驱动程序接口 8.2 以太网控制器CS8900A 8.2.1 特性 8.2.2 工作原理 8.2.3 电路连接 8.2.4 引脚 8.2.5 操作模式 8.3 网络设备驱动程序实例 8.3.1 初始化函数 8.3.2 打开函数 8.3.3 关闭函数 8.3.4 发送函数 8.3.5 接收函数 8.3.6 中断处理函数 第9章 USB驱动程序开发 9.1 USB驱动程序简介 9.1.1 USB背景知识 9.1.2 Linux内核对USB规范的支持 9.1.3 OHCI简介 9.2 Linux下USB系统文件结点 9.3 USB主机驱动结构 9.3.1 USB数据传输时序 9.3.2 USB设备连接/断开时序 9.4 主要数据结构及接口函数 9.4.1 数据传输管道 9.4.2 统一的USB数据传输块 9.4.3 USBD数据描述 9.4.4 USBD与HCD驱动程序接口 9.4.5 USBD层的设备管理 9.4.6 设备驱动与USBD接口 9.5 USBD文件系统接口 9.5.1 设备驱动程序访问 9.5.2 设备拓扑访问 9.5.3 设备信息访问 9.6 设备驱动与文件系统接口 9.7 USB HUB驱动程序 9.7.1 HUB驱动初始化 9.7.2 HUB Probe相关函数 9.8 OHCI HCD实现 9.8.1 OHCI驱动初始化 9.8.2 与USBD连接 9.8.3 OHCI根HUB 9.9 扫描仪设备驱动程序 9.9.1 USBD接口 9.9.2 文件系统接口 9.10 USB主机驱动在S3C2410X平台的实现 9.10.1 USB主机控制器简介 9.10.2 驱动程序的移植 第10章 图形用户接口 10.1 嵌入式系统中的GUI简介 10.1.1 MicroWindows 10.1.2 MiniGUI 10.1.3 Qt/Embedded 10.2 MiniGUI编程 10.2.1 MiniGUI移植 10.2.2 MiniGUI编程 10.3 初识Qt/Embedded 10.3.1 Qt介绍 10.3.2 系统要求 10.3.3 Qt的架构 10.4 Qt/Embedded嵌入式图形开发基础 10.4.1 建立Qt/Embedded 开发环境 10.4.2 认识Qt/Embedded开发环境 10.4.3 窗体 10.4.4 对话框 10.4.5 外形与感觉 10.4.6 国际化 10.5 Qt/Embedded实战演练 10.5.1 安装Qt/Embedded工具开发包 10.5.2 交叉编译Qt/Embedded库 10.5.3 Hello,World 10.5.4 发布Qt/Embedded程序到目标板 10.5.5 添加一个Qt/Embedded应用到QPE 第11章 Java虚拟机的移植 11.1 Java虚拟机概述 11.1.1 Java虚拟机的概念 11.1.2 J2ME 11.1.3 KVM 11.2 Java虚拟机的移植 11.2.1 获得源码 11.2.2 编译环境的建立 11.2.3 JDK的安装 11.2.4 KVM的移植及编译 11.2.5 KVM的测试 11.3 其他可选的虚拟机 11.4 性能优化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack.Jia

感谢打赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值