本文大概就是随便记录下调试过程中遇到的问题。考虑到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
应该是和之前效果相同的。