第三章第九節 LCD觸摸移植
打開mach-ok6410.c:
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/arch/arm/mach-s3c64xx$ vim mach-ok6410.c
在mach-ok6410.c中添加ts.h頭文件:
#include <mach/ts.h>
將dev-ts.c文件拷貝至/linux-3.8.3/arch/arm/mach-s3c64xx/目錄下:
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/arch/arm/mach-s3c64xx$ ll dev-ts.c
-rwxr-xr-x 1 zhuzhaoqi zhuzhaoqi 1552 2013-04-14 00:14 dev-ts.c*
同時在dev-ts.c文件中添加:
#include <linux/gfp.h>
將ts.h文件拷貝至/linux-3.8.3/arch/arm/mach-s3c64xx/include/mach目錄下:
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/arch/arm/mach-s3c64xx/include/mach$ ll ts.h
-rwxr-xr-x 1 zhuzhaoqi zhuzhaoqi 916 2013-04-14 00:15 ts.h*
在/linux-3.8.3/arch/arm/mach-s3c64xx/目錄下的Makefile文件中添加:
obj-$(CONFIG_TOUCHSCREEN_S3C) += dev-ts.o
進入mach-ok6410.c文件添加觸摸初始化數據:
static struct s3c_ts_mach_info s3c_ts_platform __initdata = {
.delay = 10000,
.presc = 49,
.oversampling_shift = 2,
.resol_bit = 12,
.s3c_adc_con = ADC_TYPE_2,
};
而且在mach-ok6410.c文件中添加初始化函數:
static void __init ok6410_machine_init(void)
{
……
#if 0
s3c24xx_ts_set_platdata(NULL);
#endif
s3c_ts_set_platdata(&s3c_ts_platform);
……
}
將s3c-ts.c文件拷貝至/linux-3.8.3/drivers/input/touchscreen/目錄下:
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/drivers/input/touchscreen$ ls s3c-ts.c
s3c-ts.c
在/linux-3.8.3/drivers/input/touchscreen/目錄下的Makefile添加:
obj-$(CONFIG_TOUCHSCREEN_S3C) += s3c-ts.o
在/linux-3.8.3/arch/arm/plat-samsung/include/plat/目錄下添加s3c-ts.c文件所需的ADC部分控制寄存器的宏定義:
/*----------------- Common definitions for S3C ----------------*/
/* The following definitions will be applied to S3C24XX, S3C64XX, S5PC1XX.
*/
/*-------------------------------------------------------------*/
#define S3C_ADCREG(x) (x)
#define S3C_ADCCON S3C_ADCREG(0x00)
#define S3C_ADCTSC S3C_ADCREG(0x04)
#define S3C_ADCDLY S3C_ADCREG(0x08)
#define S3C_ADCDAT0 S3C_ADCREG(0x0C)
#define S3C_ADCDAT1 S3C_ADCREG(0x10)
#define S3C_ADCUPDN S3C_ADCREG(0x14)
#define S3C_ADCCLRINT S3C_ADCREG(0x18)
#define S3C_ADCMUX S3C_ADCREG(0x1C)
#define S3C_ADCCLRWK S3C_ADCREG(0x20)
/* ADCCON Register Bits */
#define S3C_ADCCON_RESSEL_10BIT (0x0<<16)
#define S3C_ADCCON_RESSEL_12BIT (0x1<<16)
#define S3C_ADCCON_ECFLG (1<<15)
#define S3C_ADCCON_PRSCEN (1<<14)
#define S3C_ADCCON_PRSCVL(x) (((x)&0xFF)<<6)
#define S3C_ADCCON_PRSCVLMASK (0xFF<<6)
#define S3C_ADCCON_SELMUX(x) (((x)&0x7)<<3)
#define S3C_ADCCON_SELMUX_1(x) (((x)&0xF)<<0)
#define S3C_ADCCON_MUXMASK (0x7<<3)
#define S3C_ADCCON_RESSEL_10BIT_1 (0x0<<3)
#define S3C_ADCCON_RESSEL_12BIT_1 (0x1<<3)
#define S3C_ADCCON_STDBM (1<<2)
#define S3C_ADCCON_READ_START (1<<1)
#define S3C_ADCCON_ENABLE_START (1<<0)
#define S3C_ADCCON_STARTMASK (0x3<<0)
/* ADCTSC Register Bits */
#define S3C_ADCTSC_UD_SEN (1<<8)
#define S3C_ADCTSC_YM_SEN (1<<7)
#define S3C_ADCTSC_YP_SEN (1<<6)
#define S3C_ADCTSC_XM_SEN (1<<5)
#define S3C_ADCTSC_XP_SEN (1<<4)
#define S3C_ADCTSC_PULL_UP_DISABLE (1<<3)
#define S3C_ADCTSC_AUTO_PST (1<<2)
#define S3C_ADCTSC_XY_PST(x) (((x)&0x3)<<0)
/* ADCDAT0 Bits */
#define S3C_ADCDAT0_UPDOWN (1<<15)
#define S3C_ADCDAT0_AUTO_PST (1<<14)
#define S3C_ADCDAT0_XY_PST (0x3<<12)
#define S3C_ADCDAT0_XPDATA_MASK (0x03FF)
#define S3C_ADCDAT0_XPDATA_MASK_12BIT (0x0FFF)
/* ADCDAT1 Bits */
#define S3C_ADCDAT1_UPDOWN (1<<15)
#define S3C_ADCDAT1_AUTO_PST (1<<14)
#define S3C_ADCDAT1_XY_PST (0x3<<12)
#define S3C_ADCDAT1_YPDATA_MASK (0x03FF)
#define S3C_ADCDAT1_YPDATA_MASK_12BIT (0x0FFF)
在/Linux/linux-3.8.3/include/linux目錄下的interrupt.h文件添加:
#define IRQF_SAMPLE_RANDOM 0x00000040
並在/linux-3.8.3/drivers/input/touchscreen/目錄下的Kconfig添加LCD觸摸配置:
config TOUCHSCREEN_S3C
tristate "S3C touchscreen driver"
depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5P64XX || ARCH_S5PC1XX
default y
help
Say Y here to enable the driver for the touchscreen on the
S3C SMDK board.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called s3c_ts.
修改完成,執行make menuconfig,進行LCD觸摸配置:
Device Drivers --->
Input device support --->
<*> S3C touchscreen driver
System Type --->
- ADC common driver support
Device Drivers --->
Input device support --->
<*> Event interface
LCD的顯示和觸摸配置完成以後執行make uImage命令:
……
LD vmlinux.o
arch/arm/plat-samsung/built-in.o:(.data+0x878): multiple definition of `s3c_device_ts'
arch/arm/mach-s3c64xx/built-in.o:(.data+0x34e0): first defined here
……
出現多重定義錯誤,則在/linux-3.8.3/arch/arm/plat-samsung目錄下的devs.c註釋掉s3c_device_ts便可:
//--->zzq
#undef CONFIG_SAMSUNG_DEV_TS
//<---zzq
#ifdef CONFIG_SAMSUNG_DEV_TS
若是還有錯誤,則可根據錯誤追溯源頭進行修改。
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3$ make uImage
……
Image Name: Linux-3.8.3
Created: Sun Apr 14 01:47:28 2013
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 2156336 Bytes = 2105.80 kB = 2.06 MB
Load Address: 50008000
Entry Point: 50008040
Image arch/arm/boot/uImage is ready
將生成的uImage拷貝至tftp目錄下:
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/arch/arm/boot$ cp uImage /tftpboot/
使用tftp進行燒寫、調試內核。
zhuzhaoqi@zhuzhaoqi-desktop:/tftpboot$ ls
uImage
zhuzhaoqi@zhuzhaoqi-desktop:/tftpboot$ tftp
tftp>
啓動ok6410開發平臺,停在U-Boot燒寫內核:
zzq6410 >>> tftp 0x50008000 uImage
dm9000 i/o: 0x18000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 00:40:5c:26:0a:5b
operating at 100M full duplex mode
Using dm9000 device
TFTP from server 192.168.1.187; our IP address is 192.168.1.100
Filename 'uImage'.
Load address: 0x50008000
Loading: ######################################################################################################################################################################################################################################################################################################################################################################################################################################
done
Bytes transferred = 2156400 (20e770 hex)
燒寫完成以後啓動內核:
zzq6410 >>>bootm 0x50008000
## Booting kernel from Legacy Image at 50008000 ...
Image Name: Linux-3.8.3
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 2156336 Bytes = 2.1 MiB
Load Address: 50008000
Entry Point: 50008040
Verifying Checksum ... OK
XIP Kernel Image ... OK
OK
Starting kernel ...
Starting kernel ...
Uncompressing Linux... done, booting the kernel.
……
S3C_LCD clock got enabled :: 133.250 Mhz
LCD TYPE :: LTE480WV will be initialized
……
S3C Touchscreen driver, (c) 2008 Samsung Electronics
S3C TouchScreen got loaded successfully : 12 bits
input: S3C TouchScreen as /devices/virtual/input/input0
……
從串口的信息輸出可知LCD的顯示和觸摸驅動成功。
第四章第二節 字符設備驅動
Linux操做系統將全部的設備都會當作是文件,所以當咱們須要訪問設備時,都是經過操做文件的方式進行訪問。對字符設備的讀寫是以字節爲單位進行的。
對字符設備驅動程序的學習過程,主要以兩個具備表明性且在OK6410開發平臺可實踐性的字符驅動展開分析,分別爲:LED驅動程序、ADC驅動程序。
1.1.1 LED
驅動程序設計
爲了展示LED的裸板程序和基於Linux系統的LED驅動程序區別與減小難度梯度,在寫LED驅動程序以前頗有必要先看一下LED的裸板程序是怎樣設計的。
1. LED
裸板程序
OK6410開發平臺中有4個LED燈,原理圖如圖4. 1所示。
從圖4. 1中可知,4個LED是共陽鏈接,GPM0~GPM3分別控制着LED1~LED4。而GPMCON寄存器地址爲:0x7F008820;GPMDAT寄存器地址爲:0x7F008824。那麼GPM中3個寄存器宏定義爲:
/*===============================================================
**
基地址的定義
===============================================================*/
#define AHB_BASE (0x7F000000)
/****************************************************************
** GPX
的地址定義
****************************************************************/
#define GPX_BASE (AHB_BASE+0x08000)
……
/****************************************************************
** GPM
寄存器地址定義
****************************************************************/
#define GPMCON (*(volatile unsigned long *)(GPX_BASE +0x0820))
#define GPMDAT (*(volatile unsigned long *)(GPX_BASE +0x0824))
#define GPMPUD (*(volatile unsigned long *)(GPX_BASE +0x0828))
將GPM0~GPM3設置爲輸出功能:
/* GPM0,1,2,3
設爲輸出引腳 */
/*
**
每個GPXCON
的引腳有 4
位二進制進行控制
** 0000-
輸入 0001-
輸出
*/
GPMCON = 0x1111;
點亮LED1,則是讓GPM3~GPM0輸出:1110。
GPMDAT = 0x0e;
點亮LED3,則是讓GPM3~GPM0輸出:1011。
GPMDAT = 0x0b;
2. LED
驅動程序
有了LED裸板程序的基礎,移植到Linux系統LED驅動設備程序,難度也不會很大了。可是在Linux中,特別注意《s3c6410用戶手冊》提供的GPM寄存器地址不能直接用於Linux中。
通常狀況下,Linux系統中,進程的4GB( )內存空間被劃分紅爲兩個部分:用戶空間(3G)和內核空間(1G),大小分別爲0~3G和3~4G。如圖4. 2所示。
3~4G之間的內核空間中,從低地址到高地址依次爲:物理內存映射區、隔離帶、vmalloc虛擬內存分配區、隔離帶、高端內存映射區、專用頁面映射區。
用戶進程一般狀況下,只能訪問用戶空間的虛擬地址,不能訪問到內核空間。
每一個進程的用戶空間都是徹底獨立、互不相干的,用戶進程各自有不一樣的頁表。而內核空間是由內核負責映射,它並不會跟着進程改變,是固定的。內核空間地址有本身對應的頁表,內核的虛擬空間獨立於其餘程序。
在內核中,訪問IO內存以前,咱們只有IO內存的物理地址,這樣是沒法經過軟件直接訪問的,須要首先用ioremap()函數將設備所處的物理地址映射到內核虛擬地址空間(3GB~4GB)。而後,才能根據映射所獲得的內核虛擬地址範圍,經過訪問指令訪問這些IO內存資源。
通常來講,在系統運行時,外設的I/O內存資源的物理地址是已知的,由硬件的設計決定。可是CPU一般並無爲這些已知的外設I/O內存資源的物理地址預約義虛擬地址範圍,驅動程序並不能直接經過物理地址訪問I/O內存資源,而必須將它們映射到核心虛地址空間內(經過頁表),而後才能根據映射所獲得的核心虛地址範圍,經過訪內指令訪問這些I/O內存資源。Linux在io.h頭文件中聲明瞭函數ioremap(),用來將I/O內存資源的物理地址映射到核心虛地址空間(3GB-4GB)中,以下所示:
void * ioremap(unsigned long phys_addr, unsigned longsize,
unsignedlong flags);
iounmap函數用於取消ioremap()所作的映射,以下所示:
void iounmap(void *addr);
到這裏應該明白,像GPMCON(0x7F008820 )這個物理地址是不能直接操控的,必須經過映射到內核的虛擬地址中,才能進行操做。
如今開始設計第一個LED驅動程序。
字符驅動程序所要包含的頭文件主要位於include/linux及/arch/arm/mach-s3c64xx /include/mach目錄下,以下LED驅動程序所包含的頭文件:
/*
* head file
*/
//moudle.h
包含了大量加載模塊須要的函數和符號的定義
#include <linux/module.h>
//kernel.h
以便使用printk()
等函數
#include <linux/kernel.h>
//fs.h
包含經常使用的數據結構,如struct file
等
#include <linux/fs.h>
//uaccess.h
包含copy_to_user()
,copy_from_user()
等函數
#include <linux/uaccess.h>
//io.h
包含inl()
,outl()
,readl()
,writel()
等IO
操做函數
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/pci.h>
//init.h
來指定你的初始化和清理函數,例如:module_init(init_function)
、module_exit(cleanup_function)
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
//irq.h
中斷與併發請求事件
#include <asm/irq.h>
//
下面這些頭文件是IO
口在內核的虛擬映射地址,涉及IO
口的操做所必須包含
//#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/hardware.h>
#include <mach/map.h>
上面所列出的頭文件便是本次LED驅動程序說須要包含的頭文件。
#define DEVICE_NAME "led"
#define LED_MAJOR 240 /*
主設備號*/
這是LED驅動程序的驅動名稱和主設備號。
設備節點位於/dev目錄下,以下所示,例舉出了ubuntu系統/dev/vcs*的設備節點:
zhuzhaoqi@zhuzhaoqi-desktop:~$ ls -l /dev/vcs*
……
crw-rw---- 1 root tty 7, 7 2013-04-09 20:56 /dev/vcs7
crw-rw---- 1 root tty 7, 128 2013-04-09 20:56/dev/vcsa
……
/dev/vcs7設備節點的主設備號爲:7,次設備號爲:7;/dev/vcsa設備節點的主設備號爲:7,次設備號爲:128。
#define LED_ON 0
#define LED_OFF 1
這是LED燈打開或者關閉的宏定義,因爲OK6410開發平臺的4個LED是共陽鏈接,因此輸出1即爲熄滅LED,輸出0爲點亮LED。
字符驅動程序中實現了open、close、read、write等系統調用。
open函數指針的聲明位於fs.h的file_operations結構體中,以下所示:
struct file_operations {
……
int (*open) (struct inode *, struct file *);
……
};
在open函數指針的回調函數led_open()完成的任務是設置GPM的輸出模式。
static int led_open(struct inode *inode,struct file*file)
{
unsigned inti;
/*
設置GPM0~GPM3
爲輸出模式*/
for (i = 0;i < 4; i++)
{
s3c_gpio_cfgpin(S3C64XX_GPM(i),S3C_GPIO_OUTPUT);
printk("TheGPMCON %x is %x \n",i,s3c_gpio_getcfg(S3C64XX_GPM(i)) );
}
printk("Led open... \n");
return 0;
}
s3c_gpio_cfgpin()函數原型位於gpio-cfg.h中,以下:
extern int s3c_gpio_cfgpin(unsigned int pin, unsignedint to);
內核對這個函數是這樣註釋的:s3c_gpio_cfgpin()函數用於改變引腳的GPIO功能。參數pin是GPIO的引腳名稱,參數to是須要將GPIO這個引腳設置成爲的功能。
GPIO的名稱在arch/arm/mach-s3c6400/include/mach/gpio.h進行了宏定義:
/* S3C64XX GPIO number definitions. */
#define S3C64XX_GPA(_nr) (S3C64XX_GPIO_A_START + (_nr))
#define S3C64XX_GPB(_nr) (S3C64XX_GPIO_B_START + (_nr))
#define S3C64XX_GPC(_nr) (S3C64XX_GPIO_C_START + (_nr))
#define S3C64XX_GPD(_nr) (S3C64XX_GPIO_D_START + (_nr))
#define S3C64XX_GPE(_nr) (S3C64XX_GPIO_E_START + (_nr))
#define S3C64XX_GPF(_nr) (S3C64XX_GPIO_F_START + (_nr))
#define S3C64XX_GPG(_nr) (S3C64XX_GPIO_G_START + (_nr))
#define S3C64XX_GPH(_nr) (S3C64XX_GPIO_H_START + (_nr))
#define S3C64XX_GPI(_nr) (S3C64XX_GPIO_I_START + (_nr))
#define S3C64XX_GPJ(_nr) (S3C64XX_GPIO_J_START + (_nr))
#define S3C64XX_GPK(_nr) (S3C64XX_GPIO_K_START + (_nr))
#define S3C64XX_GPL(_nr) (S3C64XX_GPIO_L_START + (_nr))
#define S3C64XX_GPM(_nr) (S3C64XX_GPIO_M_START + (_nr))
#define S3C64XX_GPN(_nr) (S3C64XX_GPIO_N_START + (_nr))
#define S3C64XX_GPO(_nr) (S3C64XX_GPIO_O_START + (_nr))
#define S3C64XX_GPP(_nr) (S3C64XX_GPIO_P_START + (_nr))
#define S3C64XX_GPQ(_nr) (S3C64XX_GPIO_Q_START + (_nr))
S3C64XX_GPIO_M_START的定義以下:
enum s3c_gpio_number {
S3C64XX_GPIO_A_START= 0,
S3C64XX_GPIO_B_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_A),
S3C64XX_GPIO_C_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_B),
S3C64XX_GPIO_D_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_C),
S3C64XX_GPIO_E_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_D),
S3C64XX_GPIO_F_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_E),
S3C64XX_GPIO_G_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_F),
S3C64XX_GPIO_H_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_G),
S3C64XX_GPIO_I_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_H),
S3C64XX_GPIO_J_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_I),
S3C64XX_GPIO_K_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_J),
S3C64XX_GPIO_L_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_K),
S3C64XX_GPIO_M_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_L),
S3C64XX_GPIO_N_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_M),
S3C64XX_GPIO_O_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_N),
S3C64XX_GPIO_P_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_O),
S3C64XX_GPIO_Q_START= S3C64XX_GPIO_NEXT(S3C64XX_GPIO_P),
};
S3C64XX_GPIO_NEXT的定義:
#define S3C64XX_GPIO_NEXT(__gpio) \
((__gpio##_START)+ (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
宏定義一層一層不少,可是經過這個設置,能夠很方便得選擇想要的任何一個GPIO口進行操做。
GPIO功能設置位於在gpio-cfg.h中:
#define S3C_GPIO_SPECIAL_MARK (0xfffffff0)
#define S3C_GPIO_SPECIAL(x) (S3C_GPIO_SPECIAL_MARK | (x))
/* Defines for generic pin configurations */
#define S3C_GPIO_INPUT (S3C_GPIO_SPECIAL(0))
#define S3C_GPIO_OUTPUT (S3C_GPIO_SPECIAL(1))
#define S3C_GPIO_SFN(x) (S3C_GPIO_SPECIAL(x))
經過上面宏定義可知,GPIO的引腳功能有輸入、輸出、和你想要的任何能夠實現的功能設置,S3C_GPIO_SFN(x)這個函數便是經過設定x的值,實現任何存在功能的設置。若是要設置GPM0~GPM3爲輸出功能,則:
for (i = 0; i < 4; i++) {
s3c_gpio_cfgpin(S3C64XX_GPM(i),S3C_GPIO_OUTPUT);
}
經過這樣的操做,設置就顯得比較簡潔實用。
s3c_gpio_getcfg(S3C64XX_GPM(i))
這行代碼的做用是獲取GMP(argv)的當前值。這個函數的原型在include/linux/gpio.h中:
static inline void gpio_get_value(unsigned int gpio)
{
__gpio_get_value(gpio);
}
完成端口模式設定,接下來的程序是完成LED操做。在fs.h的file_operations結構體中,有unlocked_ioctl函數指針的聲明,以下:
struct file_operations {
……
long(*unlocked_ioctl) (struct file *,unsigned int,unsigned long);
……
};
unlocked_ioctl函數指針所要回調的函數led_ioctl()函數便是須要實現應用層對LED1~LED4的控制操做。
static long led_ioctl ( struct file *file, unsignedint cmd, \
unsigned long argv )
{
if (argv> 4) {
return-EINVAL;
}
printk("LED ioctl...\n");
/*獲取應用層的操做 */
switch(cmd){
/*若是是點亮LED(argv) */
case LED_ON:
gpio_set_value(S3C64XX_GPM(argv),0);
printk("LED ON \n");
printk("S3C64XX_GPM(i) = %x\n",gpio_get_value(S3C64XX_GPM(argv)) );
return0;
/*若是是熄滅LED(argv) */
caseLED_OFF:
gpio_set_value(S3C64XX_GPM(argv),1);
printk("LED OFF \n");
printk("S3C64XX_GPM(i) = %x \n",gpio_get_value(S3C64XX_GPM(argv)) );
return0;
default:
return-EINVAL;
}
}
本函數調用了GPIO端口值設定函數。
gpio_set_value(S3C64XX_GPM(argv),1);
這是設定GMP(argv)輸出爲1。函數的原型位於include/linux/gpio.h中:
static inline void gpio_set_value(unsigned int gpio,int value)
{
__gpio_set_value(gpio,value);
}
release函數指針所要回調的函數led_release()函數:
static int led_release(struct inode *inode,struct file*file)
{
printk("zhuzhaoqi >>> s3c6410_led release \n");
return0;
}
這是驅動程序的核心控制,各個函數指針所對應的回調函數:
struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.unlocked_ioctl = led_ioctl,
.release = led_release,
};
因爲Linux3.8.3內核中沒有ioctl函數指針,取而代之的是unlocked_ioctl函數指針實現對led_ioctl()函數的回調。
驅動程序的加載分爲靜態加載和動態加載,將驅動程序編譯進內核稱爲靜態加載,將驅動程序編譯成模塊,使用時再加載稱爲動態加載。動態加載模塊的擴展名爲:.ko,使用insmod命令進行加載,使用rmmod命令進行卸載。
static int __init led_init(void)
{
int rc;
printk("LEDinit... \n");
rc =register_chrdev(LED_MAJOR,"led",&led_fops);
if (rc< 0)
{
printk("register %s char dev error\n","led");
return -1;
}
printk("OK!\n");
return0;
}
__init修飾詞對內核是一種暗示,代表該初始化函數僅僅在初始化期間使用,在模塊裝載以後,模塊裝載器就會將初始化函數釋放掉,這樣就能將初始化函數所佔用的內存釋放出來以做他用。
當使用insmod命令加載LED驅動模塊時,led_init()初始化函數將被調用,向內核註冊LED驅動程序。
static void __exit led_exit(void)
{
unregister_chrdev(LED_MAJOR,"led");
printk("LED exit...\n");
}
__exit這個修飾詞告訴內核這個退出函數僅僅用於模塊卸載,而且僅僅能在模塊卸載或者系統關閉時被調用。
當使用rmmod命令卸載LED驅動模塊時,led_exit ()清除函數將被調用,向內核註冊LED驅動程序。
module_init(led_init);
module_exit(led_exit);
module_init和module_exit是強制性使用的,這個宏會在模塊的目標代碼中增長一個特殊的段,用於說明函數所在位置。若是沒有這個宏,則初始化函數和退出函數永遠不會被調用。
MODULE_LICENSE("GPL");
若是沒有聲明LICENSE,模塊被加載時,會給處理內核被污染(kernel taint)的警告。若是在zzq_led.c中沒有許可證(LICENSE),則會給出以下提示:
[YJR@zhuzhaoqi 3.8.3]# insmod zzq_led.ko
zzq_led: module license 'unspecified' taints kernel.
Disabling lock debugging due to kernel taint
Linux遵循GNU通用公共許可證(GPL),GPL是由自由軟件基金會爲GNU項目設計,它容許任何人對其從新發布甚至銷售。
固然,也許程序還會有驅動程序做者和描述信息:
MODULE_DESCRIPTION("OK6410(S3C6410) LEDDriver");
完成驅動程序的設計以後,將zzq_led.c驅動程序放置於/drivers/char目錄下,打開Makefile文件:
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/drivers/char$gedit Makefile
在Makefile中添加LED驅動:
obj-m += zzq_led.o
回到內核的根目錄執行make modules命令生成LED驅動模塊:
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3$ makemodules
……
CC [M] drivers/char/zzq_led.o
……
編譯完成以後在/drivers/char目錄下會生成zzq_led.ko模塊,將其拷貝到文件系統下面的/lib/modules/3.8.3(若是沒有3.8.3目錄,則創建)目錄下。
加載LED驅動模塊:
[YJR@zhuzhaoqi]\# cd lib/module/3.8.3/
[YJR@zhuzhaoqi]\# ls
zzq_led.ko
[YJR@zhuzhaoqi]\# insmod zzq_led.ko
LED init...
OK!
根據信息輸出可知加載zzq_led.ko驅動模塊成功。經過lsmod查看加載模塊:
[YJR@zhuzhaoqi]\# lsmod
zzq_led 1548 0 - Live 0xbf000000
在/dev目錄下創建設備文件,以下操做:
[YJR@zhuzhaoqi]\# mknod /dev/led c 240 0
是否創建成功,能夠查看/dev下的節點得知:
[YJR@zhuzhaoqi]\# ls /dev/l*
/dev/led /dev/log /dev/loop-control
說明LED設備文件已經成功創建。
3. LED
應用程序
驅動程序須要應用程序對其操控。程序以下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define LED_ON 0
#define LED_OFF 1
/*
* LED
操做說明信息輸出
*/
void usage(char *exename)
{
printf("How to use: \n");
printf(" %s <LED Number><on/off>\n", exename);
printf(" LED Number = 1,2, 3 or 4 \n");
}
/*
* 應用程序主函數
*/
int main(int argc, char *argv[])
{
unsigned intled_number;
if (argc !=3) {
gotoerr;
}
int fd =open("/dev/led",2,0777);
if (fd <0) {
printf("Can't open /dev/led \n");
return-1;
}
printf("open /dev/led ok ... \n");
led_number =strtoul(argv[1], 0, 0) - 1;
if(led_number > 3) {
gotoerr;
}
/* LED ON */
if(!strcmp(argv[2], "on")) {
ioctl(fd, LED_ON, led_number);
}
/* LED OFF*/
else if(!strcmp(argv[2], "off")) {
ioctl(fd, LED_OFF, led_number);
}
else {
gotoerr;
}
close(fd);
return 0;
err:
if (fd >0) {
close(fd);
}
usage(argv[0]);
return -1;
}
在main()函數中,涉及到了open()函數,其原型以下:
int open( const char* pathname,int flags, mode_t mode);
固然,不少open函數中的入口參數也是隻有2個,原型以下:
int open( const char* pathname, int flags);
第一個參數pathname是一個指向將要打開的設備文件途徑字符串。
第二個參數flags是打開文件所能使用的旗標,經常使用的幾種旗標有:
O_RDONLY
:
以只讀方式打開文件
。
O_WRONLY
:
以只寫方式打開文件
。
O_RDWR
:
以可讀寫方式打開文件。
上述三種經常使用的旗標是互斥使用,但可與其餘的旗標進行或運算符組合。
第三個參數mode是使用該文件的權限。好比777,755等。
經過這個應用程序實現對LED驅動程序的控制,爲了更加方便快捷編譯這個應用程序,爲其寫一個Makefile文件,以下所示:
#
交叉編譯鏈安裝路徑
CC = /usr/local/arm/4.4.1/bin/arm-linux-gcc
zzq_led_app:zzq_led_app.o
$(CC) -ozzq_led_appzzq_led_app.o
zzq_led_app.o:zzq_led_app.c
$(CC) -czzq_led_app.c
clean :
rm zzq_led_app.ozzq_led_app
執行Makefile以後會生成zzq_led_app可執行應用文件,以下:
zhuzhaoqi@zhuzhaoqi-desktop:~/LDD/linux-3.8.3/zzq_led$make
/usr/local/arm/4.4.1/bin/arm-linux-gcc -czzq_led_app.c
/usr/local/arm/4.4.1/bin/arm-linux-gcc -o zzq_led_appzzq_led_app.o
zhuzhaoqi@zhuzhaoqi-desktop:~/LDD/linux-3.8.3/zzq_led$ls
Makefile zzq_led_app zzq_led_app.c zzq_led_app.o zzq_led.c
將生成的zzq_led_app可執行應用文件拷貝到根文件系統的/usr/bin目錄下,執行應用文件,以下操做:
[YJR@zhuzhaoqi]\# ./zzq_led_app
How to use:
./zzq_led_app <LED Number><on/off>
LED Number =1, 2, 3 or 4
根據信息提示能夠進行對LED驅動程序的控制,點亮LED1,則以下:
[YJR@zhuzhaoqi]\# ./zzq_led_app 1 on
The GPMCON 0 is fffffff1
The GPMCON 1 is fffffff1
The GPMCON 2 is fffffff1
The GPMCON 3 is fffffff1
zhuzhaoqi >>> LED open...
LED ioctl...
LED ON
S3C64XX_GPM(i) = 0
LED release...
open /dev/led ok ...
此時能夠看到LED1點亮。
注:本節配套視頻位於光盤中「嵌入式Linux實用教程視頻」目錄下第四章01課(字符設備驅動之LED)。
第四章第三節 ADC驅動程序設計
A/D轉換便是將模擬量轉換爲數字量,在物聯網迅速發展的今天,做爲物聯網的感知前端傳感器也隨之迅速更新,壓力、溫度、溼度等衆多模擬信號的處理都須要涉及到A/D轉換,所以A/D驅動程序學習在嵌入式佔據着重要地位。
1. S3C6410的ADC控制寄存器簡介
S3C6410控制芯片自帶有4路獨立專用A/D轉換通道,如圖4. 3所示。
圖4. 3A/D轉換鏈接圖
透過三星公司提供的《s3c6410用戶手冊》可知,ADCCON爲ADC控制寄存器,地址爲:0x7E00 B0000。ADCCON的復位值爲:0x3FC4,即爲:0011 1111 1100 0100。
#define S3C_ADCREG(x) (x)
#define S3C_ADCCON S3C_ADCREG(0x00)
ADCCON控制寄存器具備16位,每一位都能經過賦值來實現其相對應的功能。
ADCCON[0]:ENABLE_START,A/D 轉換開始啓用。若是READ_START 啓用,這個值是無效的。ENABLE_START = 0,無行動;ENABLE_START = 1,A/D 轉換開始和該位被清理後開啓。ADCCON[0]的復位值爲0,即復位以後默認爲無行動。
#define S3C_ADCCON_NO_ENABLE_START (0<<0)
#define S3C_ADCCON_ENABLE_START (1<<0)
ADCCON[1]:READ_START,A/D 轉換開始讀取。READ_START = 0,禁用開始讀操做;READ_START = 1,啓動開始讀操做。ADCCON[1]的復位值爲0,禁用開始讀操做。
#define S3C_ADCCON_NO_READ_START (0<<1)
#define S3C_ADCCON_READ_START (1<<1)
ADCCON[2]:STDBM,待機模式選擇。STDBM = 0,正常運做模式;STDBM = 1,待機模式。ADCCON[2]的復位值爲1,待機模式。
#define S3C_ADCCON_RUN (0<<2)
#define S3C_ADCCON_STDBM (1<<2)
ADCCON[5:3]:SEL_MUX,模擬輸入通道選擇。SEL_MUX = 000,AIN0;SEL_MUX = 001,AIN1;SEL_MUX = 010,AIN2;SEL_MUX = 011,AIN3;SEL_MUX = 100,YM;SEL_MUX = 101,YP;SEL_MUX = 110,XM;SEL_MUX = 111,XP。ADCCON[5:3]的復位值爲000,選用AIN0通道。
#define S3C_ADCCON_RESSEL_10BIT_1 (0x0<<3)
#define S3C_ADCCON_RESSEL_12BIT_1 (0x1<<3)
#define S3C_ADCCON_MUXMASK (0x7<<3)
#define S3C_ADCCON_SELMUX(x) (((x)&0x7)<<3) //任意通道的選擇
ADCCON[13:6]:PRSCVL,ADC 預約標器值0xFF。數據值:5~255。ADCCON[13:6]的復位值爲1111 1111,即爲0xFF。
#define S3C_ADCCON_PRSCVL(x) (((x)&0xFF)<<6) // 任意值設定
#define S3C_ADCCON_PRSCVLMASK (0xFF<<6) //復位值
ADCCON[14]:PRSCEN,ADC預約標器啓動。PRSCEN = 0,禁用;PRSCEN = 0,啓用。ADCCON[14]的復位值爲0,禁用ADC預約標器。
#define S3C_ADCCON_NO_PRSCEN (0<<14)
#define S3C_ADCCON_PRSCEN (1<<14)
ADCCON[15]:ECFLG,轉換的結束標記(只讀)。ECFLG = 0,A/D 轉換的過程當中;ECFLG = 1,A/D 轉換結束。ADCCON[15]的復位值爲0,A/D 轉換的過程當中。
#define S3C_ADCCON_ECFLG_ING (0<<15)
#define S3C_ADCCON_ECFLG (1<<15)
ADCDAT0寄存器爲ADC 的數據轉換寄存器。地址爲:0x7E00B00C。
ADCDAT0[9:0]:XPDATA,X 座標的數據轉換(包括正常的ADC 的轉換數據值)。數據值: 0x000~0x3FF。
ADCDAT0[11:10]:保留。當啓用12位AD時做爲轉換數據值使用。
#define S3C_ADCDAT0_XPDATA_MASK (0x03FF)
#define S3C_ADCDAT0_XPDATA_MASK_12BIT (0x0FFF)
上面所介紹的是專用A/D轉換通道經常使用寄存器,LCD觸摸屏A/D轉換有另外A/D通道
2. ADC驅動程序
A/D轉化驅動因爲也屬於字符設備驅動,因此其程序設計流程和LED驅動大致一致。在linux-3.8.3/drivers/char目錄下新建zzqadc.c驅動文件,固然也可寫好以後在拷貝到linux-3.8.3/drivers/char目錄下。
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/drivers/char$ vim zzqadc.c
頭文件是必不可少的,A/D驅動程序所要包含的頭文件以下所示:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/regs-adc.h>
與LED驅動程序所包含的頭文件相比較,多了ADC專用的頭文件,如regs-adc.h,這個頭文件位於linux-3.8.3/arch/arm/plat-samsung/include/plat目錄下。
static void __iomem *base_addr;
static struct clk *adc_clock;
#define __ADCREG(name) (*(unsigned long int *)(base_addr + name))
自從linux-2.6.9版本開始便把__iomem加入內核,__iomem是表示指向一個I/O的內存空間。將__iomem加入linux,主要是考慮到驅動程序的通用性。因爲不一樣的CPU體系結構對I/O空間的表示可能不一樣,可是當使用__iomem時,就會忽略對變量的檢查,由於__iomem使用的是void。
#define S3C_ADCREG(x) (x)
#define S3C_ADCCON S3C_ADCREG(0x00)
#define S3C_ADCDAT0 S3C_ADCREG(0x0C)
/* ADC contrl */
#define ADCCON __ADCREG(S3C_ADCCON)
/* read the ADdata */
#define ADCDAT0 __ADCREG(S3C_ADCCON)
聲明ADC控制寄存器的地址。
/* The set of ADCCON */
#define S3C_ADCCON_ENABLE_START (1 << 0)
#define S3C_ADCCON_READ_START (1 << 1)
#define S3C_ADCCON_RUN (0 << 2)
#define S3C_ADCCON_STDBM (1 << 2)
#define S3C_ADCCON_SELMUX(x) ( ((x)&0x7) << 3 )
#define S3C_ADCCON_PRSCVL(x) ( ((x)&0xFF) << 6 )
#define S3C_ADCCON_PRSCEN (1 << 14)
#define S3C_ADCCON_ECFLG (1 << 15)
/* The set of ADCDAT0 */
#define S3C_ADCDAT0_XPDATA_MASK (0x03FF)
#define S3C_ADCDAT0_XPDATA_MASK_12BIT (0x0FFF)
根據上一小節對ADCCON和ADCDAT0的介紹,能夠很容易寫出上面宏定義。
在使用ADC以前,先得對ADC進行初始化設置,因爲OK6410開發平臺自帶的A/D電壓採樣電路選用的是AIN0通道。則這裏須要對進行AIN0初始化,初始化階段須要完成的事情爲:A/D 轉換開始和該位被清理後開啓、正常運做模式、模擬輸入通道選擇AIN0、ADC 預約標器值0xFF、ADC預約標器啓動。
/*
* AIN0 init
*/
static int adc_init(void)
{
ADCCON = S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(0xFF) | \
S3C_ADCCON_SELMUX(0x00) | S3C_ADCCON_RUN;
ADCCON |=S3C_ADCCON_ENABLE_START;
return 0;
}
open函數指針的實現函數adc_open():
/*
* open dev
*/
static int adc_open(struct inode *inode, struct file *filp)
{
adc_init();
return 0;
}
release函數指針的實現函數adc_release():
/*
* release dev
*/
static int adc_release(struct inode *inode,struct file *filp)
{
return 0;
}
read()函數指針的實現函數adc_read(),這個函數的做用是讀取ADC採樣數據。
/*
* adc_read
*/
static ssize_t adc_read(struct file *filp, char __user *buff,
size_t size, loff_t *ppos)
{
ADCCON |= S3C_ADCCON_READ_START;
/* check the adc Enabled ,The [0] is low*/
while(ADCCON & 0x01);
/* check adc change end */
while(!(ADCCON & 0x8000));
/* return the data of adc */
return (ADCDAT0 & S3C_ADCDAT0_XPDATA_MASK);
}
ADC驅動程序的核心控制部分:
static struct file_operations dev_fops =
{
.owner = THIS_MODULE,
.open = adc_open,
.release = adc_release,
.read = adc_read,
};
static struct miscdevice misc =
{
.minor = MISC_DYNAMIC_MINOR,
.name = 「zzqadc「,
.fops = &dev_fops,
};
加載insmod驅動程序以下所示:
static int __init dev_init()
{
int ret;
/* Address Mapping */
base_addr = ioremap(SAMSUNG_PA_ADC,0X20);
if(base_addr == NULL)
{
printk(KERN_ERR"failed to remap \n");
return -ENOMEM;
}
/* Enabld acd clock */
adc_clock = clk_get(NULL,"adc");
if(!adc_clock)
{
printk(KERN_ERR"failed to get adc clock \n");
return -ENOENT;
}
clk_enable(adc_clock);
ret = misc_register(&misc);
printk("dev_init return ret: %d \n", ret);
return ret;
}
加載insmod驅動程序,這裏使用到了ioremap()函數,在內核驅動程序的初始化階段,經過ioremap()函數將物理地址映射到內核虛擬空間;在驅動程序的mmap系統調用中,使用remap_page_range()函數將該塊ROM映射到用戶虛擬空間。這樣內核空間和用戶空間都能訪問這段被映射後的虛擬地址。
ioremap()宏定義在asm/io.h內:
#define ioremap(cookie,size) __ioremap(cookie,size,0)
__ioremap函數原型爲(arm/mm/ioremap.c):
void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned longflags);
phys_addr:要映射的起始的IO地址;
size:要映射的空間的大小;
flags:要映射的IO空間和權限有關的標誌。
該函數返回映射後的內核虛擬地址(3G-4G),接着即可以經過讀寫該返回的內核虛擬地址去訪問之這段I/O內存資源。
base_addr = ioremap(SAMSUNG_PA_ADC,0X20);
這行代碼便是將SAMSUNG_PA_ADC(0x7E00 B000)映射到內核,返回內核的虛擬地址給base_addr。
clk_get(NULL,"adc")能夠得到adc時鐘,每個外設都有本身的工做頻率,PRSCVL是A/D轉換器時鐘的預分頻功能時A/D時鐘的計算公式,A/D時鐘 = PCLK / (PRSCVL+1)。
注意:AD時鐘最大爲2.5MHZ而且應該小於PCLK的1/5。
adc_clock = clk_get(NULL,"adc");
即爲獲取adc的工做時鐘頻率。
ret = misc_register(&misc);
建立雜項設備節點。這裏使用到了雜項設備,雜項設備也是在嵌入式系統中用得比較多的一種設備驅動。在 Linux 內核的include/linux目錄下有miscdevice.h文件,要把本身定義的misc device從設備定義在這裏。實際上是由於這些字符設備不符合預先肯定的字符設備範疇,全部這些設備採用主編號10 ,一塊兒歸於misc device,其實misc_register就是用主標號10調用register_chrdev()的。也就是說,misc設備其實也就是特殊的字符設備,可自動生成設備節點。
(雜項設備結構體分析)
卸載rmmod驅動程序:
static void __exit dev_exit()
{
iounmap(base_addr);
/* disable ths adc clock */
if(adc_clock)
{
clk_disable(adc_clock);
clk_put(adc_clock);
adc_clock = NULL;
}
misc_deregister(&misc);
}
許可證聲明、做者信息、調用加載和卸載程序:
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhuzhaoqi
jxlgzzq@163.com");
module_init(dev_init);
module_exit(dev_exit);
在/linux-3.8.3/drivers/char目錄下的Makefile中添加:
obj-m += zzqadc.o
回到/linux-3.8.3根目錄下:
/home/zhuzhaoqi/Linux/linux-3.8.3# make modules
將/linux-3.8.3/drivers/char目錄下生成的zzqadc.ko拷貝到文件系統的/lib/module/3.8.3目錄中。
3. ADC應用程序
ADC應用程序也是相對簡單,打開設備驅動文件以後進行數據讀取便可。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fp,adc_data,i;
fp = open("/dev/zzqadc",O_RDWR);
if (fp < 0)
{
printf("open failed! \n");
}
printf("opened ... \n");
for ( ; ; i++)
{
adc_data = read(fp,NULL,0);
printf("Begin the NO. %d test... \n",i);
printf("adc_data = %d \n",adc_data);
printf("The Value = %f V \n" , ( (float)adc_data )* 3.3 / 1024);
printf("End the NO. %d test ...... \n \n",i);
sleep(1);
}
close(fp);
return 0;
}
因爲本次使用的A/D轉換是10位,數據轉換值即爲1024,而OK6410的參考電壓是3.3V,則A/D採集數據和電壓之間的轉換公式爲:(float)adc_data )* 3.3 / 1024。
爲ADC應用程序編寫Makefile:
CC = /usr/local/arm/4.4.1/bin/arm-linux-gcc
zzqadcapp:zzqadcapp.o
$(CC) -o zzqadcapp zzqadcapp.o
zzqadcapp.o:zzqadcapp.c
$(CC) -c zzqadcapp.c
clean :
rm zzqadcapp.o zzqadcapp
將生成的zzqadcapp應用文件拷貝到到跟文件系統/usr/bin文件夾下。
加載zzqadc.ko設備:
[YJR@zhuzhaoqi 3.8.3]# insmod zzqadc.ko
dev_init return ret: 0
[YJR@zhuzhaoqi]\# ls -l /dev/zzqadc
crw-rw---- 1 root root 10, 60 Jan 1 08:00 /dev/zzqadc
在/dev目錄下存在zzqadc設備節點,則說明ADC驅動加載成功。
執行ADC應用程序,電壓採樣以下所示:
[YJR@zhuzhaoqi]\# ./zzqadcapp
opened ...
……
Begin the NO. 10 test...
adc_data = 962
The Value = 3.100195 V
End the NO. 10 test ......
……
注:本節配套視頻位於光盤中「嵌入式Linux實用教程視頻」目錄下第四章02課(字符設備驅動之ADC)。
第五章第一節 Qt
編譯環境搭建
在進行Qt開發以前,創建Qt編譯環境、移植Qt是一個相當重要的步驟。
1.1.1 tslib
安裝
OK6410開發平臺在使用觸摸屏時,由於電磁噪聲的緣故,觸摸屏容易存在點擊不許確、有抖動等問題。tslib可以爲觸摸屏驅動得到的採樣提供諸如濾波、去抖、校準等功能,一般做爲觸摸屏驅動的適配層,爲上層的應用提供一個統一的接口。
在官方網站下載tslib-1.0.tar.bz2,地址:http://sourceforge.net/projects/tslib.berlios/files/。
將下載完成以後的tslib-1.0.tar.bz2存放在宿主機任意一個目錄下:
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/tslib$ ls
tslib-1.0.tar.bz2
將其解壓出來:
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/tslib$ tar jxvftslib-1.0.tar.bz2
解壓完成以後進入tslib-1.0目錄,以下所示:
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/tslib/tslib-1.0$ls
acinclude.m4 autogen.sh COPYING m4 plugins tests
AUTHORS ChangeLog etc Makefile.am tslib.pc.in
autogen-clean.sh configure.ac INSTALL NEWS src
安裝autoconf、automake、libtool:
root@zhuzhaoqi-desktop:/home/zhuzhaoqi#apt-get installautoconf
root@zhuzhaoqi-desktop:/home/zhuzhaoqi#apt-get installautomake
root@zhuzhaoqi-desktop:/home/zhuzhaoqi#apt-get installlibtool
因爲open函數的語法不符合最新的gcc,在/tests/ts_calibrate.c中加入open的第三個參數:
if ((calfile = getenv("TSLIB_CALIBFILE")) !=NULL) {
cal_fd= open (calfile, O_CREAT | O_RDWR,0777);
} else {
cal_fd= open ("/etc/pointercal", O_CREAT | O_RDWR,0777);
}
執行編譯:
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/tslib/tslib-1.0$./autogen.sh
……
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/tslib/tslib-1.0$./configure --prefix=/usr/local/tslib-1.0/ --host=arm-linux ac_cv_func_malloc_0_nonnull=yes--enable-inputapi=no
……
--prefix=/usr/local/tslib-1.0/這是安裝路徑,進行編譯、安裝。
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/tslib/tslib-1.0$make
……
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/tslib/tslib-1.0$sudomake install
安裝完成以後在/usr/local/目錄下有:
zhuzhaoqi@zhuzhaoqi-desktop:/usr/local$ ls
arm etc include lib qwt-6.0.2 sbin src
bin games info man qwt-6.0.2-arm share tslib-1.0
修改ts.conf:
zhuzhaoqi@zhuzhaoqi-desktop:/usr/local/tslib-1.0/etc$vim ts.conf
去掉module_rawinput前面的#,注意前面這個空格也得刪除,以下:
# Uncomment if you wish to use the linux input layerevent interface
module_raw input
將/usr/local/tslib-1.0/目錄下的全部文件拷貝到開發板,筆者是存放在usr/local/。以下:
zhuzhaoqi@zhuzhaoqi-desktop:/usr/local/tslib-1.0$ sudocp -r * /home/zhuzhaoqi/rootfs/usr/local/
在OK6410中設置tslib環境變量,在文件系統的/etc/profile中添加以下:
//
指定幀緩衝設備
export set TSLIB_FBDEVICE=/dev/fb0
//
指定觸摸屏設備節點
export set TSLIB_TSDEVICE=/dev/input/event0
//
指定TSLIB
配置文件的位置
export set TSLIB_CONFFILE=/usr/local/etc/ts.conf
//
指定觸摸屏校準文件 pintercal
的存放位置
export set TSLIB_CALIBFILE=/etc/pointercal
//
指定觸摸屏插件所在路徑
export set TSLIB_PLUGINDIR=/usr/local/lib/ts
//
設定控制檯設備爲 none
,不然默認爲 /dev/tty
,
export TSLIB_CONSOLEDEVICE=none
在測試觸摸屏以前,首先得保證在/dev目錄下有觸摸屏設備節點eventX:
[YJR@zhuzhaoqi]\# ls -l /dev/input/e*
crw-rw---- 1 root root 13, 64 Jan 1 08:00 /dev/input/event0
運行ts_calibrate:
[YJR@zhuzhaoqi]\# cd bin/
[YJR@zhuzhaoqi]\# ls
ts_calibrate ts_harvest ts_print ts_print_raw ts_test
[YJR@zhuzhaoqi]\# ./ts_calibrate
運行/usr/local/bin中的ts_calibrate進行校準,成功的話會出現界面,並點擊十字符號,完成後會生成/etc/pointercal文件,這即是觸摸屏的校準配置文件。
或者能夠寫一個觸摸屏校準腳本calibrate,存放在/bin目錄下:
#!/bin/sh
export TSLIB_FBDEVICE=/dev/fb0
export TSLIB_TSDEVICE=/dev/input/event0
export TSLIB_CONFFILE=/usr/local/tslib/etc/ts.conf
export TSLIB_CALIBFILE=/etc/pointercal
export TSLIB_PLUGINDIR=/usr/local/tslib/lib/ts
export TSLIB_TSEVENTTYPE=H3600
export TSLIB_CONSOLEDEVICE=none
export QWS_KEYBOARD="TTY:/dev/tty1"
if [ -c /dev/input/event0 ]; then
if [ -e/etc/pointercal -a ! -s /etc/pointercal ] ; then
rm /etc/pointercal
fi
fi
export PATH=$QTDIR/bin:$PATH
exportLD_LIBRARY_PATH=$QTDIR/plugins/qtopialmigrate/:$QTDIR/qt_plugins/imageformats/:$QTDIR/lib:/root/tslib/build/lib:$LD_LIBRARY_PATH
exec /usr/local/tslib/bin/ts_calibrate 1>/dev/null 2>/dev/null
#exec /usr/local/tslib/bin/ts_test 1>/dev/null 2>/dev/null
執行calibrate:
[YJR@zhuzhaoqi]\# ./calibrate
若是執行的是ts_calibrate測試,效果如圖5. 1所示。
若是執行的是ts_test測試,選擇draw畫圖選項,效果如圖5. 2所示。
注:本節配套視頻位於光盤中「嵌入式Linux實用教程視頻」目錄下第五章01課(tslib安裝)。
第五章第二節 安裝Linux/x11版Qt-4.8.4
在官方網站下載Qt libraries 4.8.4 for Linux/X11 (225 MB)(實際是:qt-everywhere-opensource-src-4.8.4.tar.gz)。
完成以後在ubuntu宿主機解壓:
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/Qt-4.8.4$ tarzxvf qt-everywhere-opensource-src-4.8.4.tar.gz
在裝有gold linker的系統裏,編譯腳本會加入-fuse-ld=gold選項,但這個選項gcc是不支持的。解決辦法是移除該選項,找到文件src/3rdparty/webkit/Source/common.pri,屏蔽QMAKE_LFLAGS+=-fuse-ld=gold。
linux-g++ {
isEmpty($$(SBOX_DPKG_INST_ARCH)):exists(/usr/bin/ld.gold){
message(Using gold linker)
# QMAKE_LFLAGS+=-fuse-ld=gold
}
}
配置安裝路徑:
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/Qt-4.8.4/qt-everywhere-opensource-src-4.8.4$./configure --prefix=/usr/local/qt-4.8.4-x11
……
Type 'c' if you want to use the Commercial Edition.
Type 'o' if you want to use the Open Source Edition.
c
是商業,o
是開源,選擇o
。
……
Type 'yes' to accept this license offer.
Type 'no' to decline this license offer.
選擇yes
。
……
Qt is now configured for building. Just run 'make'.
Once everything is built, you must run 'make install'.
Qt will be installed into /usr/local/qt-4.8.4-x11
To reconfigure, run 'make confclean' and 'configure'.
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/Qt-4.8.4/qt-everywhere-opensource-src-4.8.4$
Qt輸出信息提示咱們進行make編譯:
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/Qt-4.8.4/qt-everywhere-opensource-src-4.8.4$make
這個編譯過程比較久,依每一個人的電腦配置而定,大概須要1~3個小時。
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/Qt-4.8.4/qt-everywhere-opensource-src-4.8.4$make install
安裝好以後,在/usr/local目錄下面有:
zhuzhaoqi@zhuzhaoqi-desktop:/usr/local$ ls
arm etc include lib qt-4.8.4-arm qwt-6.0.2 sbin src
bin games info man qt-4.8.4-x11 qwt-6.0.2-arm tslib-1.0
1.1.2
安裝embedded
版Qt-4.8.4
Embedded版Qt4.8.4源碼和Linux/x11版Qt4.8.4是同樣的,將下載的源碼解壓在另外一個文件夾,配置embedded版配置:
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/Qt-4.8.4/arm/qt-everywhere-opensource-src-4.8.4$./configure -prefix /usr/local/qt-4.8.4-arm/-shared -no-fast -no-largefile -no-exceptions -qt-sql-sqlite -qt3support-no-xmlpatterns -multimedia -no-svg -no-mmx -no-3dnow -no-sse -no-sse2 -qt-zlib-no-webkit -qt-libtiff -qt-libpng -qt-libjpeg -make libs -nomake examples-nomake docs -nomake demo -no-optimized-qmake -no-nis -no-cups -no-iconv-no-dbus -no-separate-debug-info -no-openssl -xplatform qws/linux-arm-g++-embedded arm -little-endian -no-freetype -depths 4,8,16,32 -qt-gfx-linuxfb-no-gfx-multiscreen -no-gfx-vnc -no-gfx-qvfb -qt-kbd-linuxinput -no-kbd-tty-no-glib -armfpa -no-mouse-qvfb -qt-mouse-pc -qt-mouse-tslib -I/usr/local/tslib-1.0/include-L/usr/local/tslib-1.0/lib
執行make進行編譯:
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/Qt-4.8.4/arm/qt-everywhere-opensource-src-4.8.4$make
執行makeinstall進行安裝:
zhuzhaoqi@zhuzhaoqi-desktop:~/Qt-4.8.4/Qt-4.8.4/arm/qt-everywhere-opensource-src-4.8.4$make install
安裝好以後,在/usr/local目錄下面有:
zhuzhaoqi@zhuzhaoqi-desktop:/usr/local$ ls
arm etc include lib qt-4.8.4-arm qwt-6.0.2 sbin src
bin games info man qt-4.8.4-x11 qwt-6.0.2-arm tslib-1.0
在qt-4.8.4-arm目錄下有以下目錄:
zhuzhaoqi@zhuzhaoqi-desktop:/usr/local/qt-4.8.4-arm$ls
bin imports include lib mkspecs plugins
在文件系統/opt/目錄下新建Qt-4.8.4目錄,以下所示:
zhuzhaoqi@zhuzhaoqi-desktop:~/rootfs/opt$ sudo mkdirQt-4.8.4/
將imports、lib、mkspecs、plugins拷貝至/opt/Qt-4.8.4/。
zhuzhaoqi@zhuzhaoqi-desktop:/usr/local/qt-4.8.3-arm$sudo cp -r importslibmkspecsplugins /home/zhuzhaoqi/rootfs/opt/Qt-4.8.4/
在開發板的文件系統/usr/目錄下新建/qt/目錄,將/qt-4.8.4-arm/lib/目錄下的全部文件拷貝到/usr/qt/目錄中。
zhuzhaoqi@zhuzhaoqi-desktop:~/rootfs/usr$ sudo mkdirqt
zhuzhaoqi@zhuzhaoqi-desktop:/usr/local/qt-4.8.4-arm/lib$sudo cp -r * /home/zhuzhaoqi/rootfs/usr/qt/
爲OK6410開發平臺添加Qt啓動環境參數,在/etc/profile中添加:
export QTDIR=/usr/qt
export QPEDIR=$QTDIR
export QT_PLUGIN_PATH=/usr/qt
export T_ROOT=/usr/local/tslib
export PATH=$QTDIR/:$PATH
export QWS_MOUSE_PROTO=Tslib:/dev/event0
export LD_LIBRARY_PATH=$T_ROOT/lib:$QTDIR
export QT_QWS_FONTDIR=/usr/qt
至此,Qt移植就完成。
注:本節配套視頻位於光盤中「嵌入式Linux實用教程視頻」目錄下第五章02課(安裝Linux和embedded版本Qt-4.8.4)。