本文大概就是随便记录下调试过程中遇到的问题。考虑到iMX6还是相当好用的芯片,很有可能也不是最后一次用iMX6,那就姑且记录下,方便自己以后参考吧。

摄像头I2C

不知道为什么,iMX6的I2C拒绝和我的摄像头通信,示波器观察的话摄像头已经给了ACK,但是iMX6没有继续传输数据。好在Linux下使用软件I2C非常简单,只需要选中i2c-gpio驱动并且简单修改设备树即可。

i2c@0 {
	compatible = "i2c-gpio";
	gpios = <&gpio1 29 0
		 &gpio1 28 0>;
	#address-cells = <1>;
	#size-cells = <0>;

	mt9m: mt9m@5d {
		compatible = "mt9m";
		reg = <0x5d>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_csi1>;
		clocks = <&clks IMX6UL_CLK_CSI>;
		clock-names = "csi_mclk";
		csi_id = <0>;
		mclk = <20000000>;
		mclk_source = <0>;
		status = "okay";
		port {
			mt9m_ep: endpoint {
				remote-endpoint = <&csi1_ep>;
			};
		};
	};
};

另外不要忘了在PinCtrl下加入对应的引脚模式配置,简单起见直接加在hog中

pinctrl_hog_1: hoggrp-1 {
	fsl,pins = <
		MX6UL_PAD_UART4_TX_DATA__GPIO1_IO28	0x1b8b0
		MX6UL_PAD_UART4_RX_DATA__GPIO1_IO29	0x1b8b0
	>;
};

userspace GPIO

这个更加简单,直接像上面一样把模式配置加入hog,但是不要加入别相关的节点。进入系统后如下使用GPIO

/sys/class/gpio # echo 0 > export
/sys/class/gpio # echo in > gpio0/direction
/sys/class/gpio # cat gpio0/value
0
/sys/class/gpio # echo out > gpio0/direction
/sys/class/gpio # echo 1 > gpio0/value

需要注意的是序号问题,iMX6的GPIO是32pin一组,序号是顺延的。比如GPIO1_3的序号就是3,而GPIO4_21的是(4-1)*32+21=117。

启动模式设计考虑

对于BOOT_MODE1和BOOT_MODE0,使用以下方式来拉低:

  • 使用任意阻值电阻接地
  • 直接接地

需要拉高的话:

  • 直接接上VDD_SNVS_IN
  • 通过10k电阻连接到VDD_SNVS_IN。如果干扰较大,使用4.7k电阻

如果需要通过开关控制,不需要任何外部下拉电阻,直接将SPST开关接上VDD_SNVS_IN。如果电流消耗非常重要的话,使用4.7k到10k的电阻。

USB设计考虑

由于iMX6UL本身设计上的bug,会存在VBUS泄漏的问题,为此,同时只能有一个USB工作在OTG/Device模式,另外一个要么不用,要么是Host模式。两个USB可以同时工作在Host模式下。

CSI摄像头

iMX6S/D/DL/Q内置了IPU(图像处理单元),CSI捕捉到的图像会先经过IPU再到达内存。而iMX6SX/iMX6SL/iMX6UL/iMX6ULL/iMX7D没有内置IPU,图像从CSI直接到内存。为此,两者使用的驱动是不同的,前者位于drivers/media/platform/mxc/capture,后者位于drivers/media/platform/mxc/subdev,两者的API是不同的,移植时千万注意不要弄错。

另外目前而言,linux-imx的驱动中并不支持8位灰度图像的捕捉,不过简单修改下加上就行,毕竟基本上等同于8位BayerRAW。

MT9M001的输出时钟极性和iMX默认极性相反,需要进行反转。

@@ -273,6 +273,12 @@ static struct mx6s_fmt formats[] = {
                .pixelformat    = V4L2_PIX_FMT_SBGGR8,
                .mbus_code      = MEDIA_BUS_FMT_SBGGR8_1X8,
                .bpp            = 1,
+       }, {
+               .name           = "GREYSCALE (Y8)",
+               .fourcc         = V4L2_PIX_FMT_GREY,
+               .pixelformat    = V4L2_PIX_FMT_GREY,
+               .mbus_code      = MEDIA_BUS_FMT_Y8_1X8,
+               .bpp            = 1,
        }
 };
 
@@ -463,6 +469,7 @@ static void csi_init_interface(struct mx6s_csi_dev *csi_dev)
        val |= BIT_FCC;
        val |= 1 << SHIFT_MCLKDIV;
        val |= BIT_MCLKEN;
+       val |= BIT_INV_PCLK;
        __raw_writel(val, csi_dev->regbase + CSI_CSICR1);
 
        imag_para = (640 << 16) | 960;
@@ -804,6 +811,7 @@ static int mx6s_configure_csi(struct mx6s_csi_dev *csi_dev)
        switch (csi_dev->fmt->pixelformat) {
        case V4L2_PIX_FMT_YUV32:
        case V4L2_PIX_FMT_SBGGR8:
+       case V4L2_PIX_FMT_GREY:
                width = pix->width;
                break;
        case V4L2_PIX_FMT_UYVY:
@@ -835,6 +843,7 @@ static int mx6s_configure_csi(struct mx6s_csi_dev *csi_dev)
                        cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B;
                        break;
                case V4L2_PIX_FMT_SBGGR8:
+               case V4L2_PIX_FMT_GREY:
                        cr18 |= BIT_MIPI_DATA_FORMAT_RAW8;
                        break;
                default:

图像预览的话,很遗憾似乎并不能直接使用mplayer,因为无法显式指定像素格式。不过可以自己写个简单的程序用于预览(捕捉类似):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <string.h>

#define NR_BUFFER 4
void * vm_addr[NR_BUFFER];
void * framebuffer;

int main(int argc,char **argv)
{
	int fd, vd, fb, ret, i, type;
	char buffer[32];
	char *grey_buf = NULL;
	char capflags;
	struct fb_var_screeninfo fb_var;
	struct fb_fix_screeninfo fb_fix;
	struct v4l2_capability cap;
	struct v4l2_fmtdesc fmt;
	struct v4l2_frmsizeenum fsize;
	struct v4l2_frmivalenum fival;
	struct v4l2_streamparm sparm;

	fb = open("/dev/fb0",O_RDWR);
	if(fb < 0){
		perror("open fb");
		exit(1);
	}
	if(ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) < 0){
		perror("get var");
		exit(1);
	}
	if(ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) < 0){
		perror("get fix");
		exit(1);
	}
	framebuffer = mmap(NULL,fb_var.xres*fb_var.yres*fb_var.bits_per_pixel/8 * 2,PROT_WRITE,MAP_SHARED,fb,0);
	if(framebuffer == MAP_FAILED){
		perror("fb map");
		exit(1);
	}
	
	//video init
	vd = open( "/dev/video0", O_RDWR);
	if(vd < 0){
		perror("open cam ");
		exit(1);
	}

	if(ioctl(vd,VIDIOC_QUERYCAP,&cap) < 0){
		perror("cam capability");
		exit(1);
	}
	printf("capability driver %s card %s businfo %s\n",cap.driver,cap.card,cap.bus_info);

	struct v4l2_format vfmt;
	memset(&vfmt,0,sizeof(vfmt));
	vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	vfmt.fmt.pix.width = 640;
	vfmt.fmt.pix.height = 480;
	vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;
	vfmt.fmt.pix.field = V4L2_FIELD_NONE;
	if(ioctl(vd,VIDIOC_S_FMT,&vfmt) < 0){
		perror("cam set fmt");
		exit(1);
	}
	printf("cam set width %d height %d\n",vfmt.fmt.pix.width,vfmt.fmt.pix.height);
	memset(&sparm,0,sizeof(sparm));
	sparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	sparm.parm.capture.capturemode = 0;
	sparm.parm.capture.timeperframe.numerator = 1;
	sparm.parm.capture.timeperframe.denominator = 30;
	if(ioctl(vd,VIDIOC_S_PARM,&sparm) < 0){
		perror("cam s parm");
		exit(1);
	}


	struct v4l2_requestbuffers reqb;
	memset(&reqb,0,sizeof(reqb));
	reqb.count = NR_BUFFER;
	reqb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	reqb.memory = V4L2_MEMORY_MMAP;
	if(ioctl(vd,VIDIOC_REQBUFS,&reqb) < 0){
		perror("cam req buf");
		exit(1);
	}
	struct v4l2_buffer vbuffer;
	for(i = 0; i < NR_BUFFER; i++){
		memset(&vbuffer,0,sizeof(vbuffer));
		vbuffer.index = i;
		vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		vbuffer.memory = V4L2_MEMORY_MMAP;
		if(ioctl(vd,VIDIOC_QUERYBUF,&vbuffer) < 0){
			perror("cam querybuf");
			exit(1);
		}
		vm_addr[i] = mmap(NULL,vbuffer.length,PROT_READ,MAP_SHARED,vd,vbuffer.m.offset);
		if(vm_addr[i] == MAP_FAILED){
			perror("cam map ");
			exit(1);
		}
	}
	for(i = 0;i < NR_BUFFER; i++){
		memset(&vbuffer,0,sizeof(vbuffer));
		vbuffer.index = i;
		vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		vbuffer.memory = V4L2_MEMORY_MMAP;
		if(ioctl(vd,VIDIOC_QBUF,&vbuffer) < 0){
			perror("cam qbuf");
			exit(1);
		}
	}

	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if(ioctl(vd,VIDIOC_STREAMON,&type) < 0){
		perror("cam streamon");
		exit(1);
	}

	memset(&vbuffer,0,sizeof(vbuffer));
	vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	vbuffer.memory = V4L2_MEMORY_MMAP;
	while(1){
		if(ioctl(vd,VIDIOC_DQBUF,&vbuffer) < 0){
			perror("cam dqbuf");
			exit(1);
		}
		grey2rgb(vm_addr[vbuffer.index],framebuffer,640,480);
		if(ioctl(vd,VIDIOC_QBUF,&vbuffer) < 0){
			perror("cam qbuf");
			exit(1);
		}
	}

	if(ioctl(vd, VIDIOC_STREAMOFF, &type) < 0){
		perror("cam stream off");
		exit(1);
	}
	
	close(vd);
	return 0;
}

超频

!请不要在正式产品中超频

i.MX6UL出厂有两种速度等级,一种是528MHz,另外一种是696MHz,我手头的这种是528MHz的,系统中也能看到:

root@jessie-dev:~# lscpu                                                        
Architecture:          armv7l                                                   
Byte Order:            Little Endian                                            
CPU(s):                1                                                        
On-line CPU(s) list:   0                                                        
Thread(s) per core:    1                                                        
Core(s) per socket:    1                                                        
Socket(s):             1                                                        
Model name:            ARMv7 Processor rev 5 (v7l)                              
CPU max MHz:           528.0000                                                 
CPU min MHz:           198.0000  

那么有没有办法假装自己是696MHz呢?答案是肯定的。判断代码位于arch/arm/mach-imx/mach-imx.c,具体如下:

	if (cpu_is_imx6ul()) {
		if (val < OCOTP_CFG3_SPEED_696MHZ) {
			if (dev_pm_opp_disable(cpu_dev, 696000000))
				pr_warn("Failed to disable 696MHz OPP\n");
		}
	}

只要注释掉以上代码即可达到696MHz的频率。

还嫌不够怎么办呢?可以修改dtsi文件。具体配置如下:

	operating-points = <
		/* kHz	uV */
		792000	1275000
		696000	1275000
		528000	1175000
		396000	1025000
		198000	950000
	>;
	fsl,soc-operating-points = <
		/* KHz	uV */
		792000	1275000
		696000  1275000
		528000	1175000
		396000	1175000
		198000	1175000
	>;

如上,792000就是我自己加的配置,也就是792MHz。

至于超频是否成功,可以用跑分来验证。比如这里使用time pi 1048576 >> null来测试。实测528MHz下大约为48.5s,696MHz下大约为38s,792MHz下大约为32.8s,提升幅度48%(主频提升了50%,基本是线性提升),还是相当可观的,不过再怎么超也是垃圾,也就是超着玩了。

移植U-Boot

这里选择的版本是不带SPL的版本,因为对于i.MX来说SPL意义不大。唯一的选择是Freescale提供的的2015.04。请注意请clone uboot-imx.git下的代码而不是u-boot.git。

git clone -b imx_v2015.04_3.14.38_6ul_ga git://git.freescale.com/imx/uboot-imx.git

完成后进行必要的移植配置,确认烧录可以运行。其实关键也就是调整内存配置,我们的板子用的是256MB的DDR3而非默认的512MB,这里简单记录一下diff:

diff --git a/board/freescale/mx6ul_14x14_evk/imximage.cfg b/board/freescale/mx6ul_14x14_evk/imximage.cfg
index 1164fdd..67b8076 100644
--- a/board/freescale/mx6ul_14x14_evk/imximage.cfg
+++ b/board/freescale/mx6ul_14x14_evk/imximage.cfg
@@ -93,24 +93,25 @@ DATA 4 0x021B08C0 0x00922012
 DATA 4 0x021B0858 0x00000F00
 DATA 4 0x021B08b8 0x00000800
 DATA 4 0x021B0004 0x0002002D
-DATA 4 0x021B0008 0x1B333000
-DATA 4 0x021B000C 0x676B54F3
-DATA 4 0x021B0010 0xB68E0A83
+DATA 4 0x021B0008 0x1B333030
+DATA 4 0x021B000C 0x3F4354F3
+DATA 4 0x021B0010 0xB66D0B63
 DATA 4 0x021B0014 0x01FF00DB
 DATA 4 0x021B0018 0x00211740
 DATA 4 0x021B001C 0x00008000
 DATA 4 0x021B002C 0x000026D2
-DATA 4 0x021B0030 0x006B1023
-DATA 4 0x021B0040 0x0000004F
-DATA 4 0x021B0000 0x84180000
+DATA 4 0x021B0030 0x00431023
+DATA 4 0x021B0040 0x00000047
+DATA 4 0x021B0000 0x83180000
+DATA 4 0x021B0890 0x00400A38
 DATA 4 0x021B001C 0x02008032
 DATA 4 0x021B001C 0x00008033
 DATA 4 0x021B001C 0x00048031
 DATA 4 0x021B001C 0x15208030
 DATA 4 0x021B001C 0x04008040
-DATA 4 0x021B0020 0x00000800
+DATA 4 0x021B0020 0x00007800
 DATA 4 0x021B0818 0x00000227
-DATA 4 0x021B0004 0x0002552D
+DATA 4 0x021B0004 0x0002556D
 DATA 4 0x021B0404 0x00011006
 DATA 4 0x021B001C 0x00000000
 #endif
diff --git a/include/configs/mx6ul_14x14_evk.h b/include/configs/mx6ul_14x14_evk.h
index 833f2b0..23b087c 100644
--- a/include/configs/mx6ul_14x14_evk.h
+++ b/include/configs/mx6ul_14x14_evk.h
@@ -138,7 +138,7 @@
 #define CONFIG_POWER_PFUZE300_I2C_ADDR 0x08
 #else
 #define CONFIG_DEFAULT_FDT_FILE "imx6ul-14x14-evk.dtb"
-#define PHYS_SDRAM_SIZE                        SZ_512M
+#define PHYS_SDRAM_SIZE                        SZ_256M
 #define CONFIG_BOOTARGS_CMA_SIZE   ""
 #endif

编译就没什么了

ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make mx6ul_14x14_evk_defconfig
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make -j4
sudo dd if=u-boot.imx of=/dev/sdb bs=1k seek=1 conv=fsync

顺便试一下手动生成u-boot.imx:

./tools/mkimage -n ./board/freescale/mx6ul_14x14_evk/imximage.cfg.cfgtmp -T imximage -e 0x87800000 -d u-boot.bin u-boot.imx
sudo dd if=u-boot.imx of=/dev/sdb bs=1k seek=1 conv=fsync

应该是和之前效果相同的。