对比市场上各种cortex-A8开发板,其实我很偏向于买友善的,但友善的mini210有一个致命缺陷——核心版上没有片选线,无法连接自定义的总线设备。为了扩展总线设备,我买了友坚的android210开发板,但这个开发板资料太少了,我想在它上面跑Linux都没有,套件中只有一个Android3.0.8系统,开发手册几乎等于没写,只有Android的开发视频。这下可惨了……
听师兄说过,国内这些做开发板的实际上都是使用的三星公司发布的SDK模板,硬件上基本都是照抄的,没有多大区别。这就让我想到在友坚的系统上跑友善的Linux系统,大不了改造一下试试。
我试过直接从官网下载Linux3.0.x,将其配制成samsung的SMDK210,结果启动都启不起来,看来改造的路漫漫呀……
为什么不接用友善已经配置好的内核呢?
于是托蔡辉从同行那里讨来了mini210的Linux2.6.35内核,和superboot软件。(文件系统由于太大,且有以前mini6410的,就没要)
通过superboot可以启动内核,但是存在一些问题:
首先LCD屏不亮,网络还连不上,不过CF卡是好的(要不然根本使不了superboot),usb是好的,串口是好的,其他就不知道了……
是不是内核配置不一样?我对比了两家的内核,并按友坚的配置配置了友善原来的内核(当然是做了备分),结果启动还是会死机或者不停地重启,失败……
看来两家的硬件还是有区别的,只好对内核源代码进行手术了……
对比二者的内核源代码,当然差异很大啦,版本都不一样……但最不一样的是arch/arm/mach-s5pv210/mach-mini210.c这个文件(友坚的是mach-sdmkv210.c),重点就是改造这个属于个性订制的文件,以下是我的改造记录
其实LCD屏本身没有多大区别,不亮的原因是背光引脚不一样。一个diff文件(这里也包含了对触摸屏的修改,希望你们能看懂,用Kompare软件应该可以打开):
---friendlyarm/linux-2.6.35.7/arch/arm/mach-s5pv210/mach-mini210.c 2012-02-2718:16:45.000000000 +0800
+++linux-2.6.35.7/arch/arm/mach-s5pv210/mach-mini210.c 2012-08-1715:32:10.209674178 +0800
@@-93,6 +93,8 @@
#include<plat/mfc.h>
#include<plat/iic.h>
#include<plat/pm.h>
+#include<plat/ts.h> //触摸屏用
+//../plat-samsung/include/
#include<plat/sdhci.h>
#include<plat/fimc.h>
@@-597,6 +599,52 @@
pr_err("nomemory for Touchscreen platform data\n");
}
}
+#else //触摸屏用
+staticstruct s3c2410_ts_mach_info s3c_ts_platform __initdata = {
+ .delay =10000,
+ .presc =49,
+ .oversampling_shift =2,
+ .cal_x_max = 800,
+ .cal_y_max = 480,
+ .cal_param = {
+ -13357,-85, 53858048, -95, -8493, 32809514, 65536
+},
+};
+static struct resource s3c_ts_resource[] = {
+[0] = {
+.start = SAMSUNG_PA_ADC,
+ .end = SAMSUNG_PA_ADC + SZ_256 - 1,
+ .flags = IORESOURCE_MEM,
+},
+ [1] = {
+ .start = IRQ_TC,
+ .end = IRQ_TC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device s3c_device_ts = {
+.name = "s3c64xx-ts",
+ .id =-1,
+ .num_resources =ARRAY_SIZE(s3c_ts_resource),
+ .resource =s3c_ts_resource,
+};
+
+void __init s3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *pd)
+ {
+struct s3c2410_ts_mach_info *npd;
+
+if (!pd) {
+printk(KERN_ERR "%s: no platform data\n", __func__);
+return;
+}
+
+npd = kmemdup(pd, sizeof(struct s3c2410_ts_mach_info), GFP_KERNEL);
+if (!npd)
+printk(KERN_ERR "%s: no memory for platform data\n",__func__);
+
+s3c_device_ts.dev.platform_data = npd;
+ }
#endif
#ifdefCONFIG_KEYBOARD_GPIO
@@-878,12 +926,18 @@
/*mDNIe SEL: why we shall write 0x2 ? */
writel(0x2,S5P_MDNIE_SEL);
-
+#if0
/*drive strength to max */
writel(0xaaaaaaaa,S5PV210_GPF0_BASE + 0xc);
writel(0xaaaaaaaa,S5PV210_GPF1_BASE + 0xc);
writel(0xaaaaaaaa,S5PV210_GPF2_BASE + 0xc);
writel(0x000000aa,S5PV210_GPF3_BASE + 0xc);
+#else
+ writel(0xffffffff,S5PV210_GPF0_BASE + 0xc);
+ writel(0xffffffff, S5PV210_GPF1_BASE + 0xc);
+ writel(0xffffffff, S5PV210_GPF2_BASE + 0xc);
+ writel(0x000000ff, S5PV210_GPF3_BASE + 0xc);
+#endif
}
#defineS5PV210_GPD_0_0_TOUT_0 (0x2)
@@-1018,40 +1072,51 @@
//触摸屏用
structs3c_pwm_data pwm_data[] = {
{
-#ifndefCONFIG_MINI210_BUZZER
.gpio_no = S5PV210_GPD0(0),
.gpio_name = "GPD",
.gpio_set_value= S5PV210_GPD_0_0_TOUT_0,
- },{
-#endif
+ },
+{
.gpio_no = S5PV210_GPD0(1),
.gpio_name = "GPD",
.gpio_set_value= S5PV210_GPD_0_1_TOUT_1,
- },{
+ },
+{
.gpio_no = S5PV210_GPD0(2),
.gpio_name = "GPD",
.gpio_set_value= S5PV210_GPD_0_2_TOUT_2,
- },{
+ },
+#ifndefCONFIG_MINI210_BUZZER
+{
+
.gpio_no = S5PV210_GPD0(3),
.gpio_name = "GPD",
.gpio_set_value= S5PV210_GPD_0_3_TOUT_3,
}
+#endif
};
#endif
#ifdefined(CONFIG_BACKLIGHT_PWM)
staticstruct platform_pwm_backlight_data smdk_backlight_data = {
+#if0
.pwm_id = 3,
.max_brightness= 255,
.dft_brightness= 255,
.pwm_period_ns = 25000,
+#else
+ .pwm_id =0,
+ .max_brightness =255,
+ .dft_brightness =192,//128,
+ .pwm_period_ns =50000,//78770,
+#endif
};
staticstruct platform_device smdk_backlight_device = {
.name = "pwm-backlight",
.id = -1,
.dev = {
- .parent= &s3c_device_timer[3].dev,
+ .parent= &s3c_device_timer[0].dev, //3
.platform_data= &smdk_backlight_data,
},
};
@@-1516,6 +1581,7 @@
I2C_BOARD_INFO("s5p_ddc",(0x74>>1)),
},
#endif
+ {I2C_BOARD_INFO("ft5x0x_ts", 0x38), },
};
#ifdefCONFIG_TOUCHSCREEN_GOODIX
@@-1668,10 +1734,10 @@
&s3c_device_mfc,
#endif
-#ifdefCONFIG_TOUCHSCREEN_S3C
+//#ifdefCONFIG_TOUCHSCREEN_S3C //触摸屏用
&s3c_device_ts,
-#endif
- &s3c_device_1wire,
+//#endif
+// &s3c_device_1wire,
#ifdefCONFIG_KEYBOARD_GPIO
&s3c_device_gpio_btns,
#endif
@@-1973,8 +2039,9 @@
#ifdefined(CONFIG_TOUCHSCREEN_S3C)
s3c_ts_set_platdata(&s3c_ts_platform);
+#else
+ s3c24xx_ts_set_platdata(&s3c_ts_platform); //触摸屏用
#endif
-
#ifdefined(CONFIG_S5P_ADC)
s3c_adc_set_platdata(&s3c_adc_platform);
#endif
除此之外,需要将友坚的driver/video/samsung/s3cfb_lte480wv.c拷贝到友善的同名目录下,并修改该目录下的Kconfig文件,在prompt"Select LCD Type"后添加
defaultFB_S3C_LTE480WV
configFB_S3C_LTE480WV
bool"LTE480WV"
dependson (MACH_SMDKV210 || MACH_SMDKC110)
---help---
This enables support for Samsung LTE480WV 4.8\" WVGA LCD panel
在Makefile文件中添加:
obj-$(CONFIG_FB_S3C_LTE480WV) +=s3cfb_lte480wv.o
修改
arch/arm/mach-s5pv210/mini210-lcds.c
将s3cfb_lcdwvga_s70 里的内容换成(这是根据LTE480WV的datasheet配的时序数据),
staticstruct s3cfb_lcd wvga_s70 = {
.width= 800,
.height= 480,
.p_width= 154,
.p_height= 90,
.bpp= 32,
.freq= 60,
.timing= {
.h_fp= 10,
.h_bp= 78,
.h_sw= 10,
.v_fp= 30,
.v_fpe= 1,
.v_bp= 30,
.v_bpe= 1,
.v_sw= 2,
},
.polarity= {
.rise_vclk= 0,
.inv_hsync= 1,
.inv_vsync= 1,
.inv_vden= 0,
},
};
将drivers/char/mini210_pwm.c中的对应定义改为:
#defineBUZZER_PWM_ID 3
#defineBUZZER_PMW_GPIO S5PV210_GPD0(3)
可能还有一些其他的小改动,我也记不清了,主要是这些。
之后就是编译了,相信各位很熟悉.
cpmini210_linux_defconfig .config
makezImage
烧到板子上,一开机,成啦!小企鹅和Qtopia都出来啦。但是触摸屏还是不管用……查了一下,友坚用的是ft5x0x_ts触摸屏,于是下一步的工作就有啦……
移植ft5x0x_ts触摸屏
1、在linux中添加ft5x0x_ts驱动程序
对比友坚和友善的系统内核,同样修改arch/arm/mach-s5pv210/mach-mini210.c文件,上文已述。
将友坚的arch/arm/plat-samsung/include/plat/ts.h文件替换友善的对应文件.
在drivers/input/touchscreen/中添加urbetter文件夹,将友坚内核中drivers/urbetter/touchscreen的全部内容拷入该文件夹,并修改ft5x0x_ts.c,不然编译会出错,给各位看的同样是个diff文件:
---android210-kernel/drivers/urbetter/touchscreen/ft5x0x_ts.c 2012-08-1717:01:39.317767802 +0800
+++linux-2.6.35.7/drivers/input/touchscreen/urbetter/ft5x0x_ts.c 2012-08-1717:29:50.342172715 +0800
@@-38,9 +38,13 @@
#include"ft5x0x_ts.h"
-#defineset_irq_type irq_set_irq_type //raymanfeng for kernel 3.0
+//#defineset_irq_type irq_set_irq_type //raymanfeng for kernel 3.0
u8buf[40] = {0};
+#defineGPIO_TS_EINT S5PV210_GPH0(5)
+#defineGPIO_TS_POWER S5PV210_GPH2(3)
+#defineGPIO_TS_WAKE S5PV210_GPH1(7)
+#defineGPIO_TS_COBY_WAKE S5PV210_GPH3(0)
#defineFT_INT_PORT GPIO_TS_EINT
#defineFT_WAKE_PORT GPIO_TS_WAKE
@@-74,7 +78,7 @@
#defineCONFIG_FT5X0X_MULTITOUCH 1
#defineCONFIG_ANDROID_4_ICS 1 //raymanfeng
-externint MmFlag;
+int MmFlag=1; //extern
externspinlock_t lock ;
structts_event {
@@-107,7 +111,8 @@
s_model= 712;
intvalue = 703;
- externchar g_selected_utmodel[];
+ //externchar g_selected_utmodel[];
+ charg_selected_utmodel[32] = {'\0'};
if(sscanf(g_selected_utmodel,"%d", &value) == 1)
{
if(value> 0)
@@-810,7 +815,7 @@
}
#endif //CONFIG_HAS_EARLYSUSPEND
-externint g_touchscreen_init;
+int g_touchscreen_init; //extern
staticint
ft5x0x_ts_probe(structi2c_client *client, const struct i2c_device_id *id)
{
当然还要把它加入到内核编译中,在该文件的上一层目录的Kconfig中添加:
source"drivers/input/touchscreen/urbetter/Kconfig"
在同级的Makefile中添加:
obj-$(CONFIG_TOUCHSCREEN_UT210_FT5X0X) += urbetter/
可能还有些细节我忘了写,但大概就是这样。
别忘了编译的时候选择FT5X0X触摸屏,在menuconfig中的devicedriver->input device->touch screen,勾选"touchscreendriver UT210 FT5X0X"。
makezImage
烧写、上电启动,最后会在/dev/input中生成一个event0,这就是我们想要的。验证是不是:
cat/proc/bus/input
验证灵不灵:
cat/dev/input/event0
在触摸屏上点两下,如果有乱码字符出现,就算成功了。
2、为实验触摸屏移植的正确性,我在一个不带Qt的文件系统中移植了tslib1.4(这好像是目前的最高版本),移植方法如下(参考网上的):
$sudo apt-get install autoconf automake libtool
$./autogen.sh
$./configure --prefix=/root/more_space/tslib4arm/ --host=arm-linuxac_cv_func_malloc_0_nonnull=yes
$make
$make install
在/root/more_space/tslib4arm/中生成了4个文件夹:bin,etc,include,lib
将tslib4arm整个放到ARM文件系统的/opt下
在/etc/profile中添加:
exportTS_ROOT=/opt/tslib4arm
exportTSLIB_CONSOLEDEVICE=none
exportTSLIB_FBDEVICE=/dev/fb0
exportTSLIB_TSDEVICE=/dev/input/event0
exportTSLIB_CALIBFILE=/etc/pointercal
exportPOINTERCAL_FILE=/etc/pointercal
exportTSLIB_CONFFILE=$TS_ROOT/etc/ts.conf
exportTSLIB_PLUGINDIR=$TS_ROOT/lib/ts
#exportTS_INFO_FILE=/sys/devices/virtual/input/input0/uevent 这句是从友善qtopia中学的,自动检测触摸屏类型,决定使用哪种协议
exportQWS_SIZE=800x480
exportQWS_MOUSE_PROTO="Tslib:/dev/input/event0"
exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TS_ROOT/lib
修改/opt/etc/ts.conf:
将module_rawinput解放,注意首字母前不应有其它任何字符。
启动系统,运行
cd/opt/tslib4arm/bin
./ts_test
结果出现selecteddevice is not a touchscreen I understand的提示
网上找了很多参考意见,问题定位到tslib/plugins/input-raw.c的chek_fd的函数,
if(! ((ioctl(ts->fd, EVIOCGVERSION, &version) >= 0) &&
(version== EV_VERSION) &&
(ioctl(ts->fd,EVIOCGBIT(0, sizeof(bit) * 8), &bit) >= 0) &&
(bit& (1 << EV_ABS)) &&
(ioctl(ts->fd,EVIOCGBIT(EV_ABS, sizeof(absbit) * 8), &absbit) >= 0)/* &&
(absbit& (1 << ABS_X)) &&
(absbit& (1 << ABS_Y)) && (absbit & (1 <<ABS_PRESSURE)))*/)) {
fprintf(stderr,"selected device is not a touchscreen I understand\n");
if(ioctl(ts->fd,EVIOCGVERSION, &version) < 0) fprintf(stderr,"ioctl<0\n"); //这些是我后加的
if(version!= EV_VERSION) fprintf(stderr, "version=%d,EV_VERSION=%d\n",version,EV_VERSION);
if(ioctl(ts->fd,EVIOCGBIT(0, sizeof(bit) * 8), &bit) < 0) fprintf(stderr,"ioctl<0\n");
if(!(bit& (1 << EV_ABS))) fprintf(stderr, "bit=0x%x,EV_ABS=0x%x\n",bit,EV_ABS);
if(ioctl(ts->fd,EVIOCGBIT(EV_ABS, sizeof(absbit) * 8), &absbit) < 0)fprintf(stderr, "ioctl<0\n");
if(!(absbit& (1 << ABS_X)) || !(absbit & (1 << ABS_Y)) ||!(absbit & (1 << ABS_PRESSURE)) )
fprintf(stderr,"absbit=0x%x, ABS_X=0x%x, ABS_Y=0x%x,ABS_PRESSURE=0x%x\n",absbit,ABS_X,ABS_Y,ABS_PRESSURE);
return-1;
}
有人说是EV_VERSION与内核中定义的版本不一致,我看了一下,我的是一致的,于是我将所有的导致出现这句话的条件都打印了出来,结果发现是由于
最有一个条件没满足,即absbit不对,而改变量来自于ioctl的赋值,哎只好研究一下触摸屏驱动。
这是一款i2c触摸屏,tslib中调用ioctl和read,我在他的驱动程序中死活找不到,后来在网上发现是由于Linux的I2C体系结构相当复杂
http://www.cnblogs.com/cute/archive/2011/08/30/2159326.html
这篇文章写的很好,描述了i2c在linux中的实现及i2c的协议,看后受益匪浅.
我看到的驱动属于I2C设备驱动层,真正的ioctl和read在I2C总线驱动层,具体的定义在/driver/i2c/i2c-dev.c中,看来不是没有,而是返回值和要求对应不上。
再研究发现,ts_input_read中读出值的结构是
structtslib_input {
structtslib_module_info module;
int current_x;
int current_y;
int current_p;
int sane_fd;
int using_syn;
};
通过前面的阅读,找到ft5x0x_ts驱动程序中对应的返回结构,
structts_event {
u16 x1;
u16 y1;
u16 x2;
u16 y2;
u16 x3;
u16 y3;
u16 x4;
u16 y4;
u16 x5;
u16 y5;
u16 pressure;
u8 touch_point;
};
靠!返回结构都不一样,能对吗?!问题的原因找到了:我使用的是多触点触摸屏,而tslib的程序是给单触点触摸屏使用的。看来得换tslib,于是在网上搜了一下,不幸的是对多触点的支持,tslib只有一个1.0版本,幸运的是还有一个:
http://sourceforge.net/projects/tslib-mt/
下载,同样的方法编译、使用,只有一点要变:
将/opt/etc/ts.conf中module_rawinput变为module_rawmtinput。
再试,欧拉!下图为证:
dm9000网卡移植
还有一项要紧的工作——移植网卡,现在的网络不可用,NFS连不上,不论调试还是应用都是个问题,所以还是移上的好。
我对比了一下友坚提供的Linux3.0.8的drivers/net/dm9000.c和友善的Linux2.6.35.7对应文件,发现差异还是很大的,看来网卡方面android(或是之后的Linux)还是做了一些改动的,所以我还是使用友善的原版的吧……
还是先改linux/arch/arm/mach-s5pv210/mach-mini210.c这个文件:
---friendlyarm/linux-2.6.35.7/arch/arm/mach-s5pv210/mach-mini210.c 2012-02-2718:16:45.000000000 +0800
+++linux-2.6.35.7/arch/arm/mach-s5pv210/mach-mini210.c 2012-08-2415:17:16.360204015 +0800
@@-93,6 +93,8 @@
@@-763,7 +811,7 @@
#ifdefCONFIG_DM9000
#include<linux/dm9000.h>
-
+#if0
/*physical address for dm9000a ...kgene.kim@samsung.com */
#defineS5PV210_PA_DM9000_A (0x88001000)
#defineS5PV210_PA_DM9000_F (S5PV210_PA_DM9000_A + 0x300C)
@@-849,6 +897,86 @@
s3c_gpio_cfgpin(S5PV210_MP01(1),S3C_GPIO_SFN(2));
gpio_free(S5PV210_MP01(1));
}
+#else
+staticstruct resource smdkv210_dm9000_resources[] = {
+ [0]= {
+ .start =S5P_PA_DM9000,
+ .end =S5P_PA_DM9000 + 0x3,
+ .flags =IORESOURCE_MEM,
+ },
+ [1]= {
+ .start =S5P_PA_DM9000 + 0x4,
+ .end =S5P_PA_DM9000 + 0x7,
+ .flags =IORESOURCE_MEM,
+ },
+ [2]= {
+ .start =IRQ_EINT(10),
+ .end =IRQ_EINT(10),
+ .flags =IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
+ },
+};
+
+staticstruct dm9000_plat_data smdkv210_dm9000_platdata = {
+ .flags =DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM,
+ .dev_addr = { 0x00, 0x09, 0xc0, 0xff, 0xec, 0x48 },
+};
+
+structplatform_device mini210_device_dm9000 = {
+ .name ="dm9000",
+ .id =-1,
+ .num_resources =ARRAY_SIZE(smdkv210_dm9000_resources),
+ .resource =smdkv210_dm9000_resources,
+ .dev ={
+ .platform_data =&smdkv210_dm9000_platdata,
+},
+};
+staticint __init dm9000_set_mac(char *str) {
+ unsignedchar addr[6];
+ unsignedint val;
+ intidx = 0;
+ char*p = str, *end;
+
+ while(*p && idx < 6) {
+ val= simple_strtoul(p, &end, 16);
+ if(end <= p) {
+ /*convert failed */
+ break;
+ }else {
+ addr[idx++]= val;
+ p= end;
+ if(*p == ':'|| *p == '-') {
+ p++;
+ }else {
+ break;
+ }
+ }
+ }
+
+ if(idx == 6) {
+ printk("Setupethernet address to %pM\n", addr);
+ memcpy(smdkv210_dm9000_platdata.param_addr,addr, 6);
+ }
+
+ return1;
+}
+
+__setup("ethmac=",dm9000_set_mac);
+
+staticvoid __init mini210_dm9000_set(void)
+{
+ unsignedint tmp;
+ tmp= __raw_readl(S5P_SROM_BW);
+ tmp&= ~(0xF<<4); /* dm9000 16bit */
+ tmp|= (1<<7) | (1<<6) | (1<<5) | (1<<4);
+ __raw_writel(tmp,S5P_SROM_BW);
+ __raw_writel((0x0<<28)|(0x0<<24)|(0x5<<16)|(0x0<<12)|(0x0<<8)|(0x0<<4)|(0x0<<0),S5P_SROM_BC3);
+
+ tmp= __raw_readl(S5PV210_MP01CON);
+ tmp|= (2 << 4);
+ __raw_writel(tmp,S5PV210_MP01CON);
+
+}
+#endif
#endif
然后注意这里面的S5P_PA_DM9000,是给网卡分配的地址空间,友坚的片选线连的是CSn1,在linux/arch/arm/mach-s5pv210/include/mach/map.h中,将
#defineS5PV210_PA_DM9000 (0xA8000000)
#defineS5P_PA_DM9000 S5PV210_PA_DM9000
两行改为(友坚内核提供)
#defineS5PV210_PA_SROM_BANK5 0x88000000
#defineS5P_PA_DM9000 S5PV210_PA_SROM_BANK5 + 0x300
不然的话启动时会出现:
dm9000:read wrong id 0x2b2a2928
dm9000: read wrong id 0x2b2a2928
……(共8行)
dm9000:wrong id: 0x2b2a2928
……
Tryto bring eth0 interface up......ifconfig: SIOCGIFFLAGS: No suchdevice
另外,友坚的和友善的staticvoid dm9000_reset(board_info_t * db)实现方法不一样,应该在makemenuconfig时将CONFIG_DM9000_16BIT置为零,即在devicedrivers-> network device support-> Ethernet (10 or 100Mbit)->DM9000 16-bit 置为n。
编译,烧写,运行,会看到
dm9000Ethernet Driver, V1.31
eth0:dm9000a at f0892300,f0896304 IRQ 42 MAC: 00:09:c0:ff:ec:48 (platformdata)
……
Tryto bring eth0 interface up......eth0: link down
ADDRCONF(NETDEV_UP):eth0: link is not ready
Done
Pleasepress Enter to activate this console. eth0: link up, 100Mbps,full-duplex, lpa 0x4DE1
ADDRCONF(NETDEV_CHANGE):eth0: link becomes ready
输入命令,会看到
[root@Lbs/]# ping 192.168.1.17
PING192.168.1.17 (192.168.1.17): 56 data bytes
64bytes from 192.168.1.17: seq=0 ttl=64 time=4.306 ms
64bytes from 192.168.1.17: seq=1 ttl=64 time=0.175 ms
64bytes from 192.168.1.17: seq=2 ttl=64 time=0.172 ms
64bytes from 192.168.1.17: seq=3 ttl=64 time=0.161 ms
……
这样就算成功了。