荔枝派Zero 用户指南

荔枝派Zero是一款精致迷你的Cortex-A7核心板/开发板,适用于初学者学习linux或商用产品开发。

联系我们: Q群 | github | 论坛 | 淘宝 | 邮箱 | _____ 文档来源: 看云

https://box.kancloud.cn/fb63cd12ae1def9dd50710d2a32dc5c1_1095x740.png

设计框图

荔枝派Zero: 震惊!从未见过如此精思巧构之Pi!

缘起

去年仲夏之夜,我吃着荔枝DIY了荔枝派One。 One的众筹和生产过程可谓万分曲折,不过整个流程经历下来也让我更清楚怎样的Pi才是一块好Pi。

笔者是业余时间将项目中使用过的主控芯片部分电路抽出,重新设计布局而成荔枝派系列板卡,时间并不充分,但是提供qq群与论坛供大家交流整理资料;
希望初次学习linux使用的朋友能够钻研在使用中遇到的问题并与大家交流分享,而不要当伸手党,真正的linux大神不是伸手伸出来的,也不是培训班里培训出来的,而是自己踩坑踩出来的。

荔枝派Zero从去年年末开始构思,设计目标就是一块满足创客和嵌入式工程师一切美好设想的Pi:

**低成本,小体积,高性能,易使用,多扩展。**

(~$6;~45x26mm,略长于SD卡;24MHz~1.2GHz;多种开发语言,直插面包板,同时全引脚邮票孔引出;多种外设模块)

目标应用场景是:

  • 使用较复杂的通信接口和协议的物联网应用
  • 需要较美观,复杂逻辑的人机交互界面的应用
  • 需要较多运算(相对于常用MCU)的应用场景
  • 需要使用linux下的开源软件进行快速开发的场景
  • 高端极客玩家,在体积、性能、易用性 上取得平衡。
  • 入门级玩家,软件工程师,使用熟悉的语言进行硬件diy。

初识

荔枝派Zero基于Allwinner V3s (ARM Cortex-A7 CPU, 1.2GHz, 512Mbit DDR2 integrated), 可从板载SPI Nor Flash(SOP8 16MB / WSON8 32MB) 或者 TF卡启动。

Zero采用了巧妙的引脚引出设计:

  • 兼容常用的2.54mm插针,甚至可以 直插面包板 使用;
  • 使用1.27mm邮票孔引出,可以 直接贴片 使用;
  • 1.27mm邮票孔也可以使用 2.54插针偏移半针位焊接 ,适合diy
  • 板载 FPC40接口 ,可以转接多种实用外设
  • 专为Zero设计的 TF wifi 模块,可插卡槽,也可直插2.54焊针。

荔枝派Zero 麻雀虽小五脏俱全,具备了常用的多种低速外设(UART,SPI,I2C,PWM,ADC),单片机上少有的高速接口它也有不少(OTG USB,MIPI CSI,EPHY,RGB LCD),还内置了CODEC(直接耳机麦克)~ Zero的引脚布局示意图如下:

“2.54mm Pinout”标识是可直接插入面包板或者使用标准杜邦线连接的。
“1.27mm”标识的是较为不常用或者不方便手工接线的引脚,一般在贴片时使用,或者也可以用2.54插针偏移半个针位使用。

https://box.kancloud.cn/fb63cd12ae1def9dd50710d2a32dc5c1_1095x740.png


话不多说,下面开始曝照!

由于以下样板由我手工焊接,难免有些焊接痕迹,焊得丑请轻喷,成品会用机贴的。

正面:从左往右为:RGB LED;主芯片;tf卡槽;LCD背光电路;micro usb otg。

https://box.kancloud.cn/aa69c26a162e043a1831f6693ec059d7_1024x768.jpg


背面:从左往右为:电阻屏控制器;DCDC芯片;预留的SPI flash接口,兼容SOP8和WSON8封装的SPI flash(16/32MB);FPC40接口。 https://box.kancloud.cn/f66b91d12d8a68d6fd65a70274205b19_1024x768.jpg


正反面一起看:

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c0ffddaf6c8.jpg


插针引出演示:

直插面包板:双列插针间距900mil,可直插面包板!

应该是首款可直插面包板的Cortex-A7水果派~ 手残党的福音~

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c1004512a1a.jpg

Zero的常用引脚使用2.54mm插针引出,如果想用到全部引脚,也不必像其它板子那样飞线,仍然可以用2.54插针和杜邦线搞定!

如下图所示,邮票孔是1.27mm间距,所以刚好可以间隔半个脚位焊上2.54mm插针,轻松使用杜邦线连接!

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c1005e9d727.jpg


上面的高清大图看上去是不是觉得Zero比较大呢?没有比较就没有伤害,其实Zero只比SD卡长一点!

与SD卡一起比个子:

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c1007a7df0c.jpg


Zero默认运行主频高达1G(最高1.2G),你是否认为它功耗感人?

NONONO,其实它以1G主频跑起linux后的运行电流 和 STM32F4以168MHz主频全速运行的电流(93mA)差不多!

比STMF7全速运行于216MHz的电流(193mA)要低一半以上!

有图有真相:

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c100926dcf8.jpg

再来看看Zero全速运行时的发热情况:

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c100ab44010.jpg

是的,仅有40度左右,略高于体温,比H3之流 动辄80℃+的CPU温度要让人安心多了,再也不用画蛇添足地给水果派加装散热片了~

这还是1GHz主频下的电流和发热情况,Zero可以在24MHz~1.2GHz之间以24MHz步进设置主频,如果你用24MHz或者48MHz当单片机用的话,那耗电还会低上不少!

细探

Zero除了核心板本身,还设计了一系列常用的外设以及配套底板用于功能验证,在此为您细细道来。

TF Wifi卡

IoT大行其道的今天,联网功能也必不可少。

Zero可使用Wifi,Ethernet连入网络,还在配套演示底板上集成了Lora模块,可用作Lora网关。

出于体积考虑,Zero核心板上并未加入wifi功能,但是我专为Zero设计了小巧的tf wifi 模块,可用多种方式与Zero叠加。

下图就是TF WiFi模块:

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c100fe634c9.jpg

模块本身使用0.8mm厚度 PCB制成,与TF卡厚度(0.75mm)极为接近,可以直接插入TF卡槽!

注意到上图有一长一短模块,长模块是为常用的全槽TF卡槽兼容使用,短模块是为Zero上的半槽TF卡槽使用。

由于PCB仅有0.8mm厚,所以收到PCB后,只要使用剪刀沿白色丝印剪下,即可获得半槽使用的TF WiFi模块!

TF Wifi卡的多种使用方法:

http://www.elecfans.com/tt/images/lichenpizerotfwifiuseful20170309.gif

直插板载卡槽
注意Zero默认使用TF卡启动,若要使用板子插槽接TF WiFi模块,则需要从SPI Flash启动。
2.54插针叠加

当使用TF卡作为启动介质而占用了板载TF卡槽时,仍可使用2.54插针轻松叠加使用!

Zero精心排布了2.54插针的引脚顺序,并在TF Wifi 模块上预留了相同顺序的2.54mm间距焊盘,因此可以直接使用2.54mm插针叠加!

*注意右侧刚好没有和FPC40的翻盖干涉,仍可使用座子外接屏幕等外设~ *
直插底板的TF卡槽

底板上也预留了TF卡槽,因此选购了底板的话可以直插底板上的卡槽。

底板其余部分先打码保持神秘感^_^

通用40P RGB液晶屏

Zero采用了和One兼容的通用40P RGB液晶屏(含触摸屏),并且板载了电阻屏控制器,支持触控操作。之前买了One的液晶屏套餐的可复用之前的液晶屏。

下图是5寸 480x800高清液晶屏,另有480x272的4.3寸普清液晶屏。

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c1013bc757f.jpg

RGB转VGA模块

去年年中以来,液晶屏价格暴涨,如果无法接受目前液晶屏价格的话,可以选择RGB转VGA模块,可接普通显示器使用。 http://z.elecfans.com/uploads/ueditor/2017-03-09/58c101ba18a76.jpg

RGB转HDMI模块

如果你的显示器没有VGA接口(土豪高端显示器),可选用RGB转HDMI模块,该模块可直插HDMI显示器。

注意:截止发稿时,笔者还没时间搞这模块的驱动,该驱动列入开发者奖励计划(详见后文)。

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c101e943920.jpg

RGB转LVDS模块

如果你手中有闲置的笔记本显示屏(非高清,1280x800, 1366x768或更低分辨率),则可选购该模块。注意笔记本屏幕一般需要额外的LED背光电源供电。

该模块支持单八,单六 LVDS屏;正面FPC座为单八接口,背面为单六接口。

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c101fccaa15.jpg

如果你手头没有闲置笔记本液晶屏,又想尝试大屏,众筹套餐中提供了10.1寸LVDS平板屏幕,1280x800分辨率,内置led恒流升压驱动电路。

RGB转DVP CSI模块

Zero的DVP CSI接口引脚和RGB引脚复用,所以之前购买了One的摄像头的用户,可以使用此转接板转接原DVP接口的24Pin摄像头(OV7670,OV2640等)。

注意:截止发稿时,笔者还没时间搞改摄像头驱动,该驱动列入开发者奖励计划(详见后文)。

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c102449522d.jpg

RGB转GPIO模块

如果你没用到LCD屏,又需要很多IO,则可以使用该RGB转GPIO模块,它将转出22个GPIO,以及两路ADC(使用电阻屏驱动器),和若干电源引脚。 http://z.elecfans.com/uploads/ueditor/2017-03-09/58c1022c30039.jpg

添翼

前面介绍的模块主要是FPC40的转接板,对于Zero上1.27邮票孔引出的其它引脚,除了可以用2.54插针偏移半位焊接引出外,也可使用配套底板(Docker)直接贴片上去,更加紧凑,也方便验证、使用各个高速接口。

Docker布局概览: http://z.elecfans.com/uploads/ueditor/2017-03-09/58c1027e9fed7.png


实物图镇楼:

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c104931d7c4.jpg


底板左下方有两三排插针,分别是I2C,SPI,UART,PWM的引出插针。

右下方有四个功能按键,是使用Zero的ADC功能实现的。

右侧为usb供电口,电池接口,USB OTG口;底板的电源电路支持单节锂电池的充放电管理,以及5V升压。

右上方为内置网络变压器的RJ45接口。

注意到RJ45接口较高,体积较大,所以默认不焊接上去,只附上该座子,有需要验证以太网口的朋友可自行焊接;需要DIY掌上设备的朋友可留空该座来减小底板体积。

底板上方为美标四线耳机座和高灵敏度麦克风。

底板下方配有一个24pin MIPI的摄像头座,座子为b2b连接器(axe624124)。 配套的是500W像素自动对焦的OV5648摄像头。

注意:截止发稿时,笔者还没时间搞这模块的驱动,该驱动列入开发者奖励计划(详见后文)。该摄像头在官方SDK里有驱动,但尚未在主线内核中适配。

底板的功放模块

Zero自带CODEC,但自带的CODEC只能推动耳机,所以底板上贴的是耳机座。

考虑到用Zero做WiFi音箱或者其它自带扬声器发声的设备(如掌机?),底板上预留了功放模块的接口,即TF卡槽后面的5pin 排母。

在该接口上可堆叠PA模块,如下图所示 http://z.elecfans.com/uploads/ueditor/2017-03-09/58c104a771537.jpg

PA模块可带动3W*2个扬声器,并自带音量旋钮,适合做一些手持设备。

以下是可选配的扬声器: http://z.elecfans.com/uploads/ueditor/2017-03-09/58c104c3bcc1d.jpg http://z.elecfans.com/uploads/ueditor/2017-03-09/58c104c4102b6.jpg

也可搭配骨传导扬声器!可做骨传导耳机或者振动音箱~

底板的显示模块

底板下方有一排2.54排座,可用于插接I2C接口的OLED或者SPI接口的TFT。

0.96寸128x64 OLED: http://z.elecfans.com/uploads/ueditor/2017-03-09/58c105128edc5.jpg

1.3寸128x64 OLED: http://z.elecfans.com/uploads/ueditor/2017-03-09/58c1052197743.jpg

2.4寸 240x320 TFT: http://z.elecfans.com/uploads/ueditor/2017-03-09/58c1052f744a7.jpg

底板的输入设备

底板的输入除了四个ADC做的功能按键外,还可选配下小手柄/键盘。

手柄与2.4寸屏幕搭配食用风味更佳 /斜眼笑 http://www.elecfans.com/tt/images/lichenpizerokeyboard20170309.gif

底板的通信接口

底板上有三个通信接口,分别是TF Wifi卡接口,RJ45 以太网接口,和Lora模块接口。


前面介绍的TF Wifi卡插底板的示意图:

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c105c954d9a.jpg


底板背面的Lora模块示意图: http://z.elecfans.com/uploads/ueditor/2017-03-09/58c1059be3a9f.jpg

Lora模块本身可以全双工,但是Zero作为网关使用时,底部Lora模块配置为一发一收,可以显著降低丢包率。

Lora网关的相关参数为:

  1. 星形拓扑,空旷通信距离15km
  2. xxx

底板的语音助手外设

底板的耳机口可外接麦克风阵列模块,实现远场语音识别(声源单位,波束成型),阵列模块自带关键词唤醒功能。

麦克风阵列模块默认搭配高灵敏度驻极体麦克风,可选配硅麦克(一致性好)

http://z.elecfans.com/uploads/ueditor/2017-03-09/58c105e615e20.png

麦克风阵列的主要参数为:

  1. 板载6颗驻极体/硅麦克,24颗LED,可选配OLED
  2. 声源定位精度优于15°,板上LED或者OLED指示方位
  3. 消除混响,500ms以内室内混响
  4. 波束成形,可指定6方位波束成形,具有噪声抑制和语音增强功能,室内环境下语音识别距离约5m。
  5. 关键词唤醒,可设定自定义关键词唤醒(使用拼音配置,方便快捷,无需上传服务器计算特征码),引脚输出唤醒边沿。
  6. 一路模拟音频输出,一路I2S数字音频输出。
  7. 可使用I2C/UART配置模块参数,读取信息(声源角度等)

惊艳

Zero核心板或者配套底板还可选配无线键鼠与锂电池,搭配定制的LCD转轴支架,简单组装即成一台迷你笔记本!

注:Zero底板具有锂电池充放电管理功能;Zero核心板不可直接给锂电池充电。

使用Zero核心板制作的简易“笔记本电脑”,只需一块电池,一个屏幕,一个无线键鼠。 https://cdn.instructables.com/FAI/3MH2/IZT6PA8U/FAI3MH2IZT6PA8U.LARGE.jpg

Zero开启debian桌面系统 https://cdn.instructables.com/FCG/L1US/IZT6PA9W/FCGL1USIZT6PA9W.LARGE.jpg https://cdn.instructables.com/FP2/N6UK/IZT6PA9Y/FP2N6UKIZT6PA9Y.LARGE.jpg

超级玛丽毫无压力 http://odfef978i.bkt.clouddn.com/%E6%96%B0%E5%BB%BA%E6%96%87%E6%9C%AC%E6%96%87%E6%A1%A3.png

回顾GBA经典游戏 https://cdn.instructables.com/FAI/QFQF/IZT6PAAT/FAIQFQFIZT6PAAT.LARGE.jpg

Zero畅玩经典游戏DOOM! https://cdn.instructables.com/FFZ/5W91/IZT6PABS/FFZ5W91IZT6PABS.LARGE.jpg


荔枝派Zero还可以运行树莓派系统!原来你是披着树莓皮的的荔枝派!

注:Zero由于内存限制,运行树莓派系统会较为卡顿。

https://cdn.instructables.com/FVM/EITJ/IZT6PA9O/FVMEITJIZT6PA9O.LARGE.jpg

开启键盘夜光的靓图!是不是有大牌范呢? https://cdn.instructables.com/FV9/CQCM/IZT6PA96/FV9CQCMIZT6PA96.LARGE.jpg https://cdn.instructables.com/FR0/3XJ1/IZT6R3RK/FR03XJ1IZT6R3RK.LARGE.jpg

详情还可戳 视频演示 欣赏喔~

未完待续

配件全家福(貌似还漏了个别配件) http://z.elecfans.com/uploads/ueditor/2017-03-09/58c10639ced82.jpg

Linux使用小贴士

概要:
本文记录在Linux,特别是在Ubuntu/Debian下开发工程的一些小贴士。

ubuntu如何开启ssh?

桌面版ubutnu默认没有安装ssh服务器,无法通过ssh远程连接;需要安装服务器来启用ssh。

sudo apt-get install openssh-server

如何在ubuntu与windows间传递文件?

最简单的方式:下载WinSCP,即可互传文件。

或者也可以搭建Samba服务器,那样在windows上可以直接访问共享文件夹。

如何在ubuntu上搭建samba服务器并设置共享文件夹?

此处仅作最简单的匿名登陆方式介绍(以ubuntu为例):

  1. 首先安装samba

    sudo apt-get install samba

  2. 打开samba配置文件

    sudo vim /etc/samba/smb.conf

  3. 编辑修改

    ## Debugging/Accounting ####
    # share the dir without passwd
    
    security = user
    map to guest = Bad User
    
    
    # 在文件结尾添加如下行:
    [share]
    
    # 更改path为指定目录
    path=/your/path
    public=yes
    writable=yes
    
  4. 启动Samba服务:

    /etc/init.d/samba start

    注解

    将共享目录赋予权限 sudo chmod 777 your_path

    注:最好将共享目录的上级目录也赋予权限

板卡介绍

LicheePi Zero 简介

荔枝派Zero(下面简称Zero)是一款精致迷你的 Cortex-A7 核心板/开发板,可用于初学者学习linux或者商用于产品开发。 Zero在稍长于SD卡的尺寸上(~ 45x26mm)提供了丰富的外设(LCD,ETH,UART,SPI,I2C,PWM,SDIO...)和强劲的性能( 24M~1.2G, 64MB DDR )。

Zero的使用了精巧的PCB设计,使得开发和使用非常方便:

  • 直插面包板
  • 直插40P RGB LCD
  • 使用OTG口进行供电和数据传输(虚拟串口,虚拟以太网等)
  • 使用堆叠式的WiFi 模块联网
  • 直接贴片

Zero提供了 主线Linux 支持 和 官方原生Camdriod(适用于行车记录仪应用)SDK,可在linux上使用任意你熟悉的语音编程。

Zero上手提示

对于刚入坑的小白,请先看以下几点基础说明:

  1. Zero需要插卡启动(或者焊接spi flash),请不要问为什么插上usb没反应
  2. TF卡槽是下图中荔枝派logo上方的长方形插槽
  3. 收到Zero后看到主芯片上有连锡请不要慌张,这是设计如此(相同的电源管脚),请参阅 原理图
  4. Zero的系统调试串口是UART0,即下图右下方的“U0T R”标识的两个引脚
  5. Zero正面的led不是上电就闪烁的,请不要认为上电后led不亮就是坏的
  6. Zero的usb是OTG usb,既可以供电,又能通信(比如作为usb虚拟网口 与电脑共享网络
  7. Zero usb口下方的“G 5V”插针可以作为电源输入,使用串口小板的5V或者锂电池均可供电。
  8. 推荐的两边排针焊接方式是向下焊接。“G 5V”插针推荐向上焊接。
  9. 推荐的底层调试接法是:usb转串口小板接“U0T R”和“G 5V”。
  10. 推荐的联网方式是:usb虚拟网口 或者 tf wifi ;或者使用淘宝店里的 usb转网口HUB
  11. Zero毕竟是上G主频的Cortex-A7处理器,运行时温度在40~60℃,请不要认为芯片在此范围内的发热是短路。
  12. Zero 运行Linux空载电流约100mA,满载电流约150~180mA,插上LCD电流约200~300mA。不插卡上电电流约50~60mA。

如果收到板子后还有其他疑问,请加 官方交流QQ群:573832310 。 入群验证答案是...请拿起板子看下上面芯片的丝印标识,谢谢。

硬件资料

硬件参数

  • CPU: 全志V3S, ARM Cortex-A7, 最高1.2GHz

  • 内存: 集成64MB DDR2

  • 存储:
    • 预留SOP8 SPI Flash焊盘(可定制贴片8~32MB SPI Nor Flash,128MB Nand Flash);
    • 板载 半槽TF卡座,可TF启动。
  • 显示:
    • 通用 40P RGB LCD FPC座

      • 可直插常见的40P 4.3/5/7寸屏幕(板载背光驱动),通过转接板可插50P 7/9寸屏
      • 支持常见的272x480, 480x800,1024x600等分辨率
      • 板载电阻式触摸屏芯片,支持电阻屏
    • 板载RGB LED

  • 通信接口
    • SDIO x2,可搭配配套SDIO WiFi+BT 模块
    • SPI x1
    • I2C x2
    • UART x3
    • 100M Ether x1(含EPHY)
    • OTG USB x1
    • MIPI CSI x1
  • 其它接口
    • PWM x2
    • LRADC x1
    • Speakerx2 + Mic x1
  • 电气特性
    • Micro USB 5V供电; 2.54mm 插针 3.3V~5V供电; 1.27mm 邮票孔供电
    • 输出 3.3V 和 3.0V(AVCC),可选择输入RTC电压
    • 1GHz linux空载运行电流 90~100mA, 满载运行电流 ~180mA
    • 存储温度 -40~125℃,运行温度 -20~70℃

管脚定义

https://box.kancloud.cn/cff378c6c891e69aa4a1b0ea02fe7f97_1063x638.png

注解

图中UART0的TX RX画反了,以板子上的丝印为准。

Docker环境简明教程

这里给大家准备了搭建ok的环境,可以直接下载使用。

当然这里使用的不是大家熟悉的虚拟机镜像,而是docker镜像,所以也写个简单的使用教程。

传统的开发环境搭建 ,将在下章系统开发篇进行详细介绍

Docker开发环境

docker安装

什么是docker?

Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

本节只简单介绍docker开发环境的搭建,想要详细了解docker,可以查看本节的附录“Docker 命令速查”。

简而言之,我帮你搞好了docker镜像,你就不用自己再费力搭建啦。

docker下载安装
sudo apt-get install docker.io
docker version

安装成功后可见版本信息

Client version: 1.6.2
Client API version: 1.18
Go version (client): go1.2.1
Git commit (client): 7c8fca2
OS/Arch (client): linux/amd64
FATA[0000] Get http:///var/run/docker.sock/v1.18/version: dial unix /var/run/docker.sock: permission denied. Are you trying to connect to a TLS-enabled daemon without TLS?

默认情况下会报后面的错误,如果使用sudo就不会报错。不想每次都sudo的话,可以把用户加入到docker组。

//如果还没有 docker group 就添加一个(默认安装后已经有了)
//sudo groupadd docker
//将用户加入该 group 内。然后退出并重新登录就生效啦。
sudo gpasswd -a ${your_user_name} docker
//重启 docker 服务
sudo service docker restart
//切换当前会话到新 group, 或者关掉终端重新连接也会生效
//newgrp - docker

安装荔枝派开发镜像

docker pull zepan/licheepi
docker run -d -p 6666:22 zepan/licheepi /usr/sbin/sshd -D

这样就安装并开启的容器ssh服务,只需连接主机的6666端口,以root用户,licheepi密码登录即可进行开发操作。

Docker命令速查

Docker概念

镜像 :只读的模板

容器 :负责应用程序的运行。是从镜像创建的运行实例,互相隔离,安全的平台。

镜像只读 :容器在启动时创建一层可写层作为最上层。(类似overlayfs)

仓库 :集中存放镜像的地方,和github类似。https://hub.docker.com/

Docker 安装

sudo apt-get install docker.io
sudo gpasswd -a your_user_name docker               #加入用户组
sudo service docker restart
newgrp - docker             #使用新用户组,或者也可以断开终端重连生效
docker version              #查看docker版本信息

docker下各文件默认存放在/var/lib/docker下

du -h --max-depth=1 /var/lib/docker                #查看docker目录大小

Docker镜像操作

docker search licheepi               #搜索镜像
docker pull zepan/licheepi           #下载镜像
docker run zepan/licheepi apt-get install -y xxx             #在镜像中执行命令,安装某软件
docker commit -m="install something" -a="zepan"  container_id zepan/licheepi
docker login
docker push  zepan/licheepi

Docker容器操作

运行交互式的容器
docker run -i -t learn/tutorial /bin/bash        //开启一个交互式虚拟终端
        -i    交互式
        -t     虚拟终端
        -d    后台
        -P    端口映射

docker port  id    //查看端口映射情况

docker inspect  id    //查看容器详细状态
启动容器(后台模式)
docker run -d learn/tutorial /bin/sh -c "while true; do echo hello world; sleep 1; done"
返回容器id:350807154a3dd17309b23bb9a9a9897dd3fc91667a7d176aca42f390808e3019
通过ps查看正在运行的容器实例
zepan@ubuntu:~$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
350807154a3d        learn/tutorial      "/bin/sh -c 'while..."   31 seconds ago      Up 30 seconds                           blissful_lamport
docker ps -l    //查看最后运行的容器
docker ps -a    //查看所有容器
查看对应容器的输出:
docker logs 3508 或 blissful_lamport
docker logs -f    xxxx        //类似tail -f
停止/开始/重启容器:
docker stop 3508        //通过发送信号方式停止
docker kill 3508           //kill方式停止
docker start 3508        //start -i  交互式执行
docker restart 3508
删除容器
docker remove 3508
docker rm `docker ps -a -q`        //删除所有容器
主机容器互拷数据
docker cp id:/xxx/xx   /yyy/yy/
docker cp  /yyy/yy/    id:/xxx/xx
开启容器的ssh
docker run -d -p 6666:22 zepan/licheepi /usr/sbin/sshd -D

基础ubuntu系统配置

运行以下命令:

sudo apt-get update
sudo apt-get install iputils-ping vim git wget xz-utils bzip2 gcc device-tree-compiler python time make
wget https://releases.linaro.org/components/toolchain/binaries/latest/arm-linux-gnueabihf/gcc-linaro-7.1.1-2017.08-x86_64_arm-linux-gnueabihf.tar.xz

Zero的开发环境分类

Camdriod 官方SDK

Camdriod是V3S的官方开发环境,又被称为“坑卓”。

此坑卓非彼安卓,不能运行原生安卓apk,请不要妄想在64MB内存上跑安卓。。

坑卓 linux内核版本为3.4,系统配置方法是使用fex文件,对摄像头支持较好。

需要开发行车记录仪方案的,可以尝试使用坑卓。

荔枝派目前不提供对坑卓的技术支持。(有偿的也不提供)。

坑卓SDK下载:

芯片资料和坑卓开发说明:http://pan.baidu.com/s/1pLQbwuB

下载后解压,把两个文件夹放在同一目录下,按照坑卓开发说明开发即可。

主线Uboot + Bsp 内核

如果不想被坑卓坑, 又想比较好地使用摄像头,可以使用主线Uboot加Bsp内核方案, 系统配置为fex文件配置。

bsp内核即前面的lichee linux: http://pan.baidu.com/s/1eRJrViy

主线Uboot + 主线linux

如果想使用主线的特性,可以使用 主线Uboot + 主线linux 开发环境。系统配置为dts设备树配置。

Docker开发环境

如果不想自己配置繁琐的开发环境,请使用docker开发环境,免去所有配置烦恼。

Zero的文件系统

主要分为buildroot/LEDE,emdebian两类,前者较小,可以在spi flash(16/32MB)或者小容量TF卡(64/128MB)上运行。

主线Uboot

本节讲解主线Uboot的编译、配置及相关内容

Uboot 编译

基础编译

安装交叉编译器

网盘地址:http://pan.baidu.com/s/1hsf22fq

国外用户:https://releases.linaro.org/components/toolchain/binaries/latest/arm-linux-gnueabihf/

wget https://releases.linaro.org/components/toolchain/binaries/latest/arm-linux-gnueabihf/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf.tar.xz
tar xvf gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf.tar.xz
mv gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf /opt/
vim /etc/bash.bashrc
# add: PATH="$PATH:/opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin"
source /etc/bash.bashrc
arm-linux-gnueabihf-gcc -v
sudo apt-get install device-tree-compiler
下载编译Uboot
git clone https://github.com/Lichee-Pi/u-boot.git -b v3s-current
#or git clone https://github.com/Lichee-Pi/u-boot.git -b v3s-spi-experimental
cd u-boot
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LicheePi_Zero_800x480LCD_defconfig
#or make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LicheePi_Zero480x272LCD_defconfig
#or make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LicheePi_Zero_defconfig
make ARCH=arm menuconfig
time make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 2>&1 | tee build.log

编译完成后,在当前目录下生成了u-boot-sunxi-with-spl.bin,可以烧录到8K偏移处启动。

Uboot结构简介

下面来看看该uboot中的目录结构

├── api                存放uboot提供的API接口函数
├── arch               平台相关的部分我们只需要关心这个目录下的ARM文件夹
│   ├──arm
│   │   └──cpu
│   │   │   └──armv7
│   │   └──dts
│   │   │   └──*.dts 存放设备的dts,也就是设备配置相关的引脚信息
├── board              对于不同的平台的开发板对应的代码
├── cmd                顾名思义,大部分的命令的实现都在这个文件夹下面。
├── common             公共的代码
├── configs            各个板子的对应的配置文件都在里面,我们的Lichee配置也在里面
├── disk               对磁盘的一些操作都在这个文件夹里面,例如分区等。
├── doc                参考文档,这里面有很多跟平台等相关的使用文档。
├── drivers            各式各样的驱动文件都在这里面
├── dts                一种树形结构(device tree)这个应该是uboot新的语法
├── examples           官方给出的一些样例程序
├── fs                 文件系统,uboot会用到的一些文件系统
├── include            头文件,所有的头文件都在这个文件夹下面
├── lib                一些常用的库文件在这个文件夹下面
├── Licenses           这个其实跟编译无关了,就是一些license的声明
├── net                网络相关的,需要用的小型网络协议栈
├── post              上电自检程序
├── scripts           编译脚本和Makefile文件
├── spl               second program loader,即相当于二级uboot启动。
├── test              小型的单元测试程序。
└── tools             里面有很多uboot常用的工具。

了解了uboot的基本结构,我们可以知道一些相关的配置在什么地方了。

  • lichee的uboot配置文件放在confgs文件目录下面,名称为
LicheePi_Zero_480x272LCD_defconfig
LicheePi_Zero_800x480LCD_defconfig
LicheePi_Zero_defconfig

这3个配置是根据不同的Zero显示设备进行的配置,使用其中之一即可,可在uboot目录下执行命令

make LicheePi_Zero_defconfig

这样配置就生效了。

Uboot 配置

内容整理自: 贡献投稿篇 ‣ 投稿文章精选 ‣ Zero u-boot编译和使用指南

Uboot配置命令

make ARCH=arm menuconfig
https://box.kancloud.cn/c0bc403f54d5c23409af3dda76c6eb1e_1167x606.png
---按回车,即选择当前菜单
------- 按Y 代表该config选项选中
------- 按N 代表不选中该选项
-------- 按M 代表该驱动编译成*.ko的方式,在系统起来之后,当驱动需要的时候加载
</>---------按/ 可以查找某个选项
---------退出
<*> ----------按Y选中后的状态

这里面有几个常见的配置选项我们可以看下:

  1. 第一个Architecture select架构选择,不用质疑这个是ARM架构
  2. 第二个ARM architecture 这个选项比较重要,主要配置ARM框架下的常用的配置函数以及LCD等参数
https://box.kancloud.cn/e6935388a45eb157a0267b5e0f566414_654x362.png

DDR配置

...
Target select (Support sunxi (Allwinner) SoCs)   进去之后可以选择sunxi Soc系列芯片
...
[*] Sunxi SoC Variant     这个就是对芯片Soc 的选择,我们可以看到配置选择了`sun8i (Allwinner V3s)
(360) sunxi dram clock speed          配置dram的时钟速率
(14779) sunxi dram zq value             配置dram的ZQ值,是用来动态加强DDR3的
-*- Board uses DDR2 DRAM             使用DDR2 DRAM

LCD配置

https://box.kancloud.cn/e3c46cc8756651c4cd7943b824939964_745x364.png
[*] Enable graphical uboot console on HDMI, LCD or VGA   这个就是在显示设备上使能串口控制
[ ] VGA via LCD controller support             使能支持VGA通过LCD的控制器,就是LCD和VAG转换需要的控制器
(x:800,y:480,depth:18,pclk_khz:33000,le:87,ri:40,up:31,lo:13,hs:1,vs:1,sync:3,vmode:0) LCD pane
> 该选项就是配置LCD的分辨率的配置选项可以看到x是800 y是480 等等一些关于LCD的配置内容,点击回车进去可以对其进行修改。
(1)   LCD panel display clock phase               这个是LCD的显示时钟相位
()    LCD panel power enable pin               LCD的电源使能引脚
()    LCD panel reset pin                                             LCD的复位引脚
(PB4) LCD panel backlight pwm pin                    背光PWN引脚 这个应该是调节亮度的引脚PB4
[*]   LCD panel backlight pwm is inverted            反转PWN背光引脚
[ ]   LCD panel needs to be configured via i2c
    LCD panel support (Generic parallel interface LCD panel)  --->     这个选择支持的LCDpanel
            (X) Generic parallel interface LCD panel                   这里选择支持通用的并行的LCD接口
            ( ) Generic lvds interface LCD panel                       这个是LVDS接口
            ( ) MIPI 4-lane, 513Mbps LCD panel via SSD2828 bridge chip
            ( ) eDP 4-lane, 1.62G LCD panel via ANX9804 bridge chip
            ( ) Hitachi tx18d42vm LCD panel
            ( ) tl059wv5c0 LCD panel
(0) GMAC Transmit Clock Delay Chain

时钟频率配置

Boot images --->(1008000000) CPU clock frequency

这里设置了CPU的时钟频率

开机延时设置

delay in seconds before automatically booting

这个是uboot开机的时候的一个等待时间的秒数,可以改大一点,默认是2s

SPL配置

SPL / TPL ---> 这个就是SPL相关的配置了
[*]   MMC raw mode: by sector                       按扇区
(0x50)  Address on the MMC to load U-Boot from  mmc加载uboot的地址
[*] Support GPIO                                 支持GPIO
[*] Support I2C                                 支持I2C
[*] Support common libraries                    支持通用lib
[*] Support disk paritions                      支持分区
[*] Support generic libraries                   支持一般lib库
[*] Support MMC                                 支持MMC
[*] Support power drivers                  支持电源驱动
[*] Support serial                               支持串口

主线Kernel

本节主要介绍linux内核的基础编译与相关细节。

主线Kernel基础编译

安装交叉编译器

网盘地址:http://pan.baidu.com/s/1hsf22fq

国外用户:https://releases.linaro.org/components/toolchain/binaries/latest/arm-linux-gnueabihf/

wget https://releases.linaro.org/components/toolchain/binaries/latest/arm-linux-gnueabihf/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf.tar.xz
tar xvf gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf.tar.xz
mv gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf /opt/
vim /etc/bash.bashrc
# add: PATH="$PATH:/opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin"
arm-linux-gnueabihf-gcc -v

下载编译linux源码

(默认是zero-4.10.y分支):

git clone https://github.com/Lichee-Pi/linux.git
cd linux
make ARCH=arm licheepi_zero_defconfig
make ARCH=arm menuconfig   #add bluethooth, etc.
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16 INSTALL_MOD_PATH=out modules
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16 INSTALL_MOD_PATH=out modules_install

编译完成后,zImage在arch/arm/boot/下,驱动模块在out/下

内核printk等级设置

  1. 查看当前控制台的打印级别

    cat /proc/sys/kernel/printk

    4 4 1 7

    其中第一个“4”表示内核打印函数printk的打印级别,只有级别比他高的信息才能在控制台上打印出来,既 0-3级别的信息

  2. 修改打印

    echo "新的打印级别 4 1 7" >/proc/sys/kernel/printk

3. 不够打印级别的信息会被写到日志中可通过dmesg 命令来查看 4.printk的打印级别

#define KERN_EMERG           "<0>" /* system is unusable */
#define KERN_ALERT           "<1>" /* action must be taken immediately */
#define KERN_CRIT            "<2>" /* critical conditions */
#define KERN_ERR             "<3>" /* error conditions */
#define KERN_WARNING         "<4>" /* warning conditions */
#define KERN_NOTICE          "<5>" /* normal but significant condition */
#define KERN_INFO            "<6>" /* informational */
#define KERN_DEBUG           "<7>" /* debug-level messages */
  1. printk函数的使用

    printk(打印级别 “要打印的信息”)

    打印级别 既上面定义的几个宏

BSP内核编译

由于主线内核暂不支持摄像头控制器,所以需要使用BSP内核来使能摄像头驱动。

本篇主要讲解编译启动BSP内核的方法。

BSP源码下载与编译

BSP内核剥离

BSP内核对摄像头驱动支持较好,所以在摄像头应用中有必要使用BSP内核。

官方SDK中camdriod与lichee linux内核绑定,而camdriod比较庞大,所以我们只需抽取lichee linux内核,而抛弃camdriod代码。

单独使用lichee linux的方法是:(构建走读见后一节)

解压 buildroot/dl/gcc-linarno.tar.gzlichee/out/sun8iw8p1/linux/common/buildroot/external-toolchain,并加入环境变量(这步其实在下一步里包含了)

执行 build_tiger-cdr.sh

执行过程中会生成内核文件:

Image Name:   Linux-3.4.39
Created:      Tue Sep  5 06:55:03 2017
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    2275400 Bytes = 2222.07 kB = 2.17 MB
Load Address: 40008000
Entry Point:  40008000
  Image arch/arm/boot/uImage is ready
'arch/arm/boot/Image' -> 'output/bImage'
'arch/arm/boot/uImage' -> 'output/uImage'
'arch/arm/boot/zImage' -> 'output/zImage'
Building modules

compile Kernel successful

从主线uboot启动内核,一般使用uImage。

BSP内核配置

BSP内核源码在lichee/linux-3.4下。

通过 make ARCH=arm menuconfig 来配置内核。

我们可以使能摄像头需要修改的一些内核配置:

使能USB摄像头驱动:
-> Device Drivers
  x       -> Multimedia support (MEDIA_SUPPORT [=y])
  x         -> Video capture adapters (VIDEO_CAPTURE_DRIVERS [=y])
  x           -> V4L USB devices (V4L_USB_DRIVERS [=y])
  x                          -><M>   USB Video Class (UVC)  CONFIG_USB_VIDEO_CLASS
使能DVP/MIPI摄像头
-> Device Drivers
  x       -> Multimedia support (MEDIA_SUPPORT [=y])
  x         -> Video capture adapters (VIDEO_CAPTURE_DRIVERS [=y])
  x           -> V4L platform devices (V4L_PLATFORM_DRIVERS [=y])
  x             -> SoC camera support (SOC_CAMERA [=y])
  x x                          <M>     ov2640 camera support
  x x                          <M>     ov5647 camera support
  x x                          <M>   sunxi video front end (camera and etc)driver
  x x                          <M>     v4l2 driver for SUNXI
   <*>   sunxi video encoder and decoder support

由于camdriod原始的内核配置是为了在spi nor flash上运行而配置的,没有ext4支持,所以需要额外添加ext4支持:

<*> The Extended 4 (ext4) filesystem
  x x                          [*]   Use ext4 for ext2/ext3 file systems (NEW)
  x x                          [*]   Ext4 extended attributes (NEW)
  x x                          [ ]     Ext4 POSIX Access Control Lists (NEW)
  x x                          [ ]     Ext4 Security Labels (NEW)
  x x                          [ ]   EXT4 debugging support (NEW)
  x x                          [ ] JBD2 (ext4) debugging support (NEW)

另外还要加上CONFIG_LBDAF(大文件支持,否则无法挂载文件系统)

-> Enable the block layer (BLOCK [=y])
[*]   Support for large (2TB+) block devices and files

再加上CGROUPS支持:

-> General setup
 [*] Control Group support  --->

如果在文件系统(如debian)中使用了SWAP等特性,则还需要在内核中开启SWAP。

debian下还需要开启 FHANDLE 特性,否则会出现以下错误

A start job is running for dev-ttyS0.device
timeout

如果需要使用wifi功能,则还需要勾选RTL8723BS的支持(注意需要选择模块方式),和AW_RF_PM选项。

以及下节所说的fex修改。

uboot启动BSP内核

使用主线uboot启动BSP内核,需要修改下启动脚本,放入BSP内核需要的 script.bin 配置文件(相当于主线linux的dtb)

修改boot.cmd:
setenv bootargs console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw
setenv bootm_boot_mode sec
setenv machid 1029
load mmc 0:1 0x41000000 uImage
load mmc 0:1 0x41d00000 script.bin
bootm 0x41000000

重新生成boot.scr:

mkimage -C none -A arm -T script -d boot.cmd boot.scr

将boot.scr放入第一分区。

再配置生成script.bin.

复制一份 lichee/tools/pack/chips/sun8iw8p1/configs/tiger-cdr/sys_config.fex

修改其中的摄像头配置:

首先修改SD卡检测策略,设置为不检测,默认插入

sdc_detmode=3

使能RTL8723bs无线网卡的话,需要使能mmc1,也设置为不检测sd卡。

在设置摄像头型号,csi0是mipi摄像头,csi1是dvp摄像头。

这里默认以mipi摄像头为ov5647, dvp摄像头为ov2640 为例。

;--------------------------------------------------------------------------------
;vip (video input port) configuration
;vip_used: 0:disable 1:enable
;vip_mode: 0:sample one interface to one buffer 1:sample two interface to one buffer
;vip_dev_qty: The quantity of devices linked to capture bus
;
;vip_define_sensor_list: If you want use sensor detect function, please set vip_define_sensor_list = 1, and
;                                    verify that file /system/etc/hawkview/sensor_list_cfg.ini is properly configured!
;
;vip_dev(x)_pos: sensor position, "rear" or "front", if vip_define_sensor_list = 1,vip_dev(x)_pos must be configured!
;
;vip_dev(x)_isp_used 0:not use isp 1:use isp
;vip_dev(x)_fmt: 0:yuv 1:bayer raw rgb
;vip_dev(x)_stby_mode: 0:not shut down power at standby 1:shut down power at standby
;vip_dev(x)_vflip: flip in vertical direction 0:disable 1:enable
;vip_dev(x)_hflip: flip in horizontal direction 0:disable 1:enable
;vip_dev(x)_iovdd: camera module io power handle string, pmu power supply
;vip_dev(x)_iovdd_vol: camera module io power voltage, pmu power supply
;vip_dev(x)_avdd: camera module analog power handle string, pmu power supply
;vip_dev(x)_avdd_vol: camera module analog power voltage, pmu power supply
;vip_dev(x)_dvdd: camera module core power handle string, pmu power supply
;vip_dev(x)_dvdd_vol: camera module core power voltage, pmu power supply
;vip_dev(x)_afvdd: camera module vcm power handle string, pmu power supply
;vip_dev(x)_afvdd_vol: camera module vcm power voltage, pmu power supply
;x indicates the index of the devices which are linked to the same capture bus
;fill voltage in uV, e.g. iovdd = 2.8V, vip_devx_iovdd_vol = 2800000
;fill handle string as below:
;axp22_eldo3
;axp22_dldo4
;axp22_eldo2
;fill handle string "" when not using any pmu power supply
;--------------------------------------------------------------------------------

[csi0]
vip_used                 = 1
vip_mode                 = 0
vip_dev_qty              = 1
vip_define_sensor_list   = 0
vip_csi_mck              = port:PE20<3><default><default><default>
vip_csi_sck              = port:PE21<2><default><default><default>
vip_csi_sda              = port:PE22<2><default><default><default>
vip_dev0_mname           = "h22_mipi"
vip_dev0_pos             = "rear"
vip_dev0_lane            = 1
vip_dev0_twi_id          = 0
vip_dev0_twi_addr        = 0x60
vip_dev0_isp_used        = 1
vip_dev0_fmt             = 1
vip_dev0_stby_mode       = 0
vip_dev0_vflip           = 0
vip_dev0_hflip           = 0
vip_dev0_iovdd           = ""
vip_dev0_iovdd_vol       = 3000000
vip_dev0_avdd            = "csi-avdd"
vip_dev0_avdd_vol        = 3000000
vip_dev0_dvdd            = "csi-dvdd"
vip_dev0_dvdd_vol        = 3000000
vip_dev0_afvdd           = ""
vip_dev0_afvdd_vol       = 2800000
vip_dev0_power_en        =
vip_dev0_reset           = port:PG00<1><default><default><default>
vip_dev0_pwdn            = port:PG01<1><default><default><default>
vip_dev0_flash_en        =
vip_dev0_flash_mode      =
vip_dev0_af_pwdn         =
vip_dev0_act_used        = 0
vip_dev0_act_name        = "dw9714_act"
vip_dev0_act_slave       = 0x18
vip_dev1_mname           = ""
vip_dev1_pos                 = "front"
vip_dev1_lane            = 1
vip_dev1_twi_id          = 0
vip_dev1_twi_addr        =
vip_dev1_isp_used        = 0
vip_dev1_fmt             = 1
vip_dev1_stby_mode       = 0
vip_dev1_vflip           = 0
vip_dev1_hflip           = 0
vip_dev1_iovdd           = ""
vip_dev1_iovdd_vol       = 2800000
vip_dev1_avdd            = ""
vip_dev1_avdd_vol        = 2800000
vip_dev1_dvdd            = ""
vip_dev1_dvdd_vol        = 1500000
vip_dev1_afvdd           = ""
vip_dev1_afvdd_vol       = 2800000
vip_dev1_power_en        =
vip_dev1_reset           =
vip_dev1_pwdn            =
vip_dev1_flash_en        =
;fill handle string as below:
;axp22_eldo3
;axp22_dldo4
;axp22_eldo2
;fill handle string "" when not using any pmu power supply
;--------------------------------------------------------------------------------

[csi0]

vip_used                 = 1
vip_mode                 = 0
vip_dev_qty              = 1
vip_define_sensor_list   = 0
vip_csi_mck              = port:PE20<3><default><default><default>
vip_csi_sck              = port:PE21<2><default><default><default>
vip_csi_sda              = port:PE22<2><default><default><default>
vip_dev0_mname           = "h22_mipi"
vip_dev0_pos             = "rear"
vip_dev0_lane            = 1
vip_dev0_twi_id          = 0
vip_dev0_twi_addr        = 0x60
vip_dev0_isp_used        = 1
vip_dev0_fmt             = 1
vip_dev0_stby_mode       = 0
vip_dev0_vflip           = 0
vip_dev0_hflip           = 0
vip_dev0_iovdd           = ""
vip_dev0_iovdd_vol       = 3000000
vip_dev0_avdd            = "csi-avdd"
vip_dev0_avdd_vol        = 3000000
vip_dev0_dvdd            = "csi-dvdd"
vip_dev0_dvdd_vol        = 3000000
vip_dev0_afvdd           = ""
vip_dev0_afvdd_vol       = 2800000
vip_dev0_power_en        =
vip_dev0_reset           = port:PG00<1><default><default><default>
vip_dev0_pwdn            = port:PG01<1><default><default><default>
vip_dev0_flash_en        =
vip_dev0_flash_mode      =
vip_dev0_af_pwdn         =
vip_dev0_act_used        = 0
vip_dev0_act_name        = "dw9714_act"
vip_dev0_act_slave       = 0x18
vip_dev1_mname           = ""
vip_dev1_pos                 = "front"
vip_dev1_lane            = 1
vip_dev1_twi_id          = 0
vip_dev1_twi_addr        =
vip_dev1_isp_used        = 0
vip_dev1_fmt             = 1
vip_dev1_stby_mode       = 0
vip_dev1_vflip           = 0
vip_dev1_hflip           = 0
vip_dev1_iovdd           = ""
vip_dev1_iovdd_vol       = 2800000
vip_dev1_avdd            = ""
vip_dev1_avdd_vol        = 2800000
vip_dev1_dvdd            = ""
vip_dev1_dvdd_vol        = 1500000
vip_dev1_afvdd           = ""
vip_dev1_afvdd_vol       = 2800000
vip_dev1_power_en        =
vip_dev1_reset           =
vip_dev1_pwdn            =
vip_dev1_flash_en        =
vip_dev1_flash_mode      =
vip_dev1_af_pwdn         =

[csi1]
vip_used                 = 0
vip_mode                 = 0
vip_dev_qty              = 1
vip_define_sensor_list   = 0
vip_csi_pck              = port:PE00<2><default><default><default>
vip_csi_mck              = port:PE01<2><default><default><default>
vip_csi_hsync            = port:PE02<2><default><default><default>
vip_csi_vsync            = port:PE03<2><default><default><default>
vip_csi_d0               = port:PE04<2><default><default><default>
vip_csi_d1               = port:PE05<2><default><default><default>
vip_csi_d2               = port:PE06<2><default><default><default>
vip_csi_d3               = port:PE07<2><default><default><default>
vip_csi_d4               = port:PE08<2><default><default><default>
vip_csi_d5               = port:PE09<2><default><default><default>
vip_csi_d6               = port:PE10<2><default><default><default>
vip_csi_d7               = port:PE11<2><default><default><default>
vip_csi_d8               = port:PE12<2><default><default><default>
;vip_csi_d9               = port:PE13<2><default><default><default>
vip_csi_d10               = port:PE14<2><default><default><default>
vip_csi_d11               = port:PE15<2><default><default><default>

vip_csi_sck               = port:PE21<2><default><default><default>
vip_csi_sda               = port:PE22<2><default><default><default>

vip_dev0_mname           = "ov5640"
vip_dev0_pos             = "front"
vip_dev0_twi_id          = 4
vip_dev0_twi_addr        = 0x78
vip_dev0_isp_used        = 0
vip_dev0_fmt             = 0
vip_dev0_stby_mode       = 0
vip_dev0_vflip           = 0
vip_dev0_hflip           = 0
vip_dev0_iovdd           = ""
vip_dev0_iovdd_vol       = 2800000
vip_dev0_avdd            = ""
vip_dev0_avdd_vol        = 2800000
vip_dev0_dvdd            = ""
vip_dev0_dvdd_vol        = 1500000
vip_dev0_afvdd           = ""
vip_dev0_afvdd_vol       = 2800000
vip_dev0_power_en        =
vip_dev0_reset           = port:PE23<1><default><default><default>
vip_dev0_pwdn            = port:PE24<1><default><default><default>
vip_dev0_flash_en        =
vip_dev0_flash_mode      =
vip_dev0_af_pwdn         =

vip_dev0_act_used        = 0
vip_dev0_act_name        = "ad5820_act"
vip_dev0_act_slave       = 0x18

vip_dev1_mname           = "gc2035"
vip_dev1_pos                 = "front"
vip_dev1_lane            = 1
vip_dev1_twi_id          = 4
vip_dev1_twi_addr        = 0x78
vip_dev1_isp_used        = 0
vip_dev1_fmt             = 1
vip_dev1_stby_mode       = 0
vip_dev1_vflip           = 0
vip_dev1_hflip           = 0
vip_dev1_iovdd           = ""
vip_dev1_iovdd_vol       = 2800000
vip_dev1_avdd            = ""
vip_dev1_avdd_vol        = 2800000
vip_dev1_dvdd            = ""
vip_dev1_dvdd_vol        = 1500000
vip_dev1_afvdd           = ""
vip_dev1_afvdd_vol       = 2800000
vip_dev1_power_en        =
vip_dev1_reset           =
vip_dev1_pwdn            =
vip_dev1_flash_en        =
vip_dev1_flash_mode      =
vip_dev1_af_pwdn         =
将其中的摄像头信息改成自己使用的摄像头信息。
保存,并使用 fex2bin sys_config.fex script.bin 生成script.bin文件。
将script.bin也放入第一分区。
再将前面编译的uImage放入第一分区。
此时就可以使用主线Uboot启动bsp内核了。

BSP内核构建走读

camdriod中对lichee的构建过程

使用BSP内核的话,需要使用官方sdk编译camdriod并从中取出uImage。

鉴于camdriod较大,所以我们先简单走读下camdriod中对lichee的构建过程,扣出这部分内容。

camdriod的构建脚本:

#!/bin/bash
source build/envsetup.sh &&
lunch &&
mklichee &&
extract-bsp &&
make -j8 &&
pack

source build/envsetup.sh 这里是include各种环境变量,比如就添加了 tiger_cdr-eng 选项,这里先不管它。

lunch就是选择目标板配置了,是在 build/envsetup.sh 中的函数。

接着就是关键的mklichee了,在 device/softwinner/common/vendorsetup.sh 里,深入看下:

首先是一堆常量定义,放这里备查
 function set_environment()
 {
         LICHEE_CHIP="sun8iw8p1"
         export CAMLINUX_BUILD_TOP=`pwd`
         export DEVICE_DIR=$CAMLINUX_BUILD_TOP/device/softwinner
         export TARGET_OUT=$CAMLINUX_BUILD_TOP/output
         export OUT_DIR=$CAMLINUX_BUILD_TOP/out
         export LICHEE_DIR=$CAMLINUX_BUILD_TOP/../lichee
         export LICHEE_OUT_DIR=$LICHEE_DIR/out/sun8iw8p1/linux
         export BR_ROOTFS_DIR=$TARGET_OUT/target
         export LICHEE_BR_DIR=${LICHEE_DIR}/buildroot
         export LICHEE_KERN_DIR=${LICHEE_DIR}/linux-3.4
         export LINUXOUT_MODULE_DIR=$LICHEE_KERN_DIR/output/lib/modules/*/*
         export LICHEE_KERN_OUTDIR=$LICHEE_KERN_DIR/output
         export LICHEE_TOOLS_DIR=${LICHEE_DIR}/tools
         export LICHEE_UBOOT_DIR=${LICHEE_DIR}/brandy/u-boot-2011.09
 }
mklichee本体:
 function mklichee()
 {
     mksetting               #显示目前的配置信息
     mk_info "build lichee ..."
         mkbr && mkkernel
     #    mkbr && mkkernel && mkuboot
     [ $? -ne 0 ] && return 1
         return 0
 }

mkbr是运行了 buildroot/scripts/build.sh buildroot linux sun8iw8p1

gcc-linaro.tar.bz2
 function mkbr()
 {
     mk_info "build buildroot ..."
     local build_script
     build_script="scripts/build.sh"
         LICHEE_PLATFORM="linux"

     (cd ${LICHEE_BR_DIR} && [ -x ${build_script} ] && ./${build_script} "buildroot" ${LICHEE_PLATFORM} ${LICHEE_CHIP})
     [ $? -ne 0 ] && mk_error "build buildroot Failed" && return 1
     mk_info "build buildroot OK."
 }
build.sh写着:
 if [ "x${LICHEE_PLATFORM}" = "xlinux" ] ; then
     build_buildroot
     export PATH=${LICHEE_BR_OUT}/external-toolchain/bin:$PATH
     build_external
 else
     build_toolchain

LICHEE_PLATFORM并没有被传入,实际执行的是下面这个build_toolchain:

build_toolchain:
 build_toolchain()
 {
     local tooldir="${LICHEE_BR_OUT}/external-toolchain"
     mkdir -p ${tooldir}
     if [ -f ${tooldir}/.installed ] ; then
         printf "external toolchain has been installed\n"
     else
         printf "installing external toolchain\n"
         printf "please wait for a few minutes ...\n"
         tar --strip-components=1 \
             -jxf ${LICHEE_BR_DIR}/dl/gcc-linaro.tar.bz2 \
             -C ${tooldir}
         [ $? -eq 0 ] && touch ${tooldir}/.installed
     fi

     export PATH=${tooldir}/bin:${PATH}
 }

这里LICHEE_BR_OUT是 lichee/out/sun8iw8p1/linux/common/buildroot

LICHEE_BR_DIR是lichee/buildroot, 需要先导入。

这里使用的linaro版本比较老。注意如果使用较新的gcc反而会出错。

这里就是解压了linaro-gcc,并加入到环境变量。

mkbr看完了,接下来看mkkernel。

mkkernel
 function mkkernel()
 {
 local platformdef=$tdevice
     if [ ! -n $tdevice ]; then
             echo "Please lunch device"
             return 1
     fi

     echo "Make the kernel"
     echo "platformdef="${platformdef}
     (cd ${LICHEE_KERN_DIR}/; ./build.sh -p ${platformdef})
     [ $? -ne 0 ] && mk_error "build mkkernel fail" && return 1
 echo "Make the kernel finish"
     return 0
 }

执行的是lichee/linux-3.4/build.sh, 跟下去是执行了:

./scripts/build_${PLATFORM}.sh all

看script目录下,有:

build_crane-cdr.sh
build_crane-ipc.sh
build_crane-sdv.sh
build_crane-standard.sh
build_rootfs.sh
build.sh
build_sun6i.sh
build_sun8iw8p1.sh
build_tiger-cdr.sh
build_tiger-ipc.sh
build_tiger-standard.sh

就是代表的可以构建的板子型号。我们实际编译的时候只需要执行 build_tiger-cdr.sh 即可。

综上所述,需要剥离camdriod所用的lichee内核,只需要:

  1. 解压buildroot/dl/gcc-linarno.tar.gz 到lichee/out/sun8iw8p1/linux/common/buildroot/external-toolchain,并加入环境变量(这步其实在下一步里包含了)
  2. 执行build_tiger-cdr.sh

启动信息

Starting kernel ...

[sun8i_fixup]: From boot, get meminfo:
    Start:  0x40000000
    Size:   64MB
ion_carveout reserve: 28m@0 28m@0
ion_reserve_common: ion reserve: [0x42400000, 0x44000000]!
[    0.000000] Booting Linux on physical CPU 0
[    0.000000] Linux version 3.4.39 (root@bf756b445919) (gcc version 4.6.3 20120201 (prerelease) (crosstool-NG linaro-1.13.1-2012.02-20120222 - Linaro GCC 2012.02) ) #29 Wed Nov 29 10:53:16 UTC 2017
[    0.000000] bootconsole [earlycon0] enabled
[    0.000000] Initialized persistent memory from 41d20800-41d307ff
[    0.000000] Kernel command line: console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw
[    0.000000] PID hash table entries: 256 (order: -2, 1024 bytes)
[    0.000000] Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
[    0.000000] Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
[    0.000000] Memory: 64MB = 64MB total
[    0.000000] Memory: 29312k/29312k available, 36224k reserved, 0K highmem
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xfff00000 - 0xfffe0000   ( 896 kB)
[    0.000000]     vmalloc : 0xc4800000 - 0xff000000   ( 936 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xc4000000   (  64 MB)
[    0.000000]     modules : 0xbf000000 - 0xc0000000   (  16 MB)
[    0.000000]       .text : 0xc0008000 - 0xc050d000   (5140 kB)
[    0.000000]       .init : 0xc050d000 - 0xc0530000   ( 140 kB)
[    0.000000]       .data : 0xc0530000 - 0xc05ab500   ( 494 kB)
[    0.000000]        .bss : 0xc05ab524 - 0xc068c28c   ( 900 kB)
[    0.000000] NR_IRQS:544
[    0.000000] Architected local timer running at 24.00MHz.
[    0.000000] Switching to timer-based delay loop
[    0.000000] sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 178956ms
[    0.000000] Console: colour dummy device 80x30
[    0.014545] Calibrating delay loop (skipped), value calculated using timer frequency.. 4800.00 BogoMIPS (lpj=24000000)
[    0.022936] pid_max: default: 32768 minimum: 301
[    0.027778] Mount-cache hash table entries: 512
[    0.030637] CPU: Testing write buffer coherency: ok
[    0.035150] Setting up static identity map for 0x40396048 - 0x403960a0
[    0.040773] devtmpfs: initialized
[    0.045346] pinctrl core: initialized pinctrl subsystem
[    0.049168] NET: Registered protocol family 16
[    0.050362] DMA: preallocated 128 KiB pool for atomic coherent allocations
[    0.056956] script_sysfs_init success
[    0.060951] gpiochip_add: registered GPIOs 0 to 223 on device: sunxi-pinctrl
[    0.068164] sunxi-pinctrl sunxi-pinctrl: initialized sunXi PIO driver
[    0.070438] gpiochip_add: registered GPIOs 1024 to 1031 on device: axp-pinctrl
[    0.078235] persistent_ram: uncorrectable error in header
[    0.080020] persistent_ram: no valid data in buffer (sig = 0x75371537)
[    0.091722] console [ram-1] enabled
[    0.092266] Not Found clk pll_isp in script
[    0.094113] Not Found clk pll_video in script
[    0.098719] Not Found clk pll_ve in script
[    0.100012] Not Found clk pll_periph0 in script
[    0.104666] Not Found clk pll_de in script
[    0.113517] bio: create slab <bio-0> at 0
[    0.113966] pwm module init!
[    0.118375] SCSI subsystem initialized
[    0.120028] usbcore: registered new interface driver usbfs
[    0.125313] usbcore: registered new interface driver hub
[    0.130165] usbcore: registered new device driver usb
[    0.135149] twi_chan_cfg()340 - [twi0] has no twi_regulator.
[    0.140018] twi_chan_cfg()340 - [twi1] has no twi_regulator.
[    0.146123] Linux video capture interface: v2.00
[    0.150126] gpiochip_add: gpios 1024..1028 (axp_pin) failed to register
[    0.156916] Advanced Linux Sound Architecture Driver Version 1.0.25.
[    0.160794] Switching to clocksource arch_sys_counter
[    0.171015] NET: Registered protocol family 2
[    0.171466] IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.177306] TCP established hash table entries: 2048 (order: 2, 16384 bytes)
[    0.184207] TCP bind hash table entries: 2048 (order: 1, 8192 bytes)
[    0.190440] TCP: Hash tables configured (established 2048 bind 2048)
[    0.196921] TCP: reno registered
[    0.200126] UDP hash table entries: 256 (order: 0, 4096 bytes)
[    0.206122] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
[    0.212647] NET: Registered protocol family 1
[    0.217253] standby_mode = 1.
[    0.219870] wakeup src cnt is : 3.
[    0.223491] pmu1_enable = 0x0.
[    0.226639] config_pmux_para: script_parser_fetch err.
[    0.232034] pmu2_enable = 0x0.
[    0.235126] add_sys_pwr_dm: get ldo name failed
[    0.239818] add_sys_pwr_dm: get ldo name failed
[    0.244317] add_sys_pwr_dm: get ldo name failed
[    0.248900] add_sys_pwr_dm: get ldo name failed
[    0.253610] add_sys_pwr_dm: get ldo name failed
[    0.258090] add_sys_pwr_dm: get ldo name failed
[    0.262796] add_sys_pwr_dm: get ldo name failed
[    0.267274] add_sys_pwr_dm: get ldo name failed
[    0.271877] add_sys_pwr_dm: get ldo name failed
[    0.276566] add_sys_pwr_dm: get ldo name failed
[    0.281061] after inited: sys_mask config = 0x0.
[    0.285927] dynamic_standby enalbe = 0x0.
[    0.290049] sunxi_reg_init enter
[    0.295521] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.299326] jffs2: version 2.2. (NAND) (SUMMARY)  © 2001-2006 Red Hat, Inc.
[    0.306584] msgmni has been set to 57
[    0.311246] io scheduler noop registered
[    0.313995] io scheduler deadline registered
[    0.318410] io scheduler cfq registered (default)
[    0.323712] [DISP]disp_module_init
[    0.326990] cmdline,disp=
[    0.329889] [DISP] disp_get_rotation_sw,line:68:disp 0 out of range? g_rot_sw=0
[    0.336609] [DISP] disp_init_connections,line:289:NULL pointer: 0, 0
[    0.344754] [DISP] Fb_map_kernel_logo,line:924:Fb_map_kernel_logo failed!
[    0.352381] [DISP] disp_sys_power_enable,line:387:some error happen, fail to get regulator
[    0.358275] [DISP] disp_sys_gpio_set_value,line:374:OSAL_GPIO_DevWRITE_ONEPIN_DATA, hdl is NULL
[    0.367297] [DISP]disp_module_init finish
[    0.371458] sw_uart_get_devinfo()1503 - uart0 has no uart_regulator.
[    0.378007] uart0: ttyS0 at MMIO 0x1c28000 (irq = 32) is a SUNXI
[    0.383640] sw_uart_pm()890 - uart0 clk is already enable
[    0.389159] sw_console_se󙞠  0.397781] console [ttyS0] enabled, bootconsole disabled
[    0.397781] console [ttyS0] enabled, bootconsole disabled
[    0.405808] sunxi_spi_chan_cfg()1376 - [spi-0] has no spi_regulator.
[    0.417215] spi spi0: master is unqueued, this is deprecated
[    0.423980] m25p_probe()988 - Use the Dual Mode Read.
[    0.429818] m25p80 spi0.0: found W25q128, expected at25df641
[    0.436364] m25p80 spi0.0: W25q128 (16384 Kbytes)
[    0.442069] Creating 4 MTD partitions on "spi0.0":
[    0.447623] 0x000000000000-0x000000100000 : "uboot"
[    0.454335] 0x000000100000-0x000000110000 : "script"
[    0.461007] 0x000000110000-0x000000510000 : "kernel"
[    0.467737] 0x000000510000-0x000001000000 : "rootfs"
[    0.474560] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    0.502308] sunxi-ehci sunxi-ehci.1: SW USB2.0 'Enhanced' Host Controller (EHCI) Driver
[    0.511538] sunxi-ehci sunxi-ehci.1: new USB bus registered, assigned bus number 1
[    0.520232] sunxi-ehci sunxi-ehci.1: irq 104, io mem 0xf1c1a000
[    0.540038] sunxi-ehci sunxi-ehci.1: USB 0.0 started, EHCI 1.00
[    0.547565] hub 1-0:1.0: USB hub found
[    0.552045] hub 1-0:1.0: 1 port detected
[    0.557035] sunxi-ehci sunxi-ehci.1: remove, state 1
[    0.562716] usb usb1: USB disconnect, device number 1
[    0.570093] [DISP] disp_lcd_pwm_enable,line:1021:pwm device hdl is NULL
[    0.577820] sunxi-ehci sunxi-ehci.1: USB bus 1 deregistered
[    0.594381] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[    0.621611] sunxi-ohci sunxi-ohci.1: SW USB2.0 'Open' Host Controller (OHCI) Driver
[    0.630292] sunxi-ohci sunxi-ohci.1: new USB bus registered, assigned bus number 1
[    0.638963] sunxi-ohci sunxi-ohci.1: irq 105, io mem 0xf1c1a400
[    0.704775] hub 1-0:1.0: USB hub found
[    0.709188] hub 1-0:1.0: 1 port detected
[    0.714159] sunxi-ohci sunxi-ohci.1: remove, state 1
[    0.719942] usb usb1: USB disconnect, device number 1
[    0.726383] sunxi-ohci sunxi-ohci.1: USB bus 1 deregistered
[    0.742856] Initializing USB Mass Storage driver...
[    0.748591] usbcore: registered new interface driver usb-storage
[    0.755417] USB Mass Storage support registered.
[    0.761162] file system registered
[    0.766892] android_usb gadget: Mass Storage Function, version: 2009/09/11
[    0.774821] android_usb gadget: Number of LUNs=1
[    0.780100]  lun0: LUN: removable file: (no medium)
[    0.786312] android_usb gadget: android_usb ready
[    0.791843] sunxikbd_script_init: key para not found, used default para.
[    0.800884] sunxi-rtc sunxi-rtc: rtc core: registered sunxi-rtc as rtc0
[    0.808628] [VFE]cci probe start cci_sel = 0!
[    0.813793] [VFE]cci probe end cci_sel = 0!
[    0.818593] [VFE]cci_init end
[    0.822038] [VFE]Welcome to Video Front End driver
[    0.827986] [VFE]pdev->id = 0
[    0.831435] [VFE]dev->mipi_sel = 0
[    0.835324] [VFE]dev->vip_sel = 0
[    0.839114] [VFE]dev->isp_sel = 0
[    0.849164] [VFE_WARN]vfe vpu clock is null
[    0.860599] [VFE]pdev->id = 1
[    0.864021] [VFE]dev->mipi_sel = 1
[    0.868014] [VFE]dev->vip_sel = 1
[    0.871864] [VFE]dev->isp_sel = 0
[    0.875672] [VFE]probe_work_handle start!
[    0.880358] [VFE]..........................vfe clk open!.......................
[    0.889010] [VFE]v4l2 subdev register input_num = 0
[    0.894683] [VFE_WARN]vfe vpu clock is null
[    0.899688] [VFE_ERR]vip1 request pinctrl handle for device [csi1] failed!
[    0.907603] [VFE_ERR]get regulator csi_avdd error!
[    0.913056] [VFE_ERR]vfe_device_regulator_get error at input_num = 0
[    0.920482] [VFE]vfe_init end
[    0.924426] platform reg-20-cs-dcdc2: Driver reg-20-cs-dcdc2 requests probe deferral
[    0.933586] [VFE]V4L2 device registered as video0
[    0.938969] [VFE]..........................vfe clk close!.......................
[    0.947676] platform reg-20-cs-dcdc3: Driver reg-20-cs-dcdc3 requests probe deferral
[    0.956440] [VFE]probe_work_handle end!
[    0.960938] [VFE]probe_work_handle start!
[    0.965515] [VFE]..........................vfe clk open!.......................
[    0.974033] platform reg-20-cs-ldo1: Driver reg-20-cs-ldo1 requests probe deferral
[    0.982739] platform reg-20-cs-ldo2: Driver reg-20-cs-ldo2 requests probe deferral
[    0.991682] [VFE]v4l2 subdev register input_num = 0
[    0.997227] [VFE]vfe sensor detect start! input_num = 0
[    1.003274] [VFE]Find sensor name is "ov2640", i2c address is 60, type is "YUV" !
[    1.011824] [VFE]Sub device register "ov2640" i2c_addr = 0x60 start!
[    1.018998] [VFE_ERR]Error registering v4l2 subdevice No such device!
[    1.026388] [VFE_ERR]vfe sensor register check error at input_num = 0
[    1.033813] platform reg-20-cs-ldo3: Driver reg-20-cs-ldo3 requests probe deferral
[    1.042602] platform reg-20-cs-ldo4: Driver reg-20-cs-ldo4 requests probe deferral
[    1.051390] platform reg-20-cs-ldoio0: Driver reg-20-cs-ldoio0 requests probe deferral
[    1.060339] sunxi_wdt_init_module: sunxi WatchDog Timer Driver v1.0
[    1.067734] sunxi_wdt_probe: devm_ioremap return wdt_reg 0xf1c20ca0, res->start 0x01c20ca0, res->end 0x01c20cbf
[    1.079242] [VFE]V4L2 device registered as video1
[    1.084960] [VFE]..........................vfe clk close!.......................
[    1.093623] sunxi_wdt_probe: initialized (g_timeout=16s, g_nowayout=0)
[    1.101032] wdt_enable, write reg 0xf1c20cb8 val 0x00000000
[    1.107445] wdt_set_tmout, write 0x000000b0 to mode reg 0xf1c20cb8, actual timeout 16 sec
[    1.116779] [VFE]probe_work_handle end!
[    1.127839] sunxi_leds_fetch_sysconfig_para leds is not used in config
[    1.135343] =========script_get_err============
[    1.140895] usbcore: registered new interface driver usbhid
[    1.147192] usbhid: USB HID core driver
[    1.152295] ashmem: initialized
[    1.156010] logger: created 256K log 'log_main'
[    1.161344] logger: created 32K log 'log_events'
[    1.166840] logger: created 32K log 'log_radio'
[    1.173330] logger: created 32K log 'log_system'
[    1.181135] *******************Try sdio*******************
[    1.188578] script_get_item return type err, consider it no ldo
[    1.196448] asoc: sndcodec <-> sunxi-codec mapping ok
[    1.203087] *******************Try sd *******************
[    1.210630] TCP: cubic registered
[    1.214424] NET: Registered protocol family 17
[    1.219580] VFP support v0.3: implementor 41 architecture 2 part 30 variant 7 rev 5
[    1.228464] ThumbEE CPU extension supported.
[    1.233373] Registering SWP/SWPB emulation handler
[    1.243467] platform reg-20-cs-ldoio0: Driver reg-20-cs-ldoio0 requests probe deferral
[    1.252523] platform reg-20-cs-ldo4: Driver reg-20-cs-ldo4 requests probe deferral
[    1.261210] platform reg-20-cs-ldo3: Driver reg-20-cs-ldo3 requests probe deferral
[    1.269839] platform reg-20-cs-ldo2: Driver reg-20-cs-ldo2 requests probe deferral
[    1.278387] platform reg-20-cs-ldo1: Driver reg-20-cs-ldo1 requests probe deferral
[    1.287040] platform reg-20-cs-dcdc3: Driver reg-20-cs-dcdc3 requests probe deferral
[    1.295877] platform reg-20-cs-dcdc2: Driver reg-20-cs-dcdc2 requests probe deferral
[    1.304676] sunxi-rtc sunxi-rtc: setting system clock to 1970-01-01 00:52:23 UTC (3143)
[    1.315547] ALSA device list:
[    1.318966]   #0: audiocodec
[    1.322763] Waiting for root device /dev/mmcblk0p2...
[    1.330265] mmc0: new high speed SDHC card at address 0007
[    1.336986] mmcblk0: mmc0:0007 SD16G 14.4 GiB
[    1.344244]  mmcblk0: p1 p2
[    1.348240] mmcblk mmc0:0007: Card claimed for testing.
[    1.354284] mmc0:0007: SD16G 14.4 GiB
[    1.358650] platform reg-20-cs-dcdc2: Driver reg-20-cs-dcdc2 requests probe deferral
[    1.367505] *******************sd init ok*******************
[    1.373971] platform reg-20-cs-dcdc3: Driver reg-20-cs-dcdc3 requests probe deferral
[    1.382857] platform reg-20-cs-ldo1: Driver reg-20-cs-ldo1 requests probe deferral
[    1.392791] platform reg-20-cs-ldo2: Driver reg-20-cs-ldo2 requests probe deferral
[    1.401342] platform reg-20-cs-ldo3: Driver reg-20-cs-ldo3 requests probe deferral
[    1.409966] platform reg-20-cs-ldo4: Driver reg-20-cs-ldo4 requests probe deferral
[    1.418782] platform reg-20-cs-ldoio0: Driver reg-20-cs-ldoio0 requests probe deferral
[    1.430110] fs_names=/dev/root
[    1.433712] fs_name=ext3
[    1.439951] EXT4-fs (mmcblk0p2): couldn't mount as ext3 due to feature incompatibilities
[    1.451442] err=-22
[    1.453911] fs_name=ext2
[    1.456821] *******************Try sdio*******************
[    1.465105] EXT4-fs (mmcblk0p2): couldn't mount as ext2 due to feature incompatibilities
[    1.474453] err=-22
[    1.476890] fs_name=ext4
[    1.489053] mmc1: new high speed SDIO card at address 0001
[    1.495719] *******************sdio init ok*******************
[    2.726144] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null)
[    2.735371] VFS: Mounted root (ext4 filesystem) on device 179:2.
[    2.742251] err=0
[    2.747567] devtmpfs: mounted
[    2.751204] Freeing init memory: 140K
[    2.930884] systemd[1]: systemd 215 running in system mode. (+PAM +AUDIT +SELINUX +IMA +SYSVINIT +LIBCRYPTSETUP +GCRYPT +ACL +XZ -SECCOMP -APPARMOR)
[    2.946338] systemd[1]: Detected architecture 'arm'.

Welcome to Debian GNU/Linux 8 (jessie)!

[    3.001376] systemd[1]: Failed to insert module 'autofs4'
[    3.007740] systemd[1]: Failed to insert module 'ipv6'
[    3.016049] systemd[1]: Set hostname to <LicheePi>.
[    3.386763] systemd[1]: Cannot add dependency job for unit dbus.socket, ignoring: Unit dbus.socket failed to load: No such file or directory.
[    3.401457] systemd[1]: Cannot add dependency job for unit display-manager.service, ignoring: Unit display-manager.service failed to load: No such file or directory.
[    3.420884] systemd[1]: Expecting device dev-ttyS0.device...
        Expecting device dev-ttyS0.device...
[    3.450242] systemd[1]: Starting Forward Password Requests to Wall Directory Watch.
[    3.459321] systemd[1]: Started Forward Password Requests to Wall Directory Watch.
[    3.468062] systemd[1]: Starting Remote File Systems (Pre).
[  OK  ] Reached target Remote File Systems (Pre).
[    3.490182] systemd[1]: Reached target Remote File Systems (Pre).
[    3.497335] systemd[1]: Starting Dispatch Password Requests to Console Directory Watch.
[    3.506789] systemd[1]: Started Dispatch Password Requests to Console Directory Watch.
[    3.515820] systemd[1]: Starting Paths.
[  OK  ] Reached target Paths.
[    3.540183] systemd[1]: Reached target Paths.
[    3.545244] systemd[1]: Starting Encrypted Volumes.
[  OK  ] Reached target Encrypted Volumes.
[    3.570180] systemd[1]: Reached target Encrypted Volumes.
[    3.576595] systemd[1]: Set up automount Arbitrary Executable File Formats File System Automount Point.
[    3.587363] systemd[1]: Starting Swap.
[  OK  ] Reached target Swap.
[    3.610166] systemd[1]: Reached target Swap.
[    3.615133] systemd[1]: Expecting device dev-mmcblk0p1.device...
        Expecting device dev-mmcblk0p1.device...
[    3.640202] systemd[1]: Starting Root Slice.
[  OK  ] Created slice Root Slice.
[    3.660178] systemd[1]: Created slice Root Slice.
[    3.665716] systemd[1]: Starting /dev/initctl Compatibility Named Pipe.
[  OK  ] Listening on /dev/initctl Compatibility Named Pipe.
[    3.690188] systemd[1]: Listening on /dev/initctl Compatibility Named Pipe.
[    3.698235] systemd[1]: Starting Delayed Shutdown Socket.
[  OK  ] Listening on Delayed Shutdown Socket.
[    3.720181] systemd[1]: Listening on Delayed Shutdown Socket.
[    3.726873] systemd[1]: Starting Journal Socket (/dev/log).
[  OK  ] Listening on Journal Socket (/dev/log).
[    3.750183] systemd[1]: Listening on Journal Socket (/dev/log).
[    3.757124] systemd[1]: Starting udev Control Socket.
[  OK  ] Listening on udev Control Socket.
[    3.780183] systemd[1]: Listening on udev Control Socket.
[    3.786538] systemd[1]: Starting udev Kernel Socket.
[  OK  ] Listening on udev Kernel Socket.
[    3.810188] systemd[1]: Listening on udev Kernel Socket.
[    3.816409] systemd[1]: Starting User and Session Slice.
[  OK  ] Created slice User and Session Slice.
[    3.840194] systemd[1]: Created slice User and Session Slice.
[    3.846923] systemd[1]: Starting Journal Socket.
[  OK  ] Listening on Journal Socket.
[    3.870194] systemd[1]: Listening on Journal Socket.
[    3.876115] systemd[1]: Starting Sockets.
[  OK  ] Reached target Sockets.
[    3.900178] systemd[1]: Reached target Sockets.
[    3.905463] systemd[1]: Starting System Slice.
[  OK  ] Created slice System Slice.
[    3.930189] systemd[1]: Created slice System Slice.
[    3.935980] systemd[1]: Started Create list of required static device nodes for the current kernel.
[    3.946850] systemd[1]: Mounting Debug File System...
        Mounting Debug File System...
[    3.972002] systemd[1]: Mounted POSIX Message Queue File System.
[    3.985664] systemd[1]: Starting Load Kernel Modules...
        Starting Load Kernel Modules...
[    4.016065] systemd[1]: Started Set Up Additional Binary Formats.
[    4.024319] systemd[1]: Mounted Huge Pages File System.
[    4.036274] systemd[1]: Starting udev Coldplug all Devices...
        Starting udev Coldplug all Devices...
[    4.062631] systemd[1]: Starting Create Static Device Nodes in /dev...
        Starting Create Static Device Nodes in /dev...
[    4.092537] systemd[1]: Starting system-getty.slice.
[  OK  ] Created slice system-getty.slice.
[    4.112748] systemd[1]: Created slice system-getty.slice.
[    4.122317] systemd[1]: Starting system-serial\x2dgetty.slice.
[  OK  ] Created slice system-serial\x2dgetty.slice.
[    4.150268] systemd[1]: Created slice system-serial\x2dgetty.slice.
[    4.157615] systemd[1]: Started File System Check on Root Device.
[    4.164807] systemd[1]: Starting Journal Service...
        Starting Journal Service...
[  OK  ] Started Journal Service.
[    4.210299] systemd[1]: Started Journal Service.
[  OK  ] Reached target Slices.
[  OK  ] Mounted Debug File System.
[  OK  ] Started Load Kernel Modules.
[  OK  ] Started Create Static Device Nodes in /dev.
        Starting udev Kernel Device Manager...
        Starting Apply Kernel Variables...
[  OK  ] Started udev Kernel Device Man[    4.372555] systemd-udevd[69]: starting version 215
ager.
[  OK  ] Started udev Coldplug all Devices.
[  OK  ] Started Apply Kernel Variables.
        Starting Copy rules generated while the root was ro...
        Starting LSB: Set preliminary keymap...
[  OK  ] Started Copy rules generated while the root was ro.
[  OK  ] Started LSB: Set preliminary keymap.
        Starting Remount Root and Kernel File Systems...
[    4.640531] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)
[  OK  ] Started Remount Root and Kernel File Systems.
[  OK  ] Reached target Local File Systems (Pre).
        Starting Load/Save Random Seed...
[  OK  ] Reached target Sound Card.
[  OK  ] Started Load/Save Random Seed.
[    5.012110] [VFE]vfe_open
[  OK  [    5.043465] [VFE]vfe_open
] Found device /dev/mmcblk0p1.
[    5.058511] [VFE]..........................vfe clk open!.......................
[    5.082179] [VFE]..........................vfe clk open!.......................
[  OK  ] Found device /dev/ttyS0.
[    5.112320] [VFE]vfe_open ok
[    5.115853] [VFE]vfe_close
[    5.118958] [VFE]vfe select input flag = 0, s_input have not be used .
[    5.126480] [VFE]..........................vfe clk close!.......................
[    5.144821] [VFE]vfe_open ok
[    5.148348] [VFE]vfe_close
[    5.151598] [VFE]vfe select input flag = 0, s_input have not be used .
[    5.158972] [VFE]..........................vfe clk close!.......................
        Mounting /boot...
[    5.224773] [VFE]vfe_close end
[    5.282919] [VFE]vfe_close end
[  OK  ] Mounted /boot.
[  OK  ] Reached target Local File Systems.
        Starting Create Volatile Files and Directories...
[  OK  ] Reached target Remote File Systems.
        Starting Trigger Flushing of Journal to Persistent Storage...
        Starting LSB: Set console font and keymap...
        Starting LSB: Raise network interfaces....
[  OK  ] Started LSB: Set console font and keymap.
[    5.519209] systemd-journald[65]: Received request to flush runtime journal from PID 1
[  OK  ] Started Trigger Flushing of Journal to Persistent Storage.
[  OK  ] Started Create Volatile Files and Directories.
        Starting Update UTMP about System Boot/Shutdown...
[  OK  ] Started Update UTMP about System Boot/Shutdown.
[  OK  ] Started LSB: Raise network interfaces..
[  OK  ] Reached target Network.
[  OK  ] Reached target System Initialization.
[  OK  ] Reached target Timers.
        Starting Restore Sound Card State...
[  OK  ] Reached target Basic System.
        Starting Regular background program processing daemon...
[  OK  ] Started Regular background program processing daemon.
        Starting OpenBSD Secure Shell server...
[  OK  ] Started OpenBSD Secure Shell server.
        Starting /etc/rc.local Compatibility...
        Starting Permit User Sessions...
        Starting getty on tty2-tty6 if dbus and logind are not available...
[  OK  ] Started Restore Sound Card State.
[  OK  ] Started /etc/rc.local Compatibility.
[  OK  ] Started Permit User Sessions.
        Starting Getty on tty2...
[  OK  ] Started Getty on tty2.
        Starting Getty on tty1...
[  OK  ] Started Getty on tty1.
        Starting Serial Getty on ttyS0...
[  OK  ] Started Serial Getty on ttyS0.
[  OK  ] Started getty on tty2-tty6 if dbus and logind are not available.
        Starting Getty on tty6...
[  OK  ] Started Getty on tty6.
        Starting Getty on tty5...
[  OK  ] Started Getty on tty5.
        Starting Getty on tty4...
[  OK  ] Started Getty on tty4.
        Starting Getty on tty3...
[  OK  ] Started Getty on tty3.
[  OK  ] Reached target Login Prompts.
[  OK  ] Reached target Multi-User System.
[  OK  ] Reached target Graphical Interface.
        Starting Update UTMP about System Runlevel Changes...
[  OK  ] Started Update UTMP about System Runlevel Changes.

Debian GNU/Linux 8 LicheePi ttyS0

LicheePi login: root

BSP内核的摄像头使用

Device Drivers ->
 <*> Multimedia support  --->
  <*>   sunxi video encoder and decoder support     //视频编码器,包括h264/mpeg4/mpeg2/vc1/rmvb.  以模块形式编译,cedar_ve.ko
[*]   Video capture adapters  --->
[*]   V4L platform devices  --->
--- V4L platform devices
< >   Support for timberdale Video In/LogiWIN
<*>   SoC camera support
< >     imx074 support
< >     mt9m001 support
< >     mt9m111, mt9m112 and mt9m131 support
< >     mt9t031 support
< >     mt9t112 support
< >     mt9v022 support
< >     rj54n1cb0c support
< >     tw9910 support
< >     platform camera support
<M>     ov2640 camera support
<M>     ov5642 camera support
< >     ov6650 sensor support
< >     ov772x camera support
< >     ov9640 camera support
< >     ov9740 camera support
< >   SuperH Mobile MIPI CSI-2 Interface driver
< >   SuperH Mobile CEU Interface driver
<*>   sunxi video front end (camera and etc)driver
<*>     v4l2 driver for SUNXI
Sensor CSI  --->
< > ov2710_mipi
< > ov4689
< > ov4689 60fps
< > ar0330 mipi
< > ov4689 sdv
< > gc1004_mipi
<M> h22_mipi
< > nt99231_mipi

查看编译后的ko:

//触摸屏
aw5306_ts.ko
gt818_ts.ko
gt82x.ko
gt911_ts.ko
ft5x_ts.ko
gslX680.ko
gslX680new.ko
icn83xx_ts.ko
gt9xx_ts.ko
gt9xxf_ts.ko
tu_ts.ko

//杂项
da380.ko    //加速度传感器
fatfs.ko    //文件系统
scsi_wait_scan.ko
sw-device.ko        //Linux kernel modules for  Detection i2c device.
uvcvideo.ko         //UVC摄像头

//普通摄像头驱动  media/video/
ov2640.ko
ov5642.ko

//V4L2
videobuf-core.ko
videobuf-dma-contig.ko
videobuf2-core.ko
videobuf2-memops.ko
videobuf2-vmalloc.ko

//VFE
vfe_v4l2.ko                 //media/video/sunxi-vfe
vfe_os.ko
vfe_subdev.ko
cci.ko              //摄像头控制接口,sunxi-vfe/csi_cci
h22_mipi.ko    //media/video/sunxi-vfe/device/h22_mipi.ko

//cedar  视频编码
cedar_ve.ko   //media/cedar-ve/cedar_ve.ko

实际可以使用的是 media/video/sunxi-vfe/ 下的摄像头驱动

platform_cfg.h
bsp_common.c
config.c
config.h
vfe.c
vfe.h
vfe_os.c
vfe_os.h
vfe_os.ko
vfe_subdev.c
vfe_subdev.h
vfe_subdev.ko
vfe_v4l2.ko
//调焦器
actuator
//摄像头控制器接口驱动
csi
csi_cci
mipi_csi
//闪光灯
flash_light
//ISP相关
isp_cfg
lib     //isp库
platform    //不同主控芯片的配置
//测试,功能
test
utility
//支持的传感器
device

支持的传感器

Makefile       gc0312.c       gc5004.c        hi257.c          nt99252.c       ov5640.c        s5k3h7.c       sp2518.c
ar0330.c       gc0328.c       gc5004_mipi.c   hm5040.c         ov12830.c       ov5640_mipi.c   s5k4e1.c       sp2519.c
ar0330_mipi.c  gc0328c.c      gs5604.c        hm5065.c         ov13850.c       ov5647.c        s5k4e1_mipi.c  sp5408.c
bf3a03.c       gc0329.c       gt2005.c        hm8030.c         ov16825.c       ov5647_mipi.c   s5k4ec.c       sp5409.c
built-in.o     gc1004_mipi.c  h22_mipi.c      hm8131.c         ov2640.c        ov5648.c        s5k4ec_mipi.c  t4k05.c
camera.h       gc2035.c       h22_mipi.ko     imx179.c         ov2686.c        ov5650.c        s5k5e2ya.c     t8et5.c
camera_cfg.h   gc2145.c       h22_mipi.mod.c  imx214.c         ov2710.c        ov7736.c        s5k5e2yx.c     tc358743.c
gc0307.c       gc2155.c       h22_mipi.mod.o  imx219.c         ov2710_mipi.c   ov8825.c        siv121d.c
gc0308.c       gc2235.c       h22_mipi.o      modules.builtin  ov4689.c        ov8850.c        sp0718.c
gc0309.c       gc2355.c       h42_mipi.c      modules.order    ov4689_60fps.c  ov8858.c        sp0838.c
gc0311.c       gc2355_mipi.c  hi253.c         nt99231_mipi.c   ov4689_sdv.c    ov8858_4lane.c  sp2508.c

可见zero配套的ov2640和ov5647都在其中。

但是他们不在menuconfig中,所以在上层的Kconfig中加入:

config OV2640
        tristate "ov2640"
        default n
config OV5647_MIPI
        tristate "ov5647_mipi"
        default n

在本层的Makefile中加入:

obj-$(CONFIG_OV2640)            += ov2640.o
obj-$(CONFIG_OV5647_MIPI)       += ov5647_mipi.o

重新在menuconfig中勾选,编译,即可得到:

LD [M]  drivers/media/video/sunxi-vfe/device/ov2640.ko
LD [M]  drivers/media/video/sunxi-vfe/device/ov5647_mipi.ko

将ko文件放入系统中,手动加载,然后使用fswebcam尝试拍照:

root@LicheePi:~# fswebcam -d /dev/video0 --no-banner -r 320x240 capture.jpg
[  255.199106] [VFE]vfe_open
--- Opening /dev/video0...
[  255.202406] [VFE]..........................vfe clk open!.......................
Trying source module v4l2...[  255.213786] [VFE]vfe_open ok

[  255.219628] [VFE_ERR]input index(0) > dev->dev_qty(1)-1 invalid!, device_valid_flag[0] = 0
/dev/video0 opened.
255.229491] [VFE]vfe_close
mNo input was specified, using t[  255.235232] [VFE]vfe select input flag = 0, s_input have not be used .
he first.
Unable to qu[  255.245365] [VFE]..........................vfe clk close!.......................
ery input 0.
VIDIOC_EN[  255.256556] [VFE]vfe_close end
UMINPUT: Invalid argument

跟踪相关信息,是在vfe.c,

函数 static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) 中:

2807         if (0 == dev->device_valid_flag[inp->index]) {
2808                 vfe_err("input index(%d) > dev->dev_qty(%d)-1 invalid!, device_valid_flag[%d] = %d\n", inp->index, dev->dev_     qty,inp->index, dev->device_valid_flag[inp->index]);
2809                 return -EINVAL;
2810         }

即枚举摄像头时,发现device_valid_flag标志位不对,该标志位是在:

static void probe_work_handle(struct work_struct *work)
if(vfe_sensor_register_check(dev,&dev->v4l2_dev,dev->ccm_cfg[input_num],&dev->dev_sensor[input_num],input_nu     m) == NULL)
5126                 {
5127                         vfe_err("vfe sensor register check error at input_num = %d\n",input_num);
5128                         dev->device_valid_flag[input_num] = 0;
5129                         //goto snesor_register_end;
5130                 }
5131                 else{
5132                         dev->device_valid_flag[input_num] = 1;
5133                 }

这个函数是在vfe_probe中调用,也即初始化时检测的。

vfe驱动加载过程:

  • csi_cci/cci_platform_drv.c 加载cci.ko
  • vfe.c vfe_os.ko加载
  • vfe_subdev.ko
  • vfe_v4l2.ko //media/video/sunxi-vfe

查看开机启动信息:

//cci.ko加载,即摄像头控制器初始化
[    0.808628] [VFE]cci probe start cci_sel = 0!
[    0.813793] [VFE]cci probe end cci_sel = 0!
[    0.818593] [VFE]cci_init end

//VFE驱加载
[    0.822038] [VFE]Welcome to Video Front End driver
[    0.827986] [VFE]pdev->id = 0
[    0.831435] [VFE]dev->mipi_sel = 0
[    0.835324] [VFE]dev->vip_sel = 0
[    0.839114] [VFE]dev->isp_sel = 0
[    0.849164] [VFE_WARN]vfe vpu clock is null
[    0.849164] [VFE_WARN]vfe vpu clock is null
[    0.860599] [VFE]pdev->id = 1
[    0.864021] [VFE]dev->mipi_sel = 1
[    0.868014] [VFE]dev->vip_sel = 1
[    0.871864] [VFE]dev->isp_sel = 0
[    0.875672] [VFE]probe_work_handle start!
[    0.880358] [VFE]..........................vfe clk open!.......................
[    0.880358] [VFE]..........................vfe clk open!.......................
[    0.889010] [VFE]v4l2 subdev register input_num = 0
[    0.894683] [VFE_WARN]vfe vpu clock is null
[    0.894683] [VFE_WARN]vfe vpu clock is null
[    0.899688] [VFE_ERR]vip1 request pinctrl handle for device [csi1] failed!
[    0.907603] [VFE_ERR]get regulator csi_avdd error!
[    0.913056] [VFE_ERR]vfe_device_regulator_get error at input_num = 0
[    0.913056] [VFE_ERR]vfe_device_regulator_get error at input_num = 0
[    0.920482] [VFE]vfe_init end
[    0.920482] [VFE]vfe_init end

//V4L2设备注册,生成video0
[    0.933586] [VFE]V4L2 device registered as video0
[    0.938969] [VFE]..........................vfe clk close!.......................
[    0.938969] [VFE]..........................vfe clk close!.......................
[    0.956440] [VFE]probe_work_handle end!
[    0.960938] [VFE]probe_work_handle start!
[    0.965515] [VFE]..........................vfe clk open!.......................
[    0.965515] [VFE]..........................vfe clk open!.......................
[    0.991682] [VFE]v4l2 subdev register input_num = 0
[    0.997227] [VFE]vfe sensor detect start! input_num = 0
[    0.997227] [VFE]vfe sensor detect start! input_num = 0
[    1.003274] [VFE]Find sensor name is "ov2640", i2c address is 60, type is "YUV" !
[    1.011824] [VFE]Sub device register "ov2640" i2c_addr = 0x60 start!
[    1.018998] [VFE_ERR]Error registering v4l2 subdevice No such device!
[    1.026388] [VFE_ERR]vfe sensor register check error at input_num = 0
[    1.026388] [VFE_ERR]vfe sensor register check error at input_num = 0
[    1.079242] [VFE]V4L2 device registered as video1
[    1.084960] [VFE]..........................vfe clk close!.......................
[    1.084960] [VFE]..........................vfe clk close!.......................
[    1.116779] [VFE]probe_work_handle end!
[    5.012110] [VFE]vfe_open
[    5.012110] [VFE]vfe_open
[    5.043465] [VFE]vfe_open
[    5.043465] [VFE]vfe_open
[    5.058511] [VFE]..........................vfe clk open!.......................
[    5.058511] [VFE]..........................vfe clk open!.......................
[    5.082179] [VFE]..........................vfe clk open!.......................
[    5.082179] [VFE]..........................vfe clk open!.......................
[    5.112320] [VFE]vfe_open ok
[    5.112320] [VFE]vfe_open ok
[    5.115853] [VFE]vfe_close
[    5.115853] [VFE]vfe_close
[    5.118958] [VFE]vfe select input flag = 0, s_input have not be used .
[    5.118958] [VFE]vfe select input flag = 0, s_input have not be used .
[    5.126480] [VFE]..........................vfe clk close!.......................
[    5.126480] [VFE]..........................vfe clk close!.......................
[    5.144821] [VFE]vfe_open ok
[    5.144821] [VFE]vfe_open ok
[    5.148348] [VFE]vfe_close
[    5.148348] [VFE]vfe_close
[    5.151598] [VFE]vfe select input flag = 0, s_input have not be used .
[    5.151598] [VFE]vfe select input flag = 0, s_input have not be used .
[    5.158972] [VFE]..........................vfe clk close!.......................
[    5.158972] [VFE]..........................vfe clk close!.......................
[    5.224773] [VFE]vfe_close end
[    5.224773] [VFE]vfe_close end
[    5.282919] [VFE]vfe_close end
[    5.282919] [VFE]vfe_close end
vfe_sensor_register_check
 static struct v4l2_subdev *vfe_sensor_register_check(struct vfe_dev *dev,struct v4l2_device *v4l2_dev,struct ccm_config  *ccm_cfg,
                     struct i2c_board_info *sensor_i2c_board,int input_num )
 {
     int sensor_cnt,ret, sensor_num;
     struct sensor_item sensor_info;
     if(dev->vip_define_sensor_list == 1)
     {
         sensor_num = ccm_cfg->sensor_cfg_ini->detect_sensor_num;
         if(ccm_cfg->sensor_cfg_ini->detect_sensor_num == 0) {
             sensor_num = 1;
         }
     } else {
         sensor_num = 1;
     }
     for(sensor_cnt=0; sensor_cnt<sensor_num; sensor_cnt++)
     {
         if(dev->vip_define_sensor_list == 1)
         {
             if(ccm_cfg->sensor_cfg_ini->detect_sensor_num > 0)
                 cpy_ccm_sub_device_cfg(ccm_cfg, sensor_cnt);
         }
         if(get_sensor_info(ccm_cfg->ccm, &sensor_info) == 0)
         {
             if(ccm_cfg->i2c_addr != sensor_info.i2c_addr)
             {
                 vfe_warn("Sensor info \"%s\" i2c_addr is different from sys_config!\n", sensor_info.sensor_name );
                 //vfe_warn("Sensor info i2c_addr = %d, sys_config i2c_addr = %d!\n", sensor_info.i2c_addr, ccm_cfg->i2c_addr);
                 //ccm_cfg->i2c_addr = sensor_info.i2c_addr;
             }
             if(ccm_cfg->is_bayer_raw != sensor_info.sensor_type)
             {
                 vfe_warn("Camer detect \"%s\" fmt is different from sys_config!\n", sensor_info_type[sensor_info.sensor_type]);
                 vfe_warn("Apply detect  fmt = %d replace sys_config fmt = %d!\n", sensor_info.sensor_type, ccm_cfg->is_bayer_raw);
                 ccm_cfg->is_bayer_raw = sensor_info.sensor_type;
             }
             if(sensor_info.sensor_type == SENSOR_RAW)
             {
                 ccm_cfg->is_isp_used = 1;
             }
             else
             {
                 ccm_cfg->act_used = 0;
             }
             vfe_print("Find sensor name is \"%s\", i2c address is %x, type is \"%s\" !\n",sensor_info.sensor_name,sensor_info.i2c_addr,
                             sensor_info_type[sensor_info.sensor_type]);
         }
         sensor_i2c_board->addr = (unsigned short)(ccm_cfg->i2c_addr>>1);
         strcpy(sensor_i2c_board->type,ccm_cfg->ccm);

         vfe_print("Sub device register \"%s\" i2c_addr = 0x%x start!\n",sensor_i2c_board->type, ccm_cfg->i2c_addr);
         ret = vfe_sensor_subdev_register_check(dev,v4l2_dev,ccm_cfg,sensor_i2c_board);
         if( ret == -1)
         {
             vfe_sensor_subdev_unregister(v4l2_dev,ccm_cfg,sensor_i2c_board);
             vfe_print("Sub device register \"%s\" failed!\n",sensor_i2c_board->type);
             ccm_cfg->sd =NULL;
             continue;
         }
         else if(ret == ENODEV ||ret == EFAULT)
         {
             continue;
         }
         else if(ret == 0)
         {
             vfe_print("Sub device register \"%s\" is OK!\n",sensor_i2c_board->type);
             break;
         }
     }
     return ccm_cfg->sd;
 }

bsp内核中的保留内存

问?

bsp内核启动时,打印的内存信息发现有35M内存是保留的,这些内存是被谁使用了呢?

答!

原来,其中的大头,28MB是被 ion 使用了,在menuconfig里的 CONFIG_ION_SUNXI 中,有 CONFIG_ION_SUNXI_RESERVE_LIST,默认保留了28M 内存给 vfe, ve 等。

根文件系统编译

本节将讲解buildroot及emdebian两种根文件系统的编译过程

buildroot根文件系统

buildroot可用于构建小型的linux根文件系统。

大小最小可低至2M,与内核一起可以放入最小8M的spi flash中。

buildroot中可以方便地加入第三方软件包(其实已经内置了很多),省去了手工交叉编译的烦恼。

美中不足的是不支持包管理系统,没有gcc等。

下载安装:

首先安装一些依赖,比如linux头文件:

apt-get install linux-headers-$(uname -r)

然后下载安装:

wget https://buildroot.org/downloads/buildroot-2017.08.tar.gz
tar xvf buildroot-2017.08.tar.gz
cd buildroot-2017.08/
make menuconfig

配置

首先配置工具链,因为之前开发uboot和内核都用到了自己下载的工具链,所以这里也配置成外部工具链。

  • 在本机上外部工具链配置为: /opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/
  • 工具链前缀是: arm-linux-gnueabihf
  • 外部工具链gcc版本:我们使用的是最新的6.3版本
  • 外部工具链内核头文件:是在 arm-linux-gnueabi/libc/usr/include/linux/version.h 里读取内核版本信息。本机的版本是4.6
  • C库还是选择传统的glibc。需要小体积可以选uclibc(需要自行编译安装)。
  • 再在system 设置下主机名,root密码等。
  • 最后就是配置自己需要的软件包,在menuconfig中选中即可。
  • 有时候下载速度慢,可以复制下载链接,使用迅雷等下载好后,拷贝到dl目录下,会自动识别。

编译

make

>>> 有时候构建会出现莫名其妙的错误,make clean下会ok?

编译完成后,会生成 output/images/rootfs.tar,此即所需的根文件系统

默认失能串口登录,需要修改 /etc/inittab :

ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100 # GENERIC_SERIAL

删除软件包

buildroot在menuconfig里去掉软件包后,并不会在打包的镜像里去掉。

需要手动在output/target/usr/bin/里移除

make xxx-clean

清理 output/build/xxx, 包含

重新编译软件包

删除这个目录下的 .stamp_built和.stamp_target_installed 。然后回到buildroot根目录下 make。buildroot会自动重新编译对应软件包并且拷贝到文件系统。

加入软件包

  1. 在package/Config.in 中对应位置添加 source "package//Config.in"
  2. package/下添加Config.in, 使用kconfig编写,描述该软件包的状态(Y/N/M) .mk 使用make编写,描述该软件包获取源的方法,编译、安装的方法等 可选的.hash 检查下载文件的完整性 可选的.patch文件 在编译前给源代码打补丁

Config.in写法

::
config BR2_PACKAGE_ bool "pkg name" depends on BR2_PACKAGE_XXX select BR2_PACKAGE_XXX help pkg help content

网络下载的软件包

一般软件包写法,需要指定软件包的基本信息(版本、下载地址等),依赖关系,

根据不同类型目标来设置_INSTALL_xxx=YES or NO

应用软件包     TARGET_DIR     无需修改
共享库文件     TARGET_DIR  STAGING_DIR
静态库           STAGING_DIR
安装入bootloader或kernel     BINARIES_DIR

定义一般软件包generic-package的动作

::
<pkg>_CONFIGURE_CMDS, 配置命令, 总是调用 <pkg>_BUILD_CMDS, 编译命令,总是调用 <pkg>_INSTALL_TARGET_CMDS, //如上节所示调用 <pkg>_INSTALL_STAGING_CMDS, <pkg>_INSTALL_IMAGES_CMDS, <pkg>_INSTALL_CMDS, 主机软件包总是调用 <pkg>_CLEAN_CMDS //清理命令 <pkg>_UNINSTALL_TARGET_CMDS <pkg>_UNINSTALL_STAGING_CMDS

常用软件包信息

LIBFOO_VERSION         版本号,如LIBFOO_VERSION = 0.1.2
LIBFOO_SOURCE          软件包tar的名字,默认是packagename-$(LIBFOO_VERSION).tar.gz.
                        如LIBFOO_SOURCE = foobar-$(LIBFOO_VERSION).tar.bz2
LIBFOO_PATCH            补丁名
LIBFOO_SITE                软件包源地址
    缺省为http://$$(BR2_SOURCEFORGE_MIRROR).dl.sourceforge.net/sourceforge/packagename.
    LIBFOO_SITE=http://www.libfoosoftware.org/libfoo
    LIBFOO_SITE=http://svn.xiph.org/trunk/Tremor/
LIBFOO_SITE_METHOD      获取软件包的方法
    wget, svn, git, bzr, 不指定的话会从URL猜测方法。
LIBFOO_DEPENDENCIES 列出软件包的依赖

$(@D)           软件包的源代码解压目录
$(MAKE)         调用make
$(MAKE1)      不能使用并行编译时候的make
$(TARGET_MAKE_ENV) $(HOST_MAKE_ENV),     传给make的环境变量
$(TARGET_CC), $(TARGET_LD)     CC,LD的选项.
$(TARGET_CROSS)     教程编译工具链的前缀
$(TARGET_DIR), $(STAGING_DIR), $(BINARIES_DIR), $(HOST_DIR).

常用HOOK (用+=添加)
LIBFOO_POST_PATCH_HOOKS
LIBFOO_PRE_CONFIGURE_HOOKS
LIBFOO_POST_CONFIGURE_HOOKS
LIBFOO_POST_BUILD_HOOKS
LIBFOO_POST_INSTALL_HOOKS (for host packages only)
LIBFOO_POST_INSTALL_STAGING_HOOKS (for target packages only)
LIBFOO_POST_INSTALL_TARGET_HOOKS (for target packages only)

例程

#############################################################
# libfoo   download from website
#############################################################
LIBFOO_VERSION = 1.0
LIBFOO_SOURCE = libfoo-$(LIBFOO_VERSION).tar.gz
LIBFOO_SITE = http://www.foosoftware.org/download
#LIBFOO_INSTALL_STAGING = YES     # default NO
LIBFOO_DEPENDENCIES = host-libaaa libbbb

define LIBFOO_BUILD_CMDS
    # $(@D) is pkg source folder
    $(MAKE) CC=$(TARGET_CC) LD=$(TARGET_LD) -C $(@D) all
endef

#condition  statement
#ifneq ($(BR2_PACKAGE_LIBFOO_TEST),y)
#    LIBFOO_CONF_OPT += --enable-test
#endif

#define LIBFOO_INSTALL_STAGING_CMDS
#     $(INSTALL) -D -m 0755 $(@D)/libfoo.a $(STAGING_DIR)/usr/lib/libfoo.a
#     $(INSTALL) -D -m 0644 $(@D)/foo.h $(STAGING_DIR)/usr/include/foo.h
#     $(INSTALL) -D -m 0755 $(@D)/libfoo.so* $(STAGING_DIR)/usr/lib
#endef

define LIBFOO_INSTALL_TARGET_CMDS
    $(INSTALL) -D -m 0755 $(@D)/libfoo.so* $(TARGET_DIR)/usr/lib
    $(INSTALL) -d -m 0755 $(TARGET_DIR)/etc/foo.d
endef

define LIBFOO_CLEAN_CMDS
    -$(MAKE) -C $(@D) clean
endef

$(eval $(call GENTARGETS,package,libfoo))     # gen pkt, must the last line

GENTARGETS需要三个参数

  1. 软件包目录前缀,一般是package,如果更深就是package/xxx
  2. 小写的包名,比如libfoo, .mk里的变量前缀就是LIBFOO_,Config.in文件里的配置选项就是 BR2_PACKAGE_LIBFOO.
  3. 可选,缺省是target,标识为host则为主机包

autotools-based软件包的mk写法

::

LIBFOO_VERSION = 1.0 LIBFOO_SOURCE = libfoo-$(LIBFOO_VERSION).tar.gz LIBFOO_SITE = http://www.foosoftware.org/download LIBFOO_INSTALL_STAGING = YES LIBFOO_INSTALL_TARGET = YES LIBFOO_CONF_OPT = --enable-shared LIBFOO_DEPENDENCIES = libglib2 host-pkg-config

$(eval $(call AUTOTARGETS,package,libfoo))

CMake-based软件包mk写法

#############################################################
# libfoo
#############################################################
LIBFOO_VERSION = 1.0
LIBFOO_SOURCE = libfoo-$(LIBFOO_VERSION).tar.gz
LIBFOO_SITE = http://www.foosoftware.org/download
LIBFOO_INSTALL_STAGING = YES
LIBFOO_INSTALL_TARGET = YES
LIBFOO_CONF_OPT = -DBUILD_DEMOS=ON
LIBFOO_DEPENDENCIES = libglib2 host-pkg-config

$(eval $(call CMAKETARGETS,package,libfoo))

.mk写法之本地软件包

常用变量

LIBFOO_VERSION
LIBFOO_SOURCE
LIBFOO_SITE
LIBFOO_DIR                         软件包被配置和编译的目录,一般建在BUILD_DIR下
LIBFOO_BINARY                   软件包二进制文件名
LIBFOO_TARGET_BINARY     软件包的目标文件系统的安装目录

本地软件包的.mk更像正常的makefile,当做普通makefile看即可

##############################################################
# libfoo
#############################################################
LIBFOO_DIR:=$(BUILD_DIR)/libfoo

#这里 填获取源代码的方式,即本地拷贝文件
$(LIBFOO_DIR)/.source :
    mkdir -pv $(LIBFOO_DIR)  $(LIBFOO_HOST_DIR)
    cp -rf package/libfoo/src/* $(LIBFOO_DIR)
    touch $@     #create dummy file

#这里填配置编译的操作
$(LIBFOO_DIR)/.configured: $(LIBFOO_DIR)/.source
    touch $@

#compile
libfoo-binary: $(LIBFOO_DIR)/.configured
    mkdir -pv $(HOST_DIR)/usr/bin
    $(MAKE) BUILD_DIR=$(BUILD_DIR) CC="$(TARGET_CC)" -C $(LIBFOO_DIR)

#install
libfoo: libfoo-binary
    $(MAKE) DESTDIR="$(TARGET_DIR)" -C $(LIBFOO_DIR) install

##############################################################
# Add our target
#############################################################
ifeq ($(BR2_PACKAGE_LIBFOO),y)
    TARGETS += libfoo
endif

然后在src里加入对应的 源文件和makefile,示例如下

###########################################
#Makefile for simple programs
###########################################
INC=
LDLIBS += -lpthread
CFLAGS += -Wall
#CPPFLAGS +=

PRG=threadpooltest
OBJ=CThreadManage.o CThreadPool.o CThread.o CWorkerThread.o threadpooltest.o

$(PRG):$(OBJ)
    $(CC) $(INC) $(LIB) -o $@ $(OBJ)

.SUFFIXES: .c .o .cpp
.cpp.o:
    $(CC) $(CFLAGS ) $(INC) -c $*.cpp -o $*.o

.PRONY:clean
clean:
    @echo "Removing linked and compiled files......"
    rm -f $(OBJ) $(PRG)

SRCS := xxx.c
CFLAGS := -Wall

libfoo : $(SRCS)
    $(CC) $(filter %.c, $(SRCS)) $(CFLAGS) -o fsck_msdos
clean:
    rm libfoo
install:
    cp fsck_msdos $(DESTDIR)/usr/bin/

//这个是manual makefile,也类似

#############################################################
# libfoo
#############################################################
LIBFOO_VERSION:=1.0
LIBFOO_SOURCE:=libfoo-$(LIBFOO_VERSION).tar.gz
LIBFOO_SITE:=http://www.foosoftware.org/downloads
LIBFOO_DIR:=$(BUILD_DIR)/foo-$(FOO_VERSION)
LIBFOO_BINARY:=foo
LIBFOO_TARGET_BINARY:=usr/bin/foo

#method to get source
$(DL_DIR)/$(LIBFOO_SOURCE):
    $(call DOWNLOAD,$(LIBFOO_SITE),$(LIBFOO_SOURCE))

#target: .source      extract source tar
$(LIBFOO_DIR)/.source: $(DL_DIR)/$(LIBFOO_SOURCE)
    $(ZCAT) $(DL_DIR)/$(LIBFOO_SOURCE) | tar -C $(BUILD_DIR) $(TAR_OPTIONS) -
    touch $@

#target:.configured
$(LIBFOO_DIR)/.configured: $(LIBFOO_DIR)/.source
    (cd $(LIBFOO_DIR); rm -rf config.cache; \
        $(TARGET_CONFIGURE_OPTS) \
        $(TARGET_CONFIGURE_ARGS) \
        ./configure \
        --target=$(GNU_TARGET_NAME) \
        --host=$(GNU_TARGET_NAME) \
        --build=$(GNU_HOST_NAME) \
        --prefix=/usr \
        --sysconfdir=/etc \
    )
    touch $@

#target: binary file     compile
$(LIBFOO_DIR)/$(LIBFOO_BINARY): $(LIBFOO_DIR)/.configured
    $(MAKE) CC=$(TARGET_CC) -C $(LIBFOO_DIR)

#target: target binary file          move to target,strip,remove manual
$(TARGET_DIR)/$(LIBFOO_TARGET_BINARY): $(LIBFOO_DIR)/$(LIBFOO_BINARY)
    $(MAKE) DESTDIR=$(TARGET_DIR) -C $(LIBFOO_DIR) install-strip
    rm -Rf $(TARGET_DIR)/usr/man

#dependencies
libfoo: uclibc ncurses $(TARGET_DIR)/$(LIBFOO_TARGET_BINARY)

#download before compile
libfoo-source: $(DL_DIR)/$(LIBFOO_SOURCE)

libfoo-clean:
    $(MAKE) prefix=$(TARGET_DIR)/usr -C $(LIBFOO_DIR) uninstall
    -$(MAKE) -C $(LIBFOO_DIR) clean

libfoo-dirclean:
    rm -rf $(LIBFOO_DIR)

#############################################################
# Toplevel Makefile options
#############################################################
ifeq ($(BR2_PACKAGE_LIBFOO),y)
    TARGETS+=libfoo
endif

emdebian根文件系统

如果想要更接近桌面系统的根文件系统,emdebian是不二之选。emdebian也是深度可定制,大小从不到100M到上G均可配置。但这个大小基本上无法使用spi flash了,只能使用tf卡或者emmc。优点是有包管理系统,可以直接apt-get install来安装新软件包。

环境搭建

安装依赖包

sudo apt-get install multistrap qemu qemu-user-static binfmt-support dpkg-cross

建立工作目录:
mkdir emdebian
cd emdebian
mkdir mindb
cd mindb
编辑multistrap_mindb.conf配置文件

示例如下:

具体语法请参考:https://wiki.debian.org/Multistrap

这里使用了较快的清华大学的镜像站,身在国外的朋友可以视情况更改。

[General]
directory=target-rootfs
cleanup=true
noauth=true
unpack=true
debootstrap=Debian Net Utils
aptsources=Debian

[Debian]
packages=apt kmod lsof
source=http://ftp2.cn.debian.org/debian/
keyring=debian-archive-keyring
suite=stretch
components=main contrib non-free

[Net]
#Basic packages to enable the networking
packages=netbase net-tools ethtool udev iproute iputils-ping ifupdown isc-dhcp-client ssh
source=http://ftp2.cn.debian.org/debian/

[Utils]
#General purpose utilities
packages=locales adduser vim less wget dialog usbutils
source=http://ftp2.cn.debian.org/debian/
创建根文件系统

sudo multistrap -a armhf -f multistrap_mindb.conf

执行完成后,target-rootfs即是所需的根文件系统。

配置软件包

使用QEMU来配置软件包,将target-rootfs作为root挂载来操作。

sudo cp /usr/bin/qemu-arm-static target-rootfs/usr/bin
sudo mount -o bind /dev/ target-rootfs/dev/
sudo LC_ALL=C LANGUAGE=C LANG=C chroot target-rootfs dpkg --configure -a
#这里就可以模拟板子情况执行相关命令,比如安装额外的软件包
sudo umount target-rootfs/dev/      #最后记得卸载

其中出现选择系统shell的提示框,选否,即不使用dash。

做最后的一些配置
#!/bin/sh
#Directory contains the target rootfs
TARGET_ROOTFS_DIR="target-rootfs"
#Board hostname
filename=$TARGET_ROOTFS_DIR/etc/hostname
echo acqua > $filename
#Default name servers
filename=$TARGET_ROOTFS_DIR/etc/resolv.conf
echo nameserver 8.8.8.8 > $filename
echo nameserver 8.8.4.4 >> $filename
#Default network interfaces
filename=$TARGET_ROOTFS_DIR/etc/network/interfaces
echo auto eth0 >> $filename
echo allow-hotplug eth0 >> $filename
echo iface eth0 inet dhcp >> $filename
#eth0 MAC address
echo hwaddress ether 00:04:25:12:34:56 >> $filename
#Set the the debug port
filename=$TARGET_ROOTFS_DIR/etc/inittab
echo T0:2345:respawn:/sbin/getty -L ttyS0 115200 vt100 >> $filename
#Set rules to change wlan dongles
filename=$TARGET_ROOTFS_DIR/etc/udev/rules.d/70-persistent-net.rules
echo SUBSYSTEM=='"net", ACTION=="add", DRIVERS=="?", ATTR{address}=="", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="wlan*", NAME="wlan0"' > $filename
#microSD partitions mounting
filename=$TARGET_ROOTFS_DIR/etc/fstab
echo /dev/mmcblk0p1 /boot vfat noatime 0 1 > $filename
echo /dev/mmcblk0p2 / ext4 noatime 0 1 >> $filename
echo proc /proc proc defaults 0 0 >> $filename
#Add the standard Debian non-free repositories useful to load
#closed source firmware (i.e. WiFi dongle firmware)
filename=$TARGET_ROOTFS_DIR/etc/apt/sources.list
echo deb http://http.debian.net/debian/ stretch main contrib non-free > $filename
设置密码和其它操作
sudo chroot target-rootfs passwd
sudo LC_ALL=C LANGUAGE=C LANG=C chroot target-rootfs apt-get install packagename

修改 target-rootfs/etc/ssh/sshd_config来使能root登录

PermitRootLogin yes

SPI Flash 杂谈

在一些低成本应用场景,需要在SPI flash上启动系统,这需要对Uboot和系统镜像做些适配。

本节将对SPI flash的编译及相关应用作详细描述。

SPI Flash 系统编译

在一些低成本应用场景,需要在SPI flash上启动系统,这需要对Uboot和系统镜像做些适配。

本文介绍SPI Flash镜像的制作过程。

Flash分区规划

这里 使用 MX25L25645G, 32M SPI flash 作为启动介质,规划分区如下:

分区序号 分区大小 分区作用 地址空间及分区名
mtd0 1MB spl+uboot 0x0000000-0x0100000 : "uboot"
mtd1 64KB dtb文件 0x0100000-0x0110000: "dtb"
mtd2 4MB linux内核 0x0110000-0x0510000 : "kernel"
mtd3 剩余 根文件系统 0x0510000-0x2000000 : "rootfs"

Uboot编译配置

由于目前Uboot环境变量固定存放在1MB位置之内,所有留给uboot的空间固定到flash前1MB的位置不变。

每个分区的大小必须是擦除块大小的整数倍,MX25L25645G的擦除块大小是64KB。

准备uboot

下载包含spi驱动的体验版本uboot,该驱动目前尚未合并到主线

git clone -b v3s-spi-experimental https://github.com/Lichee-Pi/u-boot.git
配置Flash支持型号

执行 make ARCH=arm menuconfig 打开uboot菜单配置,进入到 Device Drivers ‣ SPI Flash Support

注意看一下自己flash的厂家名称,例如选上Macronix SPI flash support用来支持测试用的flash:MX25L25645G。

如果使用的是16MB以上的flash,需要勾选flash bank支持选项,否则最多只能读到16MB: CONFIG_SPI_FLASH_BAR

配置uboot默认环境变量

在文件 include/configs/sun8i.h 中添加默认bootcmd和bootargs的环境变量设置,注意添加的位置在“ #include <configs/sunxi-common.h> ”的前边。

https://box.kancloud.cn/b4cce3d6f353a3aabb326dab402d58a3_1642x622.jpg
vi include/configs/sun8i.h
 #define CONFIG_BOOTCOMMAND   "sf probe 0; "                           \
                             "sf read 0x41800000 0x100000 0x10000; "  \
                             "sf read 0x41000000 0x110000 0x400000; " \
                             "bootz 0x41000000 - 0x41800000"

 #define CONFIG_BOOTARGS      "console=ttyS0,115200 earlyprintk panic=5 rootwait " \
                             "mtdparts=spi32766.0:1M(uboot)ro,64k(dtb)ro,4M(kernel)ro,-(rootfs) root=31:03 rw rootfstype=jffs2"
环境命令解析:
  • sf probe 0; //初始化Flash设备(CS拉低)
  • sf read 0x41800000 0x100000 0x10000; //从flash0x100000(1MB)位置读取dtb放到内存0x41800000偏移处。 //如果是bsp的bin,则是0x41d00000
  • sf read 0x41000000 0x110000 0x400000; //从flash0x110000(1MB+64KB)位置读取dtb放到内存0x41000000偏移处。
  • bootz 0x41000000 (内核地址)- 0x41800000(dtb地址) 启动内核
启动参数解析
  • console=ttyS0,115200 earlyprintk panic=5 rootwait //在串口0上输出信息
  • mtdparts=spi32766.0:1M(uboot)ro,64k(dtb)ro,4M(kernel)ro,-(rootfs) root=31:03 rw rootfstype=jffs2 //spi32766.0是设备名,后面是分区大小,名字,读写属性。
  • root=31:03表示根文件系统是mtd3;jffs2格式
编译uboot
time make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 2>&1 | tee build.log

会在目录下生成 u-boot-sunxi-with-spl.bin

linux内核编译配置

linux内核基于github上的版本https://github.com/Lichee-Pi/linux.git,分支为最新的zero-4.13.y

内核选项配置

执行 make ARCH=arm menuconfig 打开内核菜单配置,

进入到 Device Drivers ‣ Memory Technology Device (MTD) support

确保选择上mtd的 <*> Command line partition table parsing 支持,该项目用来解析uboot传递过来的flash分区信息。

以及SPI-NOR 设备的支持。

https://box.kancloud.cn/3ed4fd5d601aceb7f896521ba4c67cf6_1430x862.jpg

添加对jffs2文件系统的支持,路径在 File systems ‣ Miscellaneous filesystems ‣ Journalling Flash File System v2 (JFFS2) support

https://box.kancloud.cn/3be64c60667c0aa3a906f095171d1fda_1396x746.png
设备树配置

修改dts配置添加spi flash节点

vi arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts

添加spi节点配置:

&spi0 {
        status ="okay";

        mx25l25635e:mx25l25635e@0 {
                compatible = "jedec,spi-nor";
                reg = <0x0>;
                spi-max-frequency = <50000000>;
                #address-cells = <1>;
                #size-cells = <1>;
        };

};
https://box.kancloud.cn/611c8c327abb212991c3d0c02b0cf6d8_954x809.jpg

这里的flash型号需要在下表之中,否则将无法识别:(注意容量也一定要对应)

static const struct spi_device_id m25p_ids[] = {
        /*
        * Allow non-DT platform devices to bind to the "spi-nor" modalias, and
        * hack around the fact that the SPI core does not provide uevent
        * matching for .of_match_table
        */
        {"spi-nor"},

        /*
        * Entries not used in DTs that should be safe to drop after replacing
        * them with "spi-nor" in platform data.
        */
        {"s25sl064a"},  {"w25x16"},     {"m25p10"},     {"m25px64"},

        /*
        * Entries that were used in DTs without "jedec,spi-nor" fallback and
        * should be kept for backward compatibility.
        */
        {"at25df321a"}, {"at25df641"},  {"at26df081a"},
        {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
        {"mx25l25635e"},{"mx66l51235l"},
        {"n25q064"},    {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
        {"s25fl256s1"}, {"s25fl512s"},  {"s25sl12801"}, {"s25fl008k"},
        {"s25fl064k"},
        {"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
        {"m25p40"},     {"m25p80"},     {"m25p16"},     {"m25p32"},
        {"m25p64"},     {"m25p128"},
        {"w25x80"},     {"w25x32"},     {"w25q32"},     {"w25q32dw"},
        {"w25q80bl"},   {"w25q128"},    {"w25q256"},

        /* Flashes that can't be detected using JEDEC */
        {"m25p05-nonjedec"},    {"m25p10-nonjedec"},    {"m25p20-nonjedec"},
        {"m25p40-nonjedec"},    {"m25p80-nonjedec"},    {"m25p16-nonjedec"},
        {"m25p32-nonjedec"},    {"m25p64-nonjedec"},    {"m25p128-nonjedec"},

        /* Everspin MRAMs (non-JEDEC) */
        { "mr25h256" }, /* 256 Kib, 40 MHz */
        { "mr25h10" },  /*   1 Mib, 40 MHz */
        { "mr25h40" },  /*   4 Mib, 40 MHz */

        { },
};
退出菜单配置并编译内核和dts
 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j32
 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

准备镜像文件

下载根文件系统

首先选择rootfs文件系统,我是用的是群朋提供的最小根文件系统 rootfs-brmin.tar.gz,大小只有3M左右,下载地址在

https://pan.baidu.com/share/link?
shareid=1432204556&uk=3658413294#list/path=%2F
Lichee Zero>zero_imager.zip
制作jffs2文件系统

Flash支持jffs2文件系统格式,所以需要使用此该rootfs制作jffs2文件系统镜像、

下载jffs2文件系统制作工具

apt-get install mtd-utils

解压 rootfs-brmin.tar.gz

tar xzvf rootfs-brmin.tar.gz

计算好jffs的大小,可以使用zero_imager里的 make_jffs2.sh 32 生成

总空间是32M-1M-64K-4M=0x1AF0000

mkfs.jffs2 -s 0x100 -e 0x10000 -p 0x1AF0000 -d rootfs/ -o jffs2.img
  • 页大小0x100 256字节
  • 块大小0x10000 64k
  • jffs2分区总空间0x1AF0000
  • jffs2.img是生成的文件系统镜像。
最后将uboot,dtb,kernel,rootfs打包成一个系统镜像,命令如下;
(即zero_imager里的 make_spiflash.sh 32 dock
#!/bin/sh
dd if=/dev/zero of=flashimg.bin bs=1M count=$1
dd if=u-boot-sunxi-with-spl-$2.bin of=flashimg.bin bs=1K conv=notrunc
dd if=sun8i-v3s-licheepi-zero-$2.dtb of=flashimg.bin bs=1K seek=1024  conv=notrunc
dd if=zImage of=flashimg.bin bs=1K seek=1088  conv=notrunc
dd if=jffs2.img of=flashimg.bin  bs=1K seek=5184  conv=notrunc
第一步: 生成一个空文件,大小是32MB
第二步: 将uboot添加到文件开头
第三步: 将dtb放到1M偏移处
第四步: 将kernel放到1M+64K偏移处
第五步: 将rootfs放到1M+64K+4M偏移处

偏移大小是seek,单位是KB。

执行完毕后生成镜像文件 flashimg.bin

烧写镜像

下载sunxiflash烧写工具
git clone -b spi-rebase https://github.com/Icenowy/sunxi-tools.git

注解

注意是spi-rebase分支。

进入工具目录执行 make && sudo make install

如果出现:fel_lib.c:26:20: fatal error: libusb.h: No such file or directory,那需要安装libusb:

sudo apt-get install libusb-1.0-0-dev
进入fel模式

Zero有一个usb下载模式称为fel模式,进入fel模式有下面几种方式:

  1. TF卡和spi flash 同时没有可启动镜像;
    也就是说你不插卡,且焊接的是新的或者没有有效镜像的spi flash,那就上电自动进入fel下载模式
  2. TF卡中有进入fel模式的特殊固件 fel-sdboot.sunxi
    如果你的spiflash已经有了启动镜像,那么需要在TF卡中烧入一个sunxi提供的 启动工具 ( dd if=fel-sdboot.sunxi of=/dev/mmcblk0 bs=1024 seek=8 ), 那么插入该TF卡启动会进入fel模式;
  3. 上电时SPI_MISO拉低到地
    该引脚为boot引脚,上电时出于低电平即会进入fel下载模式。
sunxi-fel的操作

进入fel模式后使用usb数据线连接pc和zero,即可进行操作。

sudo sunxi-fel version              #查看连接的cpu信息
AWUSBFEX soc=00001681(V3s) 00000001 ver=0001 44 08 scratchpad=00007e00 00000000 00000000
sudo sunxi-fel spiflash-info        #显示flash信息
Manufacturer: Unknown (C2h), model: 20h, size: 33554432 bytes.

执行如下命令烧入我们前边制作好的镜像文件

sudo sunxi-fel -p spiflash-write 0 flashimg.bin
# -p 显示进度条
#   spiflash-info                   Retrieves basic information
#   spiflash-hex[dump] addr length  Dumps SPI flash region in hex
#   spiflash-read addr length file  Write SPI flash contents into file
#   spiflash-write addr file        Store file contents into SPI flash
https://box.kancloud.cn/30a15ac70a49ffa8e966700b72d91478_1088x83.jpg

SPI flash下载速度约50KB/s,等待5分钟(16MB)或者10分钟(32MB),烧写完成,如果一切顺利,重新上电zero那么就会进入linux系统了,账号是root没有密码。

https://box.kancloud.cn/94cba1c9e4539c2e54836d28a8bbe12b_1281x1002.jpg

附录: 启动日志

U-Boot SPL 2017.01-rc2-00073-gdd6e874-dirty (Oct 14 2017 - 16:33:01)
DRAM: 64 MiB
Trying to boot from sunxi SPI

U-Boot 2017.01-rc2-00073-gdd6e874-dirty (Oct 14 2017 - 16:33:01 +0000) Allwinner Technology

CPU:   Allwinner V3s (SUN8I 1681)
Model: Lichee Pi Zero
DRAM:  64 MiB
MMC:   SUNXI SD/MMC: 0
SF: Detected mx25l25635f with page size 256 Bytes, erase size 64 KiB, total 32 MiB
*** Warning - bad CRC, using default environment

Setting up a 800x480 lcd console (overscan 0x0)
dotclock: 33000kHz = 33000kHz: (1 * 3MHz * 66) / 6
In:    serial@01c28000
Out:   serial@01c28000
Err:   serial@01c28000


U-Boot 2017.01-rc2-00073-gdd6e874-dirty (Oct 14 2017 - 16:33:01 +0000) Allwinner Technology

CPU:   Allwinner V3s (SUN8I 1681)
Model: Lichee Pi Zero
DRAM:  64 MiB
MMC:   SUNXI SD/MMC: 0
SF: Detected mx25l25635f with page size 256 Bytes, erase size 64 KiB, total 32 MiB
*** Warning - bad CRC, using default environment

Setting up a 800x480 lcd console (overscan 0x0)
dotclock: 33000kHz = 33000kHz: (1 * 3MHz * 66) / 6
In:    serial@01c28000
Out:   serial@01c28000
Err:   serial@01c28000
Net:   No ethernet found.
starting USB...
No controllers found
Hit any key to stop autoboot:  0
SF: Detected mx25l25635f with page size 256 Bytes, erase size 64 KiB, total 32 MiB
device 0 offset 0x100000, size 0x10000
SF: 65536 bytes @ 0x100000 Read: OK
device 0 offset 0x110000, size 0x400000
SF: 4194304 bytes @ 0x110000 Read: OK
## Flattened Device Tree blob at 41800000
Booting using the fdt blob at 0x41800000
Loading Device Tree to 42dfa000, end 42dffc48 ... OK

Starting kernel ...
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.13.0-licheepi-zero+ (root@bf756b445919) (gcc version 6.3.1 20170404 (Linaro GCC 6.3-2017.05)) #10 SMP Sat Oct 14 16:59:37 UTC 2017
[    0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c5387d
[    0.000000] CPU: div instructions available: patching division code
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] OF: fdt: Machine model: Lichee Pi Zero XFJ
[    0.000000] Memory policy: Data cache writealloc
[    0.000000] percpu: Embedded 16 pages/cpu @c3de6000 s33868 r8192 d23476 u65536
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 15883
[    0.000000] Kernel command line: console=ttyS0,115200 earlyprintk panic=5 rootwait mtdparts=spi32766.0:1M(uboot)ro,64k(dtb)ro,4M(kernel)ro,-(rootfs) root=31:03 rw rootfstype=jffs2
[    0.000000] PID hash table entries: 256 (order: -2, 1024 bytes)
[    0.000000] Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
[    0.000000] Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
[    0.000000] Memory: 53576K/64036K available (6144K kernel code, 229K rwdata, 1512K rodata, 1024K init, 265K bss, 10460K reserved, 0K cma-reserved, 0K highmem)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
[    0.000000]     vmalloc : 0xc4000000 - 0xff800000   ( 952 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xc3e89000   (  62 MB)
[    0.000000]     pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
[    0.000000]     modules : 0xbf000000 - 0xbfe00000   (  14 MB)
[    0.000000]       .text : 0xc0008000 - 0xc0700000   (7136 kB)
[    0.000000]       .init : 0xc0900000 - 0xc0a00000   (1024 kB)
[    0.000000]       .data : 0xc0a00000 - 0xc0a39580   ( 230 kB)
[    0.000000]        .bss : 0xc0a3f65c - 0xc0a81b54   ( 266 kB)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] Hierarchical RCU implementation.
[    0.000000]      RCU event tracing is enabled.
[    0.000000]      RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=1.
[    0.000000] RCU: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
[    0.000000] arch_timer: cp15 timer(s) running at 24.00MHz (virt).
[    0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x588fe9dc0, max_idle_ns: 440795202592 ns
[    0.000008] sched_clock: 56 bits at 24MHz, resolution 41ns, wraps every 4398046511097ns
[    0.000019] Switching to timer-based delay loop, resolution 41ns
[    0.000187] clocksource: timer: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 79635851949 ns
[    0.000420] Console: colour dummy device 80x30
[    0.000457] Calibrating delay loop (skipped), value calculated using timer frequency.. 48.00 BogoMIPS (lpj=240000)
[    0.000475] pid_max: default: 32768 minimum: 301
[    0.000604] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.000619] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.001213] CPU: Testing write buffer coherency: ok
[    0.001589] /cpus/cpu@0 missing clock-frequency property
[    0.001612] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000
[    0.002074] Setting up static identity map for 0x40100000 - 0x40100060
[    0.002259] Hierarchical SRCU implementation.
[    0.002765] smp: Bringing up secondary CPUs ...
[    0.002781] smp: Brought up 1 node, 1 CPU
[    0.002790] SMP: Total of 1 processors activated (48.00 BogoMIPS).
[    0.002797] CPU: All CPU(s) started in SVC mode.
[    0.003559] devtmpfs: initialized
[    0.006668] VFP support v0.3: implementor 41 architecture 2 part 30 variant 7 rev 5
[    0.006932] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.006967] futex hash table entries: 256 (order: 2, 16384 bytes)
[    0.007136] pinctrl core: initialized pinctrl subsystem
[    0.008026] random: get_random_u32 called from bucket_table_alloc+0xf4/0x244 with crng_init=0
[    0.008162] NET: Registered protocol family 16
[    0.008655] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.009800] hw-breakpoint: found 5 (+1 reserved) breakpoint and 4 watchpoint registers.
[    0.009817] hw-breakpoint: maximum watchpoint size is 8 bytes.
[    0.023260] SCSI subsystem initialized
[    0.023562] usbcore: registered new interface driver usbfs
[    0.023652] usbcore: registered new interface driver hub
[    0.023747] usbcore: registered new device driver usb
[    0.023983] Linux video capture interface: v2.00
[    0.024036] pps_core: LinuxPPS API ver. 1 registered
[    0.024044] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[    0.024064] PTP clock support registered
[    0.024282] Advanced Linux Sound Architecture Driver Initialized.
[    0.024955] Bluetooth: Core ver 2.22
[    0.025029] NET: Registered protocol family 31
[    0.025037] Bluetooth: HCI device and connection manager initialized
[    0.025056] Bluetooth: HCI socket layer initialized
[    0.025066] Bluetooth: L2CAP socket layer initialized
[    0.025097] Bluetooth: SCO socket layer initialized
[    0.026313] clocksource: Switched to clocksource arch_sys_counter
[    0.037157] NET: Registered protocol family 2
[    0.037746] TCP established hash table entries: 1024 (order: 0, 4096 bytes)
[    0.037780] TCP bind hash table entries: 1024 (order: 1, 8192 bytes)
[    0.037803] TCP: Hash tables configured (established 1024 bind 1024)
[    0.037937] UDP hash table entries: 256 (order: 1, 8192 bytes)
[    0.037985] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes)
[    0.038205] NET: Registered protocol family 1
[    0.038812] RPC: Registered named UNIX socket transport module.
[    0.038833] RPC: Registered udp transport module.
[    0.038840] RPC: Registered tcp transport module.
[    0.038846] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    0.040940] workingset: timestamp_bits=30 max_order=14 bucket_order=0
[    0.048568] NFS: Registering the id_resolver key type
[    0.048618] Key type id_resolver registered
[    0.048627] Key type id_legacy registered
[    0.048672] jffs2: version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
[    0.050140] random: fast init done
[    0.053091] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 249)
[    0.053111] io scheduler noop registered
[    0.053118] io scheduler deadline registered
[    0.053358] io scheduler cfq registered (default)
[    0.053368] io scheduler mq-deadline registered
[    0.053376] io scheduler kyber registered
[    0.057981] sun8i-v3s-pinctrl 1c20800.pinctrl: initialized sunXi PIO driver
[    0.058417] name=allwinner,sun7i-a20-pwm
[    0.058432] npwm=2
[    0.127969] Serial: 8250/16550 driver, 8 ports, IRQ sharing disabled
[    0.131445] console [ttyS0] disabled
[    0.151721] 1c28000.serial: ttyS0 at MMIO 0x1c28000 (irq = 33, base_baud = 1500000) is a U6_16550A
[    0.780269] console [ttyS0] enabled
[    0.805297] 1c28400.serial: ttyS1 at MMIO 0x1c28400 (irq = 34, base_baud = 1500000) is a U6_16550A
[    0.835807] 1c28800.serial: ttyS2 at MMIO 0x1c28800 (irq = 35, base_baud = 1500000) is a U6_16550A
[    0.848508] libphy: Fixed MDIO Bus: probed
[    0.853001] usbcore: registered new interface driver r8152
[    0.858614] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    0.865135] ehci-platform: EHCI generic platform driver
[    0.870676] ehci-platform 1c1a000.usb: EHCI Host Controller
[    0.876350] ehci-platform 1c1a000.usb: new USB bus registered, assigned bus number 1
[    0.884288] ehci-platform 1c1a000.usb: irq 25, io mem 0x01c1a000
[    0.916344] ehci-platform 1c1a000.usb: USB 2.0 started, EHCI 1.00
[    0.923490] hub 1-0:1.0: USB hub found
[    0.927421] hub 1-0:1.0: 1 port detected
[    0.931878] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[    0.938171] ohci-platform: OHCI generic platform driver
[    0.943713] ohci-platform 1c1a400.usb: Generic Platform OHCI controller
[    0.950433] ohci-platform 1c1a400.usb: new USB bus registered, assigned bus number 2
[    0.958360] ohci-platform 1c1a400.usb: irq 26, io mem 0x01c1a400
[    1.031375] hub 2-0:1.0: USB hub found
[    1.035198] hub 2-0:1.0: 1 port detected
[    1.042745] udc-core: couldn't find an available UDC - added [g_ether] to list of pending drivers
[    1.052618] sun6i-rtc 1c20400.rtc: rtc core: registered rtc-sun6i as rtc0
[    1.059513] sun6i-rtc 1c20400.rtc: RTC enabled
[    1.064048] i2c /dev entries driver
[    1.069222] usbcore: registered new interface driver uvcvideo
[    1.074974] USB Video Class driver (1.1.1)
[    1.079833] sunxi-wdt 1c20ca0.watchdog: Watchdog enabled (timeout=16 sec, nowayout=0)
[    1.087825] Bluetooth: HCI UART driver ver 2.3
[    1.092273] Bluetooth: HCI UART protocol Three-wire (H5) registered
[    1.156357] sunxi-mmc 1c0f000.mmc: base:0xc407b000 irq:23
[    1.162805] usbcore: registered new interface driver usbhid
[    1.168456] usbhid: USB HID core driver
[    1.174122] NET: Registered protocol family 17
[    1.178794] Key type dns_resolver registered
[    1.183228] Registering SWP/SWPB emulation handler
[    1.193806] simple-framebuffer 43e89000.framebuffer: framebuffer at 0x43e89000, 0x177000 bytes, mapped to 0xc4400000
[    1.204454] simple-framebuffer 43e89000.framebuffer: format=x8r8g8b8, mode=800x480x32, linelength=3200
[    1.222854] Console: switching to colour frame buffer device 100x30
[    1.235317] simple-framebuffer 43e89000.framebuffer: fb0: simplefb registered!
[    1.243916] usb_phy_generic usb_phy_generic.0.auto: usb_phy_generic.0.auto supply vcc not found, using dummy regulator
[    1.255346] musb-hdrc musb-hdrc.1.auto: MUSB HDRC host driver
[    1.261186] musb-hdrc musb-hdrc.1.auto: new USB bus registered, assigned bus number 3
[    1.270315] hub 3-0:1.0: USB hub found
[    1.274184] hub 3-0:1.0: 1 port detected
[    1.279498] using random self ethernet address
[    1.283985] using random host ethernet address
[    1.289160] usb0: HOST MAC 8e:ca:5f:61:47:a8
[    1.293475] usb0: MAC f2:57:f4:ad:74:af
[    1.297416] using random self ethernet address
[    1.301858] using random host ethernet address
[    1.306400] g_ether gadget: Ethernet Gadget, version: Memorial Day 2008
[    1.313010] g_ether gadget: g_ether ready
[    1.317402] sun6i-rtc 1c20400.rtc: setting system clock to 1970-01-01 00:56:52 UTC (3412)
[    1.325834] vcc3v0: disabling
[    1.328911] vcc5v0: disabling
[    1.331879] ALSA device list:
[    1.334841]   No soundcards found.
[    1.339241] VFS: Cannot open root device "31:03" or unknown-block(31,3): error -19
[    1.346938] Please append a correct "root=" boot option; here are the available partitions:
[    1.355286] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(31,3)
[    1.363630] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.13.0-licheepi-zero+ #10
[    1.370926] Hardware name: Allwinner sun8i Family
[    1.375664] [<c010e58c>] (unwind_backtrace) from [<c010b2b0>] (show_stack+0x10/0x14)
[    1.383408] [<c010b2b0>] (show_stack) from [<c06923dc>] (dump_stack+0x84/0x98)
[    1.390632] [<c06923dc>] (dump_stack) from [<c011b728>] (panic+0xdc/0x248)
[    1.397507] [<c011b728>] (panic) from [<c09011d0>] (mount_block_root+0x188/0x25c)
[    1.404985] [<c09011d0>] (mount_block_root) from [<c09013c4>] (mount_root+0x120/0x128)
[    1.412894] [<c09013c4>] (mount_root) from [<c090151c>] (prepare_namespace+0x150/0x194)
[    1.420891] [<c090151c>] (prepare_namespace) from [<c0900e20>] (kernel_init_freeable+0x1bc/0x1cc)
[    1.429755] [<c0900e20>] (kernel_init_freeable) from [<c06a514c>] (kernel_init+0x8/0x108)
[    1.437927] [<c06a514c>] (kernel_init) from [<c0107638>] (ret_from_fork+0x14/0x3c)
[    1.445499] Rebooting in 5 seconds..
U-Boot SPL 2017.01-rc2-00073-gdd6e874-dirty (Nov 26 2017 - 15:10:41)
DRAM: 64 MiB
Trying to boot from sunxi SPICPU:   Allwinner V3s (SUN8I 1681)
Model: Lichee Pi Zero
DRAM:  64 MiB
MMC:   SUNXI SD/MMC: 0
SF: Detected w25q128bv with page size 256 Bytes, erase size 64 KiB, total 16 MiB
*** Warning - bad CRC, using default environment

Setting up a 800x480 lcd console (overscan 0x0)
dotclock: 27000kHz = 27000kHz: (1 * 3MHz * 54) / 6
beep 0
beep 1
beep 0
beep 1
beep 0
beep 1
In:    serial@01c28000
Out:   serial@01c28000
Err:   serial@01c28000
CPU:   Allwinner V3s (SUN8I 1681)
Model: Lichee Pi Zero
DRAM:  64 MiB
MMC:   SUNXI SD/MMC: 0
SF: Detected w25q128bv with page size 256 Bytes, erase size 64 KiB, total 16 MiB
*** Warning - bad CRC, using default environment

Setting up a 800x480 lcd console (overscan 0x0)
dotclock: 27000kHz = 27000kHz: (1 * 3MHz * 54) / 6
beep 0
beep 1
beep 0
beep 1
beep 0
beep 1
In:    serial@01c28000
Out:   serial@01c28000
Err:   serial@01c28000
Net:   No ethernet found.
starting USB...
No controllers found
Hit any key to stop autoboot:  0
SF: Detected w25q128bv with page size 256 Bytes, erase size 64 KiB, total 16 MiB
device 0 offset 0x100000, size 0x10000
SF: 65536 bytes @ 0x100000 Read: OK
device 0 offset 0x110000, size 0x400000
SF: 4194304 bytes @ 0x110000 Read: OK
## Flattened Device Tree blob at 41800000
Booting using the fdt blob at 0x41800000
Loading Device Tree to 42dfa000, end 42dffbfa ... OK

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.13.0-licheepi-zero+ (root@bf756b445919) (gcc version 6.3.1 20170404 (Linaro GCC 6.3-2017.05)) #95 SMP Mon Nov 27 01:20:31 UTC 2017
[    0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c5387d
[    0.000000] CPU: div instructions available: patching division code
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] OF: fdt: Machine model: Lichee Pi Zero XFJ
[    0.000000] Memory policy: Data cache writealloc
[    0.000000] percpu: Embedded 15 pages/cpu @c3de7000 s32588 r8192 d20660 u61440
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 15883
[    0.000000] Kernel command line: console=ttyS0,115200 earlyprintk panic=5 rootwait mtdparts=spi32766.0:1M(uboot)ro,64k(dtb)ro,4M(kernel)ro,-(rootfs) root=31:03 rw rootfstype=jffs2
[    0.000000] PID hash table entries: 256 (order: -2, 1024 bytes)
[    0.000000] Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
[    0.000000] Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
[    0.000000] Memory: 55708K/64036K available (4096K kernel code, 187K rwdata, 1144K rodata, 1024K init, 232K bss, 8328K reserved, 0K cma-reserved, 0K highmem)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
[    0.000000]     vmalloc : 0xc4000000 - 0xff800000   ( 952 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xc3e89000   (  62 MB)
[    0.000000]     pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
[    0.000000]     modules : 0xbf000000 - 0xbfe00000   (  14 MB)
[    0.000000]       .text : 0xc0008000 - 0xc0500000   (5088 kB)
[    0.000000]       .init : 0xc0700000 - 0xc0800000   (1024 kB)
[    0.000000]       .data : 0xc0800000 - 0xc082ee00   ( 188 kB)
[    0.000000]        .bss : 0xc08332d0 - 0xc086d584   ( 233 kB)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] Hierarchical RCU implementation.
[    0.000000]      RCU event tracing is enabled.
[    0.000000]      RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=1.
[    0.000000] RCU: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
[    0.000000] arch_timer: cp15 timer(s) running at 24.00MHz (virt).
[    0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x588fe9dc0, max_idle_ns: 440795202592 ns
[    0.000009] sched_clock: 56 bits at 24MHz, resolution 41ns, wraps every 4398046511097ns
[    0.000024] Switching to timer-based delay loop, resolution 41ns
[    0.000214] clocksource: timer: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 79635851949 ns
[    0.000465] Console: colour dummy device 80x30
[    0.000506] Calibrating delay loop (skipped), value calculated using timer frequency.. 48.00 BogoMIPS (lpj=240000)
[    0.000525] pid_max: default: 32768 minimum: 301
[    0.000672] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.000692] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.001393] CPU: Testing write buffer coherency: ok
[    0.001817] /cpus/cpu@0 missing clock-frequency property
[    0.001842] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000
[    0.002345] Setting up static identity map for 0x40100000 - 0x40100060
[    0.002541] Hierarchical SRCU implementation.
[    0.003148] smp: Bringing up secondary CPUs ...
[    0.003163] smp: Brought up 1 node, 1 CPU
[    0.003175] SMP: Total of 1 processors activated (48.00 BogoMIPS).
[    0.003184] CPU: All CPU(s) started in SVC mode.
[    0.004066] devtmpfs: initialized
[    0.007259] VFP support v0.3: implementor 41 architecture 2 part 30 variant 7 rev 5
[    0.007578] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.007614] futex hash table entries: 256 (order: 2, 16384 bytes)
[    0.007813] pinctrl core: initialized pinctrl subsystem
[    0.008896] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.010158] hw-breakpoint: found 5 (+1 reserved) breakpoint and 4 watchpoint registers.
[    0.010179] hw-breakpoint: maximum watchpoint size is 8 bytes.
[    0.022957] SCSI subsystem initialized
[    0.023159] usbcore: registered new interface driver usbfs
[    0.023235] usbcore: registered new interface driver hub
[    0.023362] usbcore: registered new device driver usb
[    0.023474] Linux video capture interface: v2.00
[    0.023517] pps_core: LinuxPPS API ver. 1 registered
[    0.023526] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[    0.023695] Advanced Linux Sound Architecture Driver Initialized.
[    0.024215] clocksource: Switched to clocksource arch_sys_counter
[    0.039085] workingset: timestamp_bits=30 max_order=14 bucket_order=0
[    0.046435] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.046808] jffs2: version 2.2. (NAND) ? 2001-2006 Red Hat, Inc.
[    0.048756] random: fast init done
[    0.052407] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 250)
[    0.052432] io scheduler noop registered
[    0.052442] io scheduler deadline registered
[    0.052669] io scheduler cfq registered (default)
[    0.052681] io scheduler mq-deadline registered
[    0.052690] io scheduler kyber registered
[    0.057653] sun8i-v3s-pinctrl 1c20800.pinctrl: initialized sunXi PIO driver
[    0.058150] name=allwinner,sun7i-a20-pwm
[    0.058168] npwm=2
[    0.139525] Serial: 8250/16550 driver, 8 ports, IRQ sharing disabled
[    0.143224] console [ttyS0] disabled
[    0.163532] 1c28000.serial: ttyS0 at MMIO 0x1c28000 (irq = 33, base_baud = 1500000) is a U6_16550A
[    0.676919] console [ttyS0] enabled
[    0.702093] 1c28400.serial: ttyS1 at MMIO 0x1c28400 (irq = 34, base_baud = 1500000) is a U6_16550A
[    0.732771] 1c28800.serial: ttyS2 at MMIO 0x1c28800 (irq = 35, base_baud = 1500000) is a U6_16550A
[    0.747219] m25p80 spi32766.0: w25q128 (16384 Kbytes)
[    0.752288] in cmdline partion
[    0.755486] p4 : size=100000
[    0.758369] p4 : size=10000
[    0.761160] p4 : size=400000
[    0.764037] p4 : size=ffffffff
[    0.767126] spi32766.0: parser cmdlinepart: 4
[    0.771481] 4 cmdlinepart partitions found on MTD device spi32766.0
[    0.777758] Creating 4 MTD partitions on "spi32766.0":
[    0.782904] 0x000000000000-0x000000100000 : "uboot"
[    0.789535] 0x000000100000-0x000000110000 : "dtb"
[    0.795874] 0x000000110000-0x000000510000 : "kernel"
[    0.802292] 0x000000510000-0x000001000000 : "rootfs"
[    0.809706] sun6i-rtc 1c20400.rtc: rtc core: registered rtc-sun6i as rtc0
[    0.816602] sun6i-rtc 1c20400.rtc: RTC enabled
[    0.821153] i2c /dev entries driver
[    0.826715] sunxi-wdt 1c20ca0.watchdog: Watchdog enabled (timeout=16 sec, nowayout=0)
[    0.835775] usbcore: registered new interface driver usbhid
[    0.841355] usbhid: USB HID core driver
[    0.846926] Registering SWP/SWPB emulation handler
[    0.856875] simple-framebuffer 43e89000.framebuffer: framebuffer at 0x43e89000, 0x177000 bytes, mapped to 0xc4400000
[    0.867547] simple-framebuffer 43e89000.framebuffer: format=x8r8g8b8, mode=800x480x32, linelength=3200
[    0.885830] Console: switching to colour frame buffer device 100x30
[    0.900736] simple-framebuffer 43e89000.framebuffer: fb0: simplefb registered!
[    0.908121] sun6i-rtc 1c20400.rtc: setting system clock to 1970-01-01 02:33:59 UTC (9239)
[    0.916575] vcc3v0: disabling
[    0.919554] vcc3v3: disabling
[    0.922519] vcc5v0: disabling
[    0.925539] ALSA device list:
[    0.928507]   No soundcards found.
[    0.994326] random: crng init done
[    1.519199] VFS: Mounted root (jffs2 filesystem) on device 31:3.
[    1.526365] devtmpfs: mounted
[    1.530825] Freeing unused kernel memory: 1024K
Starting logging: OK
Starting mdev...
modprobe: can't change directory to '/lib/modules': No such file or directory
Initializing random number generator... done.
Starting network: ip: socket: Function not implemented
ip: socket: Function not implemented
FAIL

Welcome to Lichee Pi
Lichee login:

附录:sunxi-fel帮助说明

sunxi-fel v1.4.1-87-g78a7566

Usage: sunxi-fel [options] command arguments... [command...]
    -h, --help                      Print this usage summary and exit
    -v, --verbose                   Verbose logging
    -p, --progress                  "write" transfers show a progress bar
    -l, --list                      Enumerate all (USB) FEL devices and exit
    -d, --dev bus:devnum            Use specific USB bus and device number
        --sid SID                   Select device by SID key (exact match)

    spl file                        Load and execute U-Boot SPL
        If file additionally contains a main U-Boot binary
        (u-boot-sunxi-with-spl.bin), this command also transfers that
        to memory (default address from image), but won't execute it.

    uboot file-with-spl             like "spl", but actually starts U-Boot
        U-Boot execution will take place when the fel utility exits.
        This allows combining "uboot" with further "write" commands
        (to transfer other files needed for the boot).

    hex[dump] address length        Dumps memory region in hex
    dump address length             Binary memory dump
    exe[cute] address               Call function address
    reset64 address                 RMR request for AArch64 warm boot
    memmove dest source size        Copy <size> bytes within device memory
    readl address                   Read 32-bit value from device memory
    writel address value            Write 32-bit value to device memory
    read address length file        Write memory contents into file
    write address file              Store file contents into memory
    write-with-progress addr file   "write" with progress bar
    write-with-gauge addr file      Output progress for "dialog --gauge"
    write-with-xgauge addr file     Extended gauge output (updates prompt)
    multi[write] # addr file ...    "write-with-progress" multiple files,
                    sharing a common progress status
    multi[write]-with-gauge ...     like their "write-with-*" counterpart,
    multi[write]-with-xgauge ...      but following the 'multi' syntax:
                    <#> addr file [addr file [...]]
    echo-gauge "some text"          Update prompt/caption for gauge output
    ver[sion]                       Show BROM version
    sid                             Retrieve and output 128-bit SID key
    clear address length            Clear memory
    fill address length value       Fill memory
    spiflash-info                   Retrieves basic information
    spiflash-hex[dump] addr length  Dumps SPI flash region in hex
    spiflash-read addr length file  Write SPI flash contents into file
    spiflash-write addr file        Store file contents into SPI flash

sunxi-fel增加对16M以上Flash的支持

由于SPI flash 的地址是24bit,也就是最大16M 地址空间,所以对于32M flash,需要增加bank切换支持。

uboot中有 CONFIG_SPI_FLASH_BAR 选项可以使能bank切换。

但是sunxi-fel中尚未支持,所以下载的时候超出16M会循环覆盖掉。

这里介绍对sunxi-fel增加16M以上flash支持的方法。

u-boot的支持

drivers/mtd/spi/spi_flash.c
 static int write_bar(struct spi_flash *flash, u32 offset)
 {
     u8 cmd, bank_sel;
     int ret;

     bank_sel = offset / (SPI_FLASH_16MB_BOUN << flash->shift);
     if (bank_sel == flash->bank_curr)
         goto bar_end;

     cmd = flash->bank_write_cmd;
     ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1);
     if (ret < 0) {
         debug("SF: fail to write bank register\n");
         return ret;
     }

 bar_end:
     flash->bank_curr = bank_sel;
     return flash->bank_curr;
 }

sunxi-fel的支持

fel-spiflash.c
 #define CMD_WRITE_ENABLE 0x06
 #define SPI_FLASH_16MB_BOUN  0x1000000
 # define CMD_BANKADDR_BRWR              0x17        //only SPANSION flash use it
 # define CMD_BANKADDR_BRRD              0x16
 # define CMD_EXTNADDR_WREAR             0xC5
 # define CMD_EXTNADDR_RDEAR             0xC8
 size_t bank_curr = 0;

 void aw_fel_spiflash_write_helper(feldev_handle *dev,
                 uint32_t offset, void *buf, size_t len,
                 size_t erase_size, uint8_t erase_cmd,
                 size_t program_size, uint8_t program_cmd)
 {
     uint8_t *buf8 = (uint8_t *)buf;
     size_t max_chunk_size = dev->soc_info->scratch_addr - dev->soc_info->spl_addr;
     size_t cmd_idx, bank_sel;

     if (max_chunk_size > 0x1000)
         max_chunk_size = 0x1000;
     uint8_t *cmdbuf = malloc(max_chunk_size);
     cmd_idx = 0;

     prepare_spi_batch_data_transfer(dev, dev->soc_info->spl_addr);
     //add bank support
     {
     cmd_idx = 0;
     bank_sel = offset /SPI_FLASH_16MB_BOUN;
     if (bank_sel == bank_curr)
         goto bar_end;

     /* Emit write enable command */
     cmdbuf[cmd_idx++] = 0;
     cmdbuf[cmd_idx++] = 1;
     cmdbuf[cmd_idx++] = CMD_WRITE_ENABLE;
     /* Emit write bank */
     cmdbuf[cmd_idx++] = 0;
     cmdbuf[cmd_idx++] = 2;
     cmdbuf[cmd_idx++] = CMD_EXTNADDR_WREAR;
     cmdbuf[cmd_idx++] = offset >> 24;
     /* Emit wait for completion */
     cmdbuf[cmd_idx++] = 0xFF;
     cmdbuf[cmd_idx++] = 0xFF;
     /* Emit the end marker */
     cmdbuf[cmd_idx++] = 0;
     cmdbuf[cmd_idx++] = 0;
     aw_fel_write(dev, cmdbuf, dev->soc_info->spl_addr, cmd_idx);
     aw_fel_remotefunc_execute(dev, NULL);
     bar_end:
         bank_curr = bank_sel;
     }

     cmd_idx = 0;

重新编译sunxi-fel后就可以烧录32M flash了~

overlayfs的使用

前面简单做的jffs2文件系统,没有经过压缩,体积比较大,在spi flash中放不了多少东西。

这里介绍使用overlayfs有效利用flash空间。(著名的openwrt上用的就是这一套)

squashfs 使用

squashfs是只读压缩文件系统,我们把相对固定的根文件系统部分使用squash压缩存储。

把目录文件放在rootfs下,然后执行

mksquashfs rootfs rootfs-sq.img

即可获得squashfs的根文件系统。

启动squashfs需要改动uboot的环境变量:

include/configs/sun8i.h
 #define CONFIG_BOOTARGS      "console=ttyS0,115200 earlyprintk panic=5 rootwait " \
                             "mtdparts=spi32766.0:1M(uboot)ro,64k(dtb)ro,4M(kernel)ro,-(rootfs) root=31:03 rw rootfstype=squashfs

以及在内核编译时选上相应选项。

在启动系统后可以在/proc/filesystems 查看系统支持的文件系统:

# cat /proc/filesystems
nodev       sysfs
nodev       rootfs
nodev       ramfs
nodev       bdev
nodev       proc
nodev       cgroup
nodev       cgroup2
nodev       tmpfs
nodev       devtmpfs
nodev       configfs
nodev       debugfs
nodev       sockfs
nodev       pipefs
nodev       rpc_pipefs
nodev       devpts
    squashfs
    vfat
nodev       nfs
nodev       nfs4
nodev       jffs2
nodev       overlay

overlayfs使用

overlayfs 顾名思义,就是一种覆盖式的文件系统。

常见用法是,底层文件系统只读,上层文件系统可读写;著名的docker就是使用的overlayfs。

在嵌入式应用中,底层只读系统一般使用squashfs,上层使用jffs2.

首先我们重新分区:

#define CONFIG_BOOTARGS      "console=ttyS0,115200 earlyprintk panic=5 rootwait " \
                            "mtdparts=spi32766.0:1M(uboot)ro,64k(dtb)ro,4M(kernel)ro,16M(rootfs) -(data) root=31:03 rw rootfstype=squashfs

这样就是给固定只读的rootfs划分了16M空间,剩余空间约10.9M划分给可读写的数据分区。

同时在只读根文件系统下新建overlay目录。

开机启动后,执行以下命令来挂载overlayfs:

注意需要使用正规mount命令,而不是busybox的mount命令才能挂载

mount -n -t jffs2 /dev/mtdblock4  /overlay
mount -n -t overlay overlayfs:/overlay -o lowerdir=/,upperdir=/overlay,workdir=/workdir /mnt

jffs2文件系统挂载不上的常见原因

内核命令行不正确

[ 0.000000] Kernel command line: console=ttyS0,115200 earlyprintk panic=5 rootwait mtdparts=spi32766.0:1M(uboot)ro,64k(dtb)ro,4M(kernel)ro,3008k(rootfs) root=31:03 rw rootfstype=jffs2

内核命令行应正确显示分区信息,如果分区信息不正确则无法正确挂载。

分区信息在 include/configs/sun8i.h 里修改

vi include/configs/sun8i.h
 #define CONFIG_BOOTCOMMAND   "sf probe 0; "                           \
                             "sf read 0x41800000 0x100000 0x10000; "  \
                             "sf read 0x41000000 0x110000 0x400000; " \
                             "bootz 0x41000000 - 0x41800000"

 #define CONFIG_BOOTARGS      "console=ttyS0,115200 earlyprintk panic=5 rootwait " \
                             "mtdparts=spi32766.0:1M(uboot)ro,64k(dtb)ro,4M(kernel)ro,-(rootfs) root=31:03 rw rootfstype=jffs2"

识别不到分区

正常识别到分区,应该报以下信息:

[    0.862858] m25p80 spi32766.0: w25q128 (16384 Kbytes)
[    0.867927] in cmdline partion
[    0.871123] p4 : size=100000
[    0.874005] p4 : size=10000
[    0.876796] p4 : size=400000
[    0.879714] p4 : size=2f0000
[    0.882596] spi32766.0: parser cmdlinepart: 4
[    0.886949] 4 cmdlinepart partitions found on MTD device spi32766.0
[    0.893230] Creating 4 MTD partitions on "spi32766.0":
[    0.898374] 0x000000000000-0x000000100000 : "uboot"
[    0.904828] 0x000000100000-0x000000110000 : "dtb"
[    0.910973] 0x000000110000-0x000000510000 : "kernel"
[    0.917258] 0x000000510000-0x000000800000 : "rootfs"

如果没有显示以上信息,则需要确认有无勾选相关驱动

进入到 Device Drivers ‣ Memory Technology Device (MTD) support

确保选择上mtd的 <*> Command line partition table parsing 支持,该项目用来解析uboot传递过来的flash分区信息。

以及SPI-NOR 设备的支持。

添加对jffs2文件系统的支持,路径在 File systems ‣ Miscellaneous filesystems ‣ Journalling Flash File System v2 (JFFS2) support

jffs2 Magic bitmask 错误

jffs2: Node at 0x00000f6c with length 0x00000144 would run over the end of the erase block
[    1.133830] jffs2: Perhaps the file system was created with the wrong erase size?
[    1.141435] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000f70: 0x0144 instead
[    1.150994] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000f74: 0x912a instead
[    1.160547] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000f78: 0x0002 instead
[    1.170127] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000f7c: 0x000d instead
[    1.180689] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000f80: 0x81a4 instead
[    1.190183] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000f84: 0x03e8 instead
[    1.199668] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000f88: 0x11d8 instead
[    1.209151] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000f8c: 0xdec2 instead
[    1.218634] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000f90: 0xdec2 instead
[    1.228102] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000f94: 0xdec2 instead
[    1.237581] jffs2: Further such events for this erase block will not be printed
[    1.245110] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00001000: 0x3fb1 instead
[    1.254615] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00001004: 0x1a28 instead
[    1.264102] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00001008: 0x7f01 instead
[    1.273586] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x0000100c: 0x505d instead
[    1.283098] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00001010: 0x84c8 instead
[    1.292588] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00001014: 0xd8d1 instead
[    1.302072] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00001018: 0x4001 instead
[    1.311555] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x0000101c: 0x8485 instead
[    1.321033] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00001020: 0x65b1 instead
[    1.330514] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00001024: 0x5d81 instead

该错误有若干种可能:

  1. jffs2镜像生成错误

    mkfs.jffs2 -s 0x100 -e 0x10000 -p 0xAF0000 -d rootfs-brmin -o jffs2-brmin.img

    • 这里-s代表页大小,普通spi nor flash的页大小是256字节,即0x100
    • -e表示擦除的块大小,普通spi nor flash的块大小是64K字节,即0x10000
    • -p表示分区大小,在生成时会擦除分区大小的flash初始化。
    • 这里必须和uboot里指定的分区大小一致,否则会出现脏页。
  2. 内核使用了扇区擦除

    • mkfs.jffs2 使用的最小擦除尺寸是8KB,而spi flash的扇区大小是4KB,所以按照扇区擦除的话,会无法使用,所以必须使用块擦除。
    • 编译内核前先确认下drivers/mtd/spi-nor/spi-nor.c里,自己使用的flash的相关信息
    • #define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */
    • 如果发现信息里有SECT_4K,则会导致jffs2不能正常擦除(64KB),需要去掉该flag。

其它摘录

Question1:JFFS2 error: (1) jffs2_build_inode_pass1: child dir "alsa" (ino #1159) of dir ino #1074 appears to be a hard link
JFFS2 error: (1) jffs2_build_inode_pass1: child dir "l" (ino #1170) of dir ino #1075 appears to be a hard link
原由 : flash没有erase彻底.
VFS: Mounted root (jffs2 filesystem) on device 31:1. Freeing init memory: 136K
JFFS2 notice: (1) check_node_data: wrong data CRC in data node at 0x0f0a7f78: read 0x4462b066, calculated 0x48ea177f.
JFFS2 error: (488) jffs2_do_read_inode_internal: Argh. Special inode #139 with mode 0x61b0 had more than one node iget() failed for ino #139 mknod: /dev/null: File exists
Populating /dev using udev: udevd (499): /proc/499/oom_adj is deprecated, please use /proc/499/oom_score_adj instead.
JFFS2 error: (500) jffs2_do_read_inode_internal: Argh. Special inode #1123 with mode 0x21b0 had more than one node iget() failed for ino #1123
Question2:jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000024: 0x2b10 instead
mkfs.jffs2 -s 的参数问题 对照FLASH的大小再重新生成镜像文件过即可 The answer this means that the data on your flash device is not a valid JFFS2 file system.
There is no single solution for this problem, but we will try to provide you some ideas how to fix this.
The first question you should try to answer is "why the data on my flash device is incorrect so that JFFS2 rejects to deal with it?". There are may be a plenty of reasons, e.g.:
you flash driver is severely buggy so it reads trash instead of valid data;
you flashed some trash instead of a valid JFFS2 image;
you did not manage to flash JFFS2 image correctly so that you ended up with
garbage on your flash, although the original image was perfectly fine;
you forgot to erase your flash before flashing it, etc.
Anyways, JFFS2 wouldn't complain if it was able to find correct data. As it does complain, there is something wrong with the data it reads.
One common mistake is to use /dev/mtdX or /dev/mtdblockX devices to flash JFFS2 images on NAND flashes. E.g.
cp jffs2_fs.img /dev/mtd2
This is incorrect because when dealing with NAND flashes one has to skip bad
eraseblocks and write only in NAND page size chunks. Please, use the nandwrite utility instead.
Also please, do not forget to erase your flash before flashing the image. You may use the
flash_eraseall utility for this. And it makes sense to make sure the erase functionality
actually works by reading the erased MTD device back and checking that only 0xFF bytes were read.
You may try to check if your flash driver works correctly and if you flashed the file system image correctly by means of reading the flash back after you have flashed your image, and compare the read image with the original one. Please, use the nandread utility to read from NAND flashes.
You can also do the following experiment to make sure JFFS2 works well. Erase your MTD device and mount it to JFFS2. You will end up with an empty file system. Copy some files to the JFFS2 file system and unmount it. Then mount it again and see if it mounts without problems. If it does, this is most probably not a JFFS2 bug.
Question3:Empty flash at 0xXXXXXXXX ends at 0xXXXXXXXX
This message is generated if a block of data is partially written. It is generally not a sign of any problem.
Question4:Name CRC failed on node at 0x00b620c8: Read 0x640c8ca3, calculated 0x795111fe
重启,则不会有如上CRC错误信息。 问题原因:
我在烧写jffs2 img之前,使用fis init -f 来擦除flash。fis init -f 命令执行完以后,flash空间就都是0xFF了!即使在mkfs.jffs2的时候使用'-p'参数指定最终输出img的大小,但是超出文件系统的部分也会被填充为0xFF!但这可不是jffs2的格式!
我用fis create分了5M多(0x590000)的分区,但是jffs2fs.img只有不到3M(0x250000),那么把它烧写到flash以后,分区中除了jffs2 img之外剩余的flash空间(大概2M)全是0xFF,这不是jffs2要求的格式,所以,会发出CRC错误的信息。假如有一种工具,他可以将flash format为jffs2的格式,那么就不会出现这个问题了。目前我还没有找到这种工具,但是,可以确信的是:上面的CRC错误是不影响jffs2文件系统的使用
http://blog.chinaunix.net/space.php?uid=20727076&do=blog&id=1885384

Question5: VFS: Mounted root (jffs2 filesystem) readonly.
Freeing unused kernel memory: 304k freed Error -3 while decompressing! 804878c4(1884)->81200000(16384)
Failed to execute /linuxrc. Attempting defaults...
Kernel panic - not syncing: No init found. Try passing init= option to kernel.
原由 : 没有仔细看 mkfs.jffs2 的手册, 须要指定-b参数, 大小与PAGE_SIZE一样 (查看.config文件 CONFIG_PAGE_SIZE_16KB=y).
Question6:
共提示以下几种错误:
Empty flash at 0x00258c88 ends at 0x00258c8c
jffs2_scan_inode_node(): CRC failed on node at 0x002873f0: Read 0x50dc72ec, calculated 0xafbffd1d
jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x002d24ac: 0x000d instead JFFS2 notice: (1) jffs2_build_xattr_subsystem: complete building xattr subsystem, 0 of xdatum (0 unchecked, 0 orphan) and 0 of xref (0 dead, 0 orphan) found. VFS: Mounted root (jffs2 filesystem). Freeing init memory: 124K
JFFS2 notice: (1) check_node_data: wrong data CRC in data node at 0x00012000: read 0x1a9bfab2, calculated 0xdc27bef6.
JFFS2 notice: (728) read_dnode: wrong data CRC in data node at 0x0000e438: read 0x3dcf6001, calculated 0xcb81f1ee.
JFFS2 warning: (1) jffs2_do_read_inode_internal: no data nodes found for ino #14 JFFS2 notice: (1) jffs2_do_read_inode_internal: but it has children so we fake some modes for it
Failed to execute /linuxrc. Attempting defaults...
Kernel panic - not syncing: No init found. Try passing init= option to kernel. 分析:
记得JFFS2是采用自己的ECC算法,但是在内核中又打开了S3C2410_HARDWARE_ECC 解决方案: 去掉硬件ECC

JFFS2文件系统简介

JFFS2 是一个日志结构(log-structured)的文件系统,包含数据 和 原数据(meta-data)的节点在闪存上顺序的存储。

JFFS2 之所以选择日志结构的存储方式,是因为对闪存的更新应该是 out-of-place 的更新方式,而不是对磁盘的 in-place 的更新方式。在闪存上 in-place 更新方式的问题我们已经在闪存转换层一节描述过了。

JFFS2介绍

节点头部定义和兼容性

JFFS2 将文件系统的数据和原数据以节点的形式存储在闪存上,具体来说节点头部的定义如下:

http://www.ibm.com/developerworks/cn/linux/l-jffs2/images/image002.gif
  • 幻数屏蔽位:0x1985 用来标识 JFFS2 文件系统。

  • 节点类型:JFFS2 自身定义了三种节点类型,但是考虑到文件系统可扩展性和兼容性,JFFS2从 ext2 借鉴了经验,节点类型的最高两位被用来定义节点的兼容属性,具体来说有下面几种兼容属性:

    JFFS2_FEATURE_INCOMPAT:当 JFFS2 发现了一个不能识别的节点类型,并且它的兼容属性是 JFFS2_FEATURE_INCOMPAT,那么 JFFS2 必须拒绝挂载(mount)文件系统。
    JFFS2_FEATURE_ROCOMPAT:当 JFFS2 发现了一个不能识别的节点类型,并且它的兼容属性是 JFFS2_FEATURE_ROCOMPAT,那么 JFFS2 必须以只读的方式挂载文件系统。
    JFFS2_FEATURE_RWCOMPAT_DELETE:当 JFFS2 发现了一个不能识别的节点类型,并且它的兼容属性是 JFFS2_FEATURE_RWCOMPAT_DELETE,那么在垃圾回收的时候,这个节点可以被删除。

    JFFS2_FEATURE_RWCOMPAT_COPY:当 JFFS2 发现了一个不能识别的节点类型,并且它的兼容属性是 JFFS2_FEATURE_RWCOMPAT_COPY,那么在垃圾回收的时候,这个节点要被拷贝到新的位置。

  • 节点总长度:包括节点头和数据的长度。

  • 节点头部 CRC 校验:包含节点头部的校验码,为文件系统的可靠性提供了支持。

节点类型

JFFS2 定义了三种节点类型:

JFFS2_NODETYPE_INODE: INODE 节点包含了i-节点的原数据(i节点号,文件的组 ID, 属主 id, 访问时间,偏移,长度等),文件数据被附在 INODE 节点之后。除此之外,每个 INODE 节点还有一个版本号,它被用来维护属于一个i-节点的所有 INODE 节点的全序关系。下面举例来说明这个全序关系在 JFFS2 的使用:

http://www.ibm.com/developerworks/cn/linux/l-jffs2/images/image003.gif

因此,当文件系统从闪存上读节点信息后,会生成下面的映射信息:

http://www.ibm.com/developerworks/cn/linux/l-jffs2/images/image004.gif

根据这个映射信息表,文件系统就知道到相应的 INODE 节点去读取相应的文件内容。最后要说明的是,JFFS2 支持文件数据的压缩存储,因此在 INODE 节点中还包含了所使用的压缩算法,在读取数据的时候选择相应的压缩算法来解压缩。 JFFS2_NODETYPE_DIRENT:DIRENT 节点就是把文件名与 i 节点对应起来。在 DIRENT节点中也有一个版本号,这个版本号的作用主要是用来删除一个 dentry。具体来说,当我们要从一个目录中删除一个 dentry 时,我们要写一个 DIRENT 节点,节点中的文件名与被删除的 dentry 中的文件名相同,i 节点号置为 0,同时设置一个更高的版本号。 JFFS2_NODETYPE_CLEANMARKER:当一个擦写块被擦写完毕后,CLEANMARKER 节点会被写在 NOR flash 的开头,或 NAND flash 的 OOB(Out-Of-Band) 区域来表明这是一个干净,可写的擦写块。在 JFFS v1 中,如果扫描到开头的 1K 都是 0xFF 就认为这个擦写块是干净的。但是在实际的测试中发现,如果在擦写的过程中突然掉电,擦写块上也可能会有大块连续 0xFF,但是这并不表明这个擦写块是干净的。于是我们需要 CLEANMARKER 节点来确切的标识一个干净的擦写块。

JFFS2节点,擦写块在内存中的表示和操作

JFFS2 维护了几个链表来管理擦写块,根据擦写块上的内容,一个擦写块会在不同的链表上。具体来说,当一个擦写块上都是合法(valid)的节点时,它会在 clean_list 上;当一个擦写块包含至少一个过时(obsolete)的节点时,它会在 dirty_list 上;当一个擦写块被擦写完毕,并被写入 CLEANMARKER 节点后,它会在 free_list 上。

通常情况下,JFFS2 顺序的在擦写块上写入不同的节点,直到一个擦写块被写满。此时 JFFS2 从 free_list 上取下一个擦写块,继续从擦写块的开头开始写入节点。当 free_list 上擦写块的数量逐渐减少到一个预先设定的阀值的时候,垃圾回收就被触发了,为文件系统清理出更多的可用擦写块。为了减少对内存的占用,JFFS2 并没有把 i 节点所有的信息都保留在内存中,而只是把那些在请求到来时不能很快获得的信息保留在内存中。具体来说,对于在闪存上的每个 i 节点,在内存里都有一个 struct jffs2_inode_cache 与之对应,这个结构里保存了 i 节点号,指向 i 节点的连接数,以及一个指向属于这个 i 节点的物理节点链表的指针。所有的 struct jffs2_inode_cache 存储在一个哈希表中。闪存上的每个节点在内存中由一个 struct jffs2_raw_node_ref 表示,这个结构里保存了此节点的物理偏移,总长度,以及两个指向 struct jffs2_raw_node_ref 的指针。一个指针指向此节点在物理擦写块上的下一个节点,另一个指针指向属于同一个 i-节点的物理节点链表的下一个节点。

http://www.ibm.com/developerworks/cn/linux/l-jffs2/images/image006.gif

在闪存上的节点的起始偏移都是 4 字节对齐的,所以 struct jffs2_inode_cache 中flash_offset 的最低两位没有被用到。JFFS2 正好利用最低位作为此节点是否过时的标记。

下面举一例来说明 JFFS2 是如何使用这些数据结构的。VFS 调用 iget() 来得到一个 i 节点的信息,当这个 i 节点不在缓存中的时候,VFS 就会调用 JFFS2 的 read_inode() 回调函数来得到 i 节点信息。传给 read_inode() 的参数是 i 节点号,JFFS2 用这个 i 节点号从哈希表中查找相应的 struct jffs2_inode_cache,然后利用属于这个 i 节点的节点链表从闪存上读入节点信息,建立类似于表三的映射信息。

JFFS2 挂载过程

JFFS2 的挂载过程分为四个阶段:

  1. JFFS2 扫描闪存介质,检查每个节点 CRC 校验码的合法性,同时分配了 struct jffs2_inode_cache 和 struct jffs2_raw_node_ref
  2. 扫描每个 i 节点的物理节点链表,标识出过时的物理节点;对每一个合法的 dentry 节点,将相应的 jffs2_inode_cache 中的 nlink 加一。
  3. 找出 nlink 为 0 的 jffs2_inode_cache,释放相应的节点。
  4. 释放在扫描过程中使用的临时信息。
JFFS2 垃圾回收机制

当 free_list 上的擦写块数太少了,垃圾回收就会被触发。垃圾回收主要的任务就是回收那些已经过时的节点,但是除此之外它还要考虑磨损平衡的问题。因为如果一味的从 dirty_list上选取擦写块进行垃圾回收,那么 dirty_list 上的擦写块将先于 clean_list 上的擦写块被磨损坏。JFFS2 的处理方式是以 99% 的概率从 dirty_list,1% 的概率从 clean_list 上取一个擦写块下来。由此可以看出 JFFS2 的设计思想是偏向于性能,同时兼顾磨损平衡。对这个块上每一个没有过时的节点执行相同的操作:

  1. 找出这个节点所属的 i 节点号(见图五)。
  2. 调用 iget(),建立这个 i 节点的文件映射表。
  3. 找出这个节点上没有过时的数据内容,并且如果合法的数据太少,JFFS2 还会合并相邻的节点。
  4. 将数据读入倒缓存里,然后将它拷贝到新的擦写块上。
  5. 将回收的节点置为过时。

当擦写块上所有的节点都被置为过时,就可以擦写这个擦写块,回收使用它。

JFFS2 的不足之处

挂载时间过长

JFFS2 的挂载过程需要对闪存从头到尾的扫描,这个过程是很慢的,我们在测试中发现,挂载一个 16M 的闪存有时需要半分钟以上的时间。

磨损平衡的随意性(random nature)

JFFS2 对磨损平衡是用概率的方法来解决的,这很难保证磨损平衡的确定性。在某些情况下,可能造成对擦写块不必要的擦写操作;在某些情况下,又会引起对磨损平衡调整的不及时。

很差的扩展性

JFFS2 中有两个地方的处理是 O(N) 的,这使得它的扩展性很差。

首先,挂载时间同闪存的大小,闪存上节点数目成正比。

其次,虽然 JFFS2 尽可能的减少内存的占用,但通过上面对 JFFS2 的介绍我们可以知道实际上它对内存的占用量是同 i 节点数和闪存上的节点数成正比的。

因此在实际应用中,JFFS2 最大能用在 128M 的闪存上。

JFFS2 的新特性

最近加入到 JFFS2 中的两个补丁程序分别解决了上面提到的挂载时间过长和磨损平衡随意性的问题。

磨损块小结补丁程序(erase block summary patch)

这个补丁程序最基本的思想就是用空间来换时间。具体来说,就是将每个擦写块每个节点的原数据信息写在这个擦写块的最后,当 JFFS2 挂载的时候,对每个擦写块只需要读一次来读取这个小结节点,因此大大减少了挂载时间。使用了磨损块小结补丁程序,一个擦写块的结构就像下面这样:

http://www.ibm.com/developerworks/cn/linux/l-jffs2/images/image007.gif

根据我们的测试,使用磨损块小结补丁程序,挂载一个 12M 的闪存需要 2~3 秒,挂载一个 16M 的闪存需要 3~4 秒。

改进的磨损平衡补丁程序

这个补丁程序的基本思想是,记录每个擦写块的擦写次数,当闪存上各个擦写块的擦写次数的差距超过某个预定的阀值,开始进行磨损平衡的调整。调整的策略是,在垃圾回收时将擦写次数小的擦写块上的数据迁移到擦写次数大的擦写块上。这样一来我们提高了磨损平衡的确定性,我们可以知道什么时候开始磨损平衡的调整,也可以知道选取哪些擦写块进行磨损平衡的调整。

擦写块头部补丁程序

在写改进的磨损平衡补丁程序的过程之中,我们需要记录每个擦写块的擦写次数,这个信息需要记录在各自的擦写块上。可是我们发现 JFFS2 中缺少一种灵活的对每个擦写块的信息进行扩展的机制。于是我们为每个擦写块引入了擦写块头部(header),这个头部负责纪录每个擦写块的信息(比如说擦写次数),并且它提供了灵活的扩展机制,将来如果有新的信息需要记录,可以很容易的加入到头部之中。

JFFS3 简介

虽然不断有新的补丁程序来提高 JFFS2 的性能,但是不可扩展性是它最大的问题,但是这是它自身设计的先天缺陷,是没有办法靠后天来弥补的。因此我们需要一个全新的文件系统,而 JFFS3 就是这样的一个文件系统,JFFS3 的设计目标是支持大容量闪存(>1TB)的文件系统。JFFS3 与 JFFS2 在设计上根本的区别在于,JFFS3 将索引信息存放在闪存上,而 JFFS2将索引信息保存在内存中。比如说,由给定的文件内的偏移定位到存储介质上的物理偏移地址所需的信息,查找某个目录下所有的目录项所需的信息都是索引信息的一种。 JFFS3 现在还处于设计阶段,文件系统的基本结构借鉴了 Reiser4 的设计思想,整个文件系统就是一个 B+ 树。JFFS3 的发起者正工作于垃圾回收机制的设计,这是 JFFS3 中最复杂,也是最富有挑战性的部分。JFFS3 的设计文档可以在http://www.linux-mtd.infradead.org/doc/jffs3.html 得到,有兴趣的读者可以积极参与到 JFFS3 的设计中,发表自己的见解,参与讨论。

uboot 对SPI flash 的识别

flash信息在

drivers/mtd/spi/spi_flash_ids.c
 const struct spi_flash_info spi_flash_ids[] = {
         {"w25p80",         INFO(0xef2014, 0x0,  64 * 1024,    16, 0) },
         {"w25p16",         INFO(0xef2015, 0x0,  64 * 1024,    32, 0) },
         {"w25p32",         INFO(0xef2016, 0x0,  64 * 1024,    64, 0) },
         {"w25x40",         INFO(0xef3013, 0x0,  64 * 1024,     8, SECT_4K) },
         {"w25x16",         INFO(0xef3015, 0x0,  64 * 1024,    32, SECT_4K) },
         {"w25x32",         INFO(0xef3016, 0x0,  64 * 1024,    64, SECT_4K) },
         {"w25x64",         INFO(0xef3017, 0x0,  64 * 1024,   128, SECT_4K) },
         {"w25q80bl",       INFO(0xef4014, 0x0,  64 * 1024,    16, RD_FULL | WR_QPP | SECT_4K) },
         {"w25q16cl",       INFO(0xef4015, 0x0,  64 * 1024,    32, RD_FULL | WR_QPP | SECT_4K) },
         {"w25q32bv",       INFO(0xef4016, 0x0,  64 * 1024,    64, RD_FULL | WR_QPP | SECT_4K) },
         {"w25q64cv",       INFO(0xef4017, 0x0,  64 * 1024,   128, RD_FULL | WR_QPP | SECT_4K) },
         {"w25q128bv",      INFO(0xef4018, 0x0,  64 * 1024,   256, RD_FULL | WR_QPP | SECT_4K) },
         {"w25q256",        INFO(0xef4019, 0x0,  64 * 1024,   512, RD_FULL | WR_QPP | SECT_4K) },
         {"w25q80bw",       INFO(0xef5014, 0x0,  64 * 1024,    16, RD_FULL | WR_QPP | SECT_4K) },
         {"w25q16dw",       INFO(0xef6015, 0x0,  64 * 1024,    32, RD_FULL | WR_QPP | SECT_4K) },
         {"w25q32dw",       INFO(0xef6016, 0x0,  64 * 1024,    64, RD_FULL | WR_QPP | SECT_4K) },
         {"w25q64dw",       INFO(0xef6017, 0x0,  64 * 1024,   128, RD_FULL | WR_QPP | SECT_4K) },
         {"w25q128fw",      INFO(0xef6018, 0x0,  64 * 1024,   256, RD_FULL | WR_QPP | SECT_4K) },


 #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)      \
                 .id = {                                                 \
                         ((_jedec_id) >> 16) & 0xff,                     \
                         ((_jedec_id) >> 8) & 0xff,                      \
                         (_jedec_id) & 0xff,                             \
                         ((_ext_id) >> 8) & 0xff,                        \
                         (_ext_id) & 0xff,                               \
                         },                                              \
                 .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),       \
                 .sector_size = (_sector_size),                          \
                 .n_sectors = (_n_sectors),                              \
                 .page_size = 256,                                       \
                 .flags = (_flags),


 struct spi_flash_info {
         /* Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO]) */
         const char      *name;

         /*
         * This array stores the ID bytes.
         * The first three bytes are the JEDIC ID.
         * JEDEC ID zero means "no ID" (mostly older chips).
         */
         u8              id[SPI_FLASH_MAX_ID_LEN];
         u8              id_len;

         /*
         * The size listed here is what works with SPINOR_OP_SE, which isn't
         * necessarily called a "sector" by the vendor.
         */
         u32             sector_size;
         u32             n_sectors;

         u16             page_size;

         u16             flags;


 #define SECT_4K                 BIT(0)  /* CMD_ERASE_4K works uniformly */
 #define E_FSR                   BIT(1)  /* use flag status register for */
 #define SST_WR                  BIT(2)  /* use SST byte/word programming */
 #define WR_QPP                  BIT(3)  /* use Quad Page Program */
 #define RD_QUAD                 BIT(4)  /* use Quad Read */
 #define RD_DUAL                 BIT(5)  /* use Dual Read */
 #define RD_QUADIO               BIT(6)  /* use Quad IO Read */
 #define RD_DUALIO               BIT(7)  /* use Dual IO Read */
 #define RD_FULL                 (RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO)
 };

关于系统reboot

在使用spi flash时,执行reboot命令,有时会无法重启,这里追查下原因。

正常重启信息

# reboot
# Stopping network: OK
Saving random seed... done.
Stopping logging: OK
umount: devtmpfs busy - remounted read-only
[   16.812893] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)
Sent SIGTERM to all processes
Sent SIGKILL to all processes
Requesting system reboot
[   18.830716] reboot: Restarting system
kernel/reboot.c:
 void kernel_restart(char *cmd)
 {
         kernel_restart_prepare(cmd);
         migrate_to_reboot_cpu();
         syscore_shutdown();
         if (!cmd)
                 pr_emerg("Restarting system\n");
         else
                 pr_emerg("Restarting system with command '%s'\n", cmd);
         kmsg_dump(KMSG_DUMP_RESTART);
         machine_restart(cmd);
 }

arch/arm/kernel/setup.c: arm_pm_restart = mdesc->restart;

重启失败

arch/arm/kernel/reboot.c
 void machine_restart(char *cmd)
 {
         local_irq_disable();
         smp_send_stop();

         if (arm_pm_restart)
                 arm_pm_restart(reboot_mode, cmd);
         else
                 do_kernel_restart(cmd);
         //正常来说不会走到这里
         /* Give a grace period for failure to restart of 1s */
         mdelay(1000);

         /* Whoops - the platform was unable to reboot. Tell the user! */
         printk("Reboot failed -- System halted\n");
         while (1);
 }
kernel/reboot.c
 void do_kernel_restart(char *cmd)
 {
         atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);
 }
 register_restart_handler
kernel/notifier.c
 int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
                 unsigned long val, void *v)
 {
     return __atomic_notifier_call_chain(nh, val, v, -1, NULL);
 }

spi flash问题

[  312.719945] INFO: trying to register non-static key.
[  312.724967] the code is fine but needs lockdep annotation.
[  312.730448] turning off the locking correctness validator.
[  312.735943] CPU: 0 PID: 162 Comm: sync Not tainted 4.13.0-licheepi-zero+ #55
[  312.742981] Hardware name: Allwinner sun8i Family
[  312.747734] [<c010e8a8>] (unwind_backtrace) from [<c010b594>] (show_stack+0x10/0x14)
[  312.755483] [<c010b594>] (show_stack) from [<c048ec4c>] (dump_stack+0x84/0x98)
[  312.762711] [<c048ec4c>] (dump_stack) from [<c015e698>] (register_lock_class+0x3f8/0x624)
[  312.770886] [<c015e698>] (register_lock_class) from [<c015fb0c>] (__lock_acquire.constprop.7+0x60/0x954)
[  312.780358] [<c015fb0c>] (__lock_acquire.constprop.7) from [<c0160468>] (lock_acquire+0x68/0x84)
[  312.789143] [<c0160468>] (lock_acquire) from [<c0132498>] (flush_work+0x50/0x290)
[  312.796624] [<c0132498>] (flush_work) from [<c0133f00>] (__cancel_work_timer+0xec/0x1c4)
[  312.804722] [<c0133f00>] (__cancel_work_timer) from [<c028d1b4>] (jffs2_sync_fs+0x14/0x38)
[  312.812995] [<c028d1b4>] (jffs2_sync_fs) from [<c0207e30>] (iterate_supers+0xc0/0x120)
[  312.820912] [<c0207e30>] (iterate_supers) from [<c0233708>] (sys_sync+0x44/0xa4)
[  312.828310] [<c0233708>] (sys_sync) from [<c0107620>] (ret_fast_syscall+0x0/0x3c)
fs/jffs2/super.c
 static int jffs2_sync_fs(struct super_block *sb, int wait)
 {
         struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);

 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
         cancel_delayed_work_sync(&c->wbuf_dwork);
 #endif

         mutex_lock(&c->alloc_sem);
         jffs2_flush_wbuf_pad(c);
         mutex_unlock(&c->alloc_sem);
         return 0;
 }

 bool cancel_delayed_work_sync(struct delayed_work *dwork)
 {
     return __cancel_work_timer(&dwork->work, true);
 }
 EXPORT_SYMBOL(cancel_delayed_work_sync);

CONFIG_JFFS2_FS_WRITEBUFFER 去掉,可以不出现oops信息

原因

是使用了32M flash,在重启的时候,没有退出4-byte地址模式导致。(因为板子上没有PMU,没有对flash进行复位)

static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
                                    const struct flash_info *info)
{
        /* Do some manufacturer fixups first */
        switch (JEDEC_MFR(info)) {
        case SNOR_MFR_SPANSION:
                /* No small sector erase for 4-byte command set */
                nor->erase_opcode = SPINOR_OP_SE;
                nor->mtd.erasesize = info->sector_size;
                break;

        default:
                break;
        }

        nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
        nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
        nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
}

/* Enable/disable 4-byte addressing mode. */
static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
                            int enable)
{
        int status;
        bool need_wren = false;
        u8 cmd;

        switch (JEDEC_MFR(info)) {
        case SNOR_MFR_MICRON:
                /* Some Micron need WREN command; all will accept it */
                need_wren = true;
        case SNOR_MFR_MACRONIX:
        case SNOR_MFR_WINBOND:
                if (need_wren)
                        write_enable(nor);   //nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);

                cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
                status = nor->write_reg(nor, cmd, NULL, 0);
                if (need_wren)
                        write_disable(nor);

                return status;
        default:
                /* Spansion style */
                nor->cmd_buf[0] = enable << 7;
                return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
        }
}
struct m25p {
        struct spi_device       *spi;
        struct spi_nor          spi_nor;
        u8                      command[MAX_CMD_SIZE];
};
static int m25p_remove(struct spi_device *spi)
{
        struct m25p     *flash = spi_get_drvdata(spi);
//add to exit 4-byte address mode

        /* Clean up MTD stuff. */
        return mtd_device_unregister(&flash->spi_nor.mtd);
}

新增关机接口

static void m25p_shutdown(struct spi_device *spi)
{
        struct m25p     *flash = spi_get_drvdata(spi);
        struct spi_nor nor = flash->spi_nor;
int status;
//add to exit 4-byte address mode
nor.write_reg(&nor, SPINOR_OP_WREN, NULL, 0);
status = nor.write_reg(&nor, SPINOR_OP_EX4B, NULL, 0);
printk("remove spi flash!\n");
        /* Clean up MTD stuff. */
        mtd_device_unregister(&flash->spi_nor.mtd);
        return;
}
static struct spi_driver m25p80_driver = {
        .driver = {
                .name   = "m25p80",
                .of_match_table = m25p_of_table,
        },
        .id_table       = m25p_ids,
        .probe  = m25p_probe,
        .remove = m25p_remove,
        .shutdown = m25p_shutdown,
        /* REVISIT: many of these chips have deep power-down modes, which
        * should clearly be entered on suspend() to minimize power use.
        * And also when they're otherwise idle...
        */
};

CONFIG_SPI_FLASH_BAR

Zero系统烧录

荔枝派Zero的系统烧录主要分以下几种:

  1. dd镜像烧录
  2. 分区镜像烧录
  3. SPI Flash系统烧录
  4. 另为方便广大用户使用,有Win32DiskImage一键烧录方式

Zero能够烧录的系统目前有Linux,Camdroid,Openwrt等,甚至能够运行去掉图形界面的低版本安卓,今后随着广大荔枝派爱好者开发,将会有更多可供烧录的系统面世。

如果您对开发或移植荔枝派Zero系统感兴趣,欢迎加入荔枝派DIY交流群:573832310

dd镜像烧录

简介

在windows环境下可以方便的运用Win32DiskImage等工具烧录,而Linux环境下,用dd命令烧录不妨是个好选择,今天,本篇介绍dd烧录的操作步骤以及需注意的细节

两面性:

优点:对于喜欢此方式烧录镜像的人来说方便快捷

缺点:要敲命令行(对于初学者来说也是个学习了解类Linux环境的机会)

材料准备:
内存卡+读卡器+荔枝派Zero+支持完整指令集的x86设备一台(电脑)
或:
U盘+荔枝派Zero+支持完整指令集的x86设备一台(电脑)

推荐待烧录存储器大小:8GB及以上

推荐镜像下载位址:
hack to 环荣内网 then //192.168.1.89/musume/Lichee/ubuntu0.5.1-Lichee-20170811G.rar

dd烧录步骤

查看盘符路径先
首先,打开Linux的终端机界面(也就是命令行)
插入一个大小合适的U盘或内存卡与读卡器的组合(荔枝派Zero较适于内存卡)
随便敲一个diskutil list 命令 便会有一堆文字列出
可以看到u盘的盘符是 /dev/disknani
取消系统对于该盘符的挂载

照顾到大家电脑系统环境的不同,以下两组代码中至少有一组能够正常显示,请直接观看您电脑能正常显示的那组

:: diskutil umountDisk /dev/disk2

Shell

1 2 3 arefly:~ arefly$ diskutil unmountDisk [内存卡位置] Unmount of all volumes on [内存卡位置] was successful arefly:~ arefly$ arefly:~ arefly$ diskutil unmountDisk [内存卡位置] Unmount of all volumes on [内存卡位置] was successful arefly:~ arefly$

写入镜像

sudo dd if=源路径 of=/dev/r卷标 bs=1m [‘r’ 会让命令执行加快一点] [‘bs’为一次填充的容量]

也就是dd命令

弹出内存卡

下电拔卡

diskutil eject [内存卡位置]

然后热插拔

完成前的事项

将内存卡装载到荔枝派Zero,上电运行,您就可开启您的荔枝派Linux之途

分区镜像烧录

简介:

一步步来的镜像烧录,适合调节各个部位的代码,方便一些特别的底层运作方式

推荐镜像下载位址:hack to 环荣内网 then //192.168.1.89/musume/Lichee/all-2017/all.rar

此镜像中的文件介绍:
(当时在外出差刚好缺个路由器,于是就做了个路由器镜像)
Lichee_main_gpt.bin
主GPT头和分区表
Lichee_Zero_ac.bin
仿基带,就是区分设备用的
eazyboot.mbn
类似BIOS的界面,其实就是个设置菜单,有个看看用的磁盘检测功能,可以观看进度条在跑,并不检测磁盘,不会衰减磁盘使用寿命
bootlist.mbn
真正的系统启动选单
Misc.img
驱动跟一堆底层的东西
splash.img
里面就一张开机图片,直接将原版的 drivers/video/logo/logo_linux_clut224.ppm 移植过来的,可以任意修改
system.img
路由器系统,需配合Zero底板与TF-Wifi-Card,LCD小屏使用
recovery.img
仅恢复cache与Set,无其他功能,跟戳路由器复位效果差不多
userdata.img

初账号:Lichee Zero

密码:88888888

Web.img
就一个荔枝派Zero的欢迎页面,单独放着方便大家修改成自己喜欢的页面
步骤:
见云汉荔枝派板块系统名称帖子

SPI Flash系统烧录

步骤概要:

  1. 打开SPI烧录软件
  2. 读取现有目录
  3. 擦除区域内容
  4. 下载固件
  5. 选择对应的固件与烧写位址
  6. 等待烧好后上机使用

注意事项:如果您是将芯片吹出来装到编程器里烧写的,请注意芯片左上角圆点方向

一键镜像烧录指南

简介:最方便的镜像烧录方法,类似于ghost一键装机

两面性:

优点:装机速度快,无需任何专业知识,直接安装,方便效验成果

缺点:不适合开发过程,不利于学习

操作步骤:

  1. 打开SD卡格式化工具

    SD Format或SDA 插入内存卡

    选调整分区大小 ON

    注意盘符的选择

    然后点击格式化

    或用DiskGenius.exe削除一下分区,然后建回,再格式化一下

  2. 打开Win32DiskImage

    直接载入镜像

    注意盘符的选择

    (如果有卡启动选项,请选择它,没改过的这软件应该没这个选项)

    点击烧录

    待烧录完成

  3. 插卡

    上电

    启动

    至此,一键烧录完成!

恭喜!您已成功安装荔枝派Zero系统!

Zero Imager的使用

Zero imager是zero固件打包,烧录 工具集,使用zero imager可以方便地进行各种配置(主线,bsp;tf,spi)的镜像打包,烧录,修改等。

zero imager 目录

zepan@ubuntu:~/develop/zero_imager$ ls
build  dtb  fex  img kernel  modules  overlay  README  rootfs  uboot
  • build: 内有各种脚本,打包烧录等操作均需要进入该目录操作
  • uboot:sdk生成的uboot镜像
  • dtb:主线内核使用的dtb,会从sdk中拷贝到这里
  • fex:bsp内核使用的fex,会从sdk里拷贝到这里
  • img:打包完成的镜像
  • kernel:sdk生成的zImage,按内核版本分类摆放
  • modules:sdk生成的内核模块
  • rootfs:各种预制的根文件系统,如rootfs-brmin.tar.gz
  • overlay:在预制根文件系统上添加的内容

zero imager 板级配置

在打包系统前,需要填写板级配置并生效。

全局环境变量

首先进入 build目录,编辑env.sh文件,填写正确的路径信息,这里需要编辑的是:

# need edit as your env
export _TOP_DIR=/home/zepan/develop/zero_imager
export _KERNEL_MAINDIR="goofy_elion:/root/linux/"
export _KERNEL_BSPDIR="goofy_elion:/root/lichee/linux-3.4/"
export _UBOOT_DEVDIR="goofy_elion:/root/u-boot"
export _BR_DEVDIR="goofy_elion:/root/buildroot-2017.08"
export _CP_CMD="docker cp"
¥_LINUX_MAINDIR是你的主线linux sdk目录
¥_LINUX_BSPDIR是你的bsp inux sdk目录
¥_UBOOT_DEVDIR是你的uboot sdk目录
¥_CP_CMD是你的cp命令,如果sdk在本机上,就直接是cp,如果是在远程请自行填写cp命令

板级环境变量

然后进入configs目录,编辑或者新建你的板级配置文件,这里以 env-dock-tfmain.sh 为例介绍下。

板级环境变量配置文件名,命名为 " env-板子名-启动介质名 内核种类.sh "

其中的内容,其余部分可以拷贝,仅修改下面部分:

#change here to your config
export _CASE_NAME=dock
export _BOOT_DEV=tf
export _KERNEL_TYPE=main
export _KERNEL_VER=4.14.14
export _DT_NAME=sun8i-v3s-licheepi-zero-dock
#export _DT_NAME=sun8i-v3s-licheepi-zero-with-800x480-lcd
export _ROOTFS_TYPE=brmin
export _IMG_SIZE=128
export _UBOOT_SIZE=1
export _CFG_SIZEKB=0
export _P1_SIZE=4

这里主要就是填写你的板子使用的镜像的配置信息:

启动介质是tf或者spi, 内核种类是main或者bsp, 内核版本按实际填写, DT即设备树配置名, 根文件系统类型即rootfs目录下的名字后缀。 设定的镜像大小,以MB为单位,下同。 UBOOT分区大小。 SPI 启动时使用的FEX/DTB分区大小(单位KB)。 第一分区大小(tf启动时放置内核和dtb等,spi启动时放置内核)。

注意这里会自动生成板子配置后缀名:

export _SUFFIX=$_CASE_NAME-$_BOOT_DEV$_KERNEL_TYPE

如 dock-tfmain

生效板级配置

在build目录下执行 source configs/env-dock-tfmain.sh 即可生效对应配置。

执行后会在build目录下生成 boot.scr 启动脚本。

每次打开新终端 请先执行一遍该命令来导入配置。

镜像更新,打包命令

pull_uboot.sh:从sdk里更新uboot
pull_kernel.sh: 从sdk里更新zImage和dts和modules
pull_br.sh: 从sdk更新buildroot生成的根文件系统
pack_img.sh: 打包img到dd文件,生成文件在img目录下

局部更新镜像内容,可以在overlay下对应目录拷贝需要的文件,打包镜像时会将文件写入根文件系统的对应目录。

TF镜像烧录命令

write_dd.sh /dev/sdX: 一键烧录dd镜像,小白专用
//以下为调试时逐个分区调试使用的烧录脚本
write_all.sh /dev/sdX: 一键烧录
write_partion.sh /dev/sdX: 对tf卡分区
write_mkfs.sh /dev/sdX: tf卡格式化
write_boot.sh /dev/sdX: 烧录uboot
write_p1.sh /dev/sdX: 烧录第一分区
write_p2.sh /dev/sdX: 烧录第二分区
write_overlay.sh /dev/sdX: 烧录overlay
write_swap.sh /dev/sdX: 启用swap

设备树简介

Device Tree是一种描述硬件的数据结构,

DTS(Device Tree Source)就是用来描述目标板硬件信息的源文件。

设备树基本数据格式

device tree是一个简单的节点和属性树,属性是键值对,节点可以包含属性和子节点。下面是一个.dts格式的简单设备树。

/ {
    node1 {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        a-byte-data-property = [0x01 0x23 0x34 0x56];
        child-node1 {
            first-child-property;
            second-child-property = <1>;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
    node2 {
        an-empty-property;
        a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
        child-node1 {
        };
    };
};

编译设备树后,可以使用

dtc -I dtb -O dts xxx.dtb -o xxx.dts

来查看实际生成的设备树文件

在运行系统时,/sys/firmware/devicetree 可以查看实际使用的是设备树

设备树常用操作

删除属性
 / {
     ... ...
     demo1: demo1 {
         compatible = "demo1";
         property1 = <1>;
         property2;
         property3 = <2>;
         property4;
     };
 };

 &demo1 {
     /delete-property/property2;
     /delete-property/property3;
 };
删除节点
 memory_DDR1@c0000000 {
         device_type = "memory";
         reg = <0 0xc0000000 0 0x40000000>;
     };


 / {
     /delete-node/ memory_DDR1@c0000000;
 };

设备树实例解析

下面解析sun8i-v3s.dtsi设备树实例

#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/sun8i-v3s-ccu.h>
#include <dt-bindings/reset/sun8i-v3s-ccu.h>

/ {
    #address-cells = <1>;           //表示子节点默认用一个uint32表示地址(32位系统),如果是64位系统将是2
    #size-cells = <1>;              //表示子节点默认用一个uint32表示大小
    interrupt-parent = <&gic>;      //interrupt-parent是独立字段,表示整个设备的中断父节点是gic
    //<&gic>表示取gic地址。gic配置详见文件末尾

    chosen {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        simplefb_lcd: framebuffer@0 {
            compatible = "allwinner,simple-framebuffer",
                    "simple-framebuffer";
            allwinner,pipeline = "de0-lcd0";
            clocks = <&ccu CLK_BUS_TCON0>, <&display_clocks 0>,
                <&display_clocks 6>, <&ccu CLK_TCON0>;
            status = "disabled";
        };
    };

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;

        cpu@0 {
            compatible = "arm,cortex-a7";
            device_type = "cpu";
            reg = <0>;
            clocks = <&ccu CLK_CPU>;
        };
    };

    de: display-engine {
        compatible = "allwinner,sun8i-v3s-display-engine";
        allwinner,pipelines = <&mixer0>;
        status = "disabled";
    };

    timer {
        compatible = "arm,armv7-timer";
        interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
                <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
                <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
                <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
    };

    clocks {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        osc24M: osc24M_clk {
            #clock-cells = <0>;
            compatible = "fixed-clock";
            clock-frequency = <24000000>;
            clock-output-names = "osc24M";
        };

        osc32k: osc32k_clk {
            #clock-cells = <0>;
            compatible = "fixed-clock";
            clock-frequency = <32768>;
            clock-output-names = "osc32k";
        };
    };

    soc {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        display_clocks: clock@1000000 {
            compatible = "allwinner,sun8i-v3s-de2-clk";
            reg = <0x01000000 0x100000>;
            clocks = <&ccu CLK_DE>,
                <&ccu CLK_BUS_DE>;
            clock-names = "mod",
                    "bus";
            resets = <&ccu RST_BUS_DE>;
            #clock-cells = <1>;
            #reset-cells = <1>;
        };

        mixer0: mixer@1100000 {
            compatible = "allwinner,sun8i-v3s-de2-mixer";
            reg = <0x01100000 0x100000>;
            clocks = <&display_clocks 0>,
                <&display_clocks 6>;
            clock-names = "bus",
                    "mod";
            resets = <&display_clocks 0>;
            assigned-clocks = <&display_clocks 6>;
            assigned-clock-rates = <150000000>;

            ports {
                #address-cells = <1>;
                #size-cells = <0>;

                mixer0_out: port@1 {
                    #address-cells = <1>;
                    #size-cells = <0>;
                    reg = <1>;

                    mixer0_out_tcon0: endpoint@0 {
                        reg = <0>;
                        remote-endpoint = <&tcon0_in_mixer0>;
                    };
                };
            };
        };

        tcon0: lcd-controller@1c0c000 {
            compatible = "allwinner,sun8i-v3s-tcon";
            reg = <0x01c0c000 0x1000>;
            interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&ccu CLK_BUS_TCON0>,
                <&ccu CLK_TCON0>;
            clock-names = "ahb",
                    "tcon-ch0";
            clock-output-names = "tcon-pixel-clock";
            resets = <&ccu RST_BUS_TCON0>;
            reset-names = "lcd";
            status = "disabled";

            ports {
                #address-cells = <1>;
                #size-cells = <0>;

                tcon0_in: port@0 {
                    #address-cells = <1>;
                    #size-cells = <0>;
                    reg = <0>;

                    tcon0_in_mixer0: endpoint@0 {
                        reg = <0>;
                        remote-endpoint = <&mixer0_out_tcon0>;
                    };
                };

                tcon0_out: port@1 {
                    #address-cells = <1>;
                    #size-cells = <0>;
                    reg = <1>;
                };
            };
        };


        mmc0: mmc@01c0f000 {
            compatible = "allwinner,sun7i-a20-mmc";
            reg = <0x01c0f000 0x1000>;
            clocks = <&ccu CLK_BUS_MMC0>,
                <&ccu CLK_MMC0>,
                <&ccu CLK_MMC0_OUTPUT>,
                <&ccu CLK_MMC0_SAMPLE>;
            clock-names = "ahb",
                    "mmc",
                    "output",
                    "sample";
            resets = <&ccu RST_BUS_MMC0>;
            reset-names = "ahb";
            interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
            status = "disabled";
            #address-cells = <1>;
            #size-cells = <0>;
        };

        mmc1: mmc@01c10000 {
            compatible = "allwinner,sun7i-a20-mmc";
            reg = <0x01c10000 0x1000>;
            clocks = <&ccu CLK_BUS_MMC1>,
                <&ccu CLK_MMC1>,
                <&ccu CLK_MMC1_OUTPUT>,
                <&ccu CLK_MMC1_SAMPLE>;
            clock-names = "ahb",
                    "mmc",
                    "output",
                    "sample";
            resets = <&ccu RST_BUS_MMC1>;
            reset-names = "ahb";
            interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
            pinctrl-names = "default";
            pinctrl-0 = <&mmc1_pins>;
            status = "disabled";
            #address-cells = <1>;
            #size-cells = <0>;
        };

        mmc2: mmc@01c11000 {
            compatible = "allwinner,sun7i-a20-mmc";
            reg = <0x01c11000 0x1000>;
            clocks = <&ccu CLK_BUS_MMC2>,
                <&ccu CLK_MMC2>,
                <&ccu CLK_MMC2_OUTPUT>,
                <&ccu CLK_MMC2_SAMPLE>;
            clock-names = "ahb",
                    "mmc",
                    "output",
                    "sample";
            resets = <&ccu RST_BUS_MMC2>;
            reset-names = "ahb";
            interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
            status = "disabled";
            #address-cells = <1>;
            #size-cells = <0>;
        };

        usb_otg: usb@01c19000 {
            compatible = "allwinner,sun8i-h3-musb";
            reg = <0x01c19000 0x0400>;
            clocks = <&ccu CLK_BUS_OTG>;
            resets = <&ccu RST_BUS_OTG>;
            interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
            interrupt-names = "mc";
            phys = <&usbphy 0>;
            phy-names = "usb";
            extcon = <&usbphy 0>;
            status = "disabled";
        };

        usbphy: phy@01c19400 {
            compatible = "allwinner,sun8i-v3s-usb-phy";
            reg = <0x01c19400 0x2c>,
                <0x01c1a800 0x4>;
            reg-names = "phy_ctrl",
                    "pmu0";
            clocks = <&ccu CLK_USB_PHY0>;
            clock-names = "usb0_phy";
            resets = <&ccu RST_USB_PHY0>;
            reset-names = "usb0_reset";
            status = "disabled";
            #phy-cells = <1>;
        };

        ehci0: usb@01c1a000 {
            compatible = "allwinner,sun8i-v3s-ehci", "generic-ehci";
            reg = <0x01c1a000 0x100>;
            interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&ccu CLK_BUS_EHCI0>, <&ccu CLK_BUS_OHCI0>;
            resets = <&ccu RST_BUS_EHCI0>, <&ccu RST_BUS_OHCI0>;
            status = "disabled";
        };

        ohci0: usb@01c1a400 {
            compatible = "allwinner,sun8i-v3s-ohci", "generic-ohci";
            reg = <0x01c1a400 0x100>;
            interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&ccu CLK_BUS_EHCI0>, <&ccu CLK_BUS_OHCI0>,
                <&ccu CLK_USB_OHCI0>;
            resets = <&ccu RST_BUS_EHCI0>, <&ccu RST_BUS_OHCI0>;
            status = "disabled";
        };

        ccu: clock@01c20000 {
            compatible = "allwinner,sun8i-v3s-ccu";
            reg = <0x01c20000 0x400>;
            clocks = <&osc24M>, <&osc32k>;
            clock-names = "hosc", "losc";
            #clock-cells = <1>;
            #reset-cells = <1>;
        };

        rtc: rtc@01c20400 {
            compatible = "allwinner,sun6i-a31-rtc";
            reg = <0x01c20400 0x54>;
            interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
                    <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
        };

        pio: pinctrl@01c20800 {
            compatible = "allwinner,sun8i-v3s-pinctrl";
            reg = <0x01c20800 0x400>;
            interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>,
                    <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;
            clock-names = "apb", "hosc", "losc";
            gpio-controller;
            #gpio-cells = <3>;
            interrupt-controller;
            #interrupt-cells = <3>;

            i2c0_pins: i2c0 {
                pins = "PB6", "PB7";
                function = "i2c0";
            };

            uart0_pins_a: uart0@0 {
                pins = "PB8", "PB9";
                function = "uart0";
            };

            mmc0_pins_a: mmc0@0 {
                pins = "PF0", "PF1", "PF2", "PF3",
                    "PF4", "PF5";
                function = "mmc0";
                drive-strength = <30>;
                bias-pull-up;
            };

            mmc1_pins: mmc1 {
                pins = "PG0", "PG1", "PG2", "PG3",
                    "PG4", "PG5";
                function = "mmc1";
                drive-strength = <30>;
                bias-pull-up;
            };

            spi0_pins: spi0 {
                pins = "PC0", "PC1", "PC2", "PC3";
                function = "spi0";
            };
        };

        timer@01c20c00 {
            compatible = "allwinner,sun4i-a10-timer";
            reg = <0x01c20c00 0xa0>;
            interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
                    <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&osc24M>;
        };

        wdt0: watchdog@01c20ca0 {
            compatible = "allwinner,sun6i-a31-wdt";
            reg = <0x01c20ca0 0x20>;
            interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
        };

        lradc: lradc@1c22800 {
            compatible = "allwinner,sun4i-a10-lradc-keys";
            reg = <0x01c22800 0x400>;
            interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
            status = "disabled";
        };

        uart0: serial@01c28000 {
            compatible = "snps,dw-apb-uart";
            reg = <0x01c28000 0x400>;
            interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
            reg-shift = <2>;
            reg-io-width = <4>;
            clocks = <&ccu CLK_BUS_UART0>;
            resets = <&ccu RST_BUS_UART0>;
            status = "disabled";
        };

        uart1: serial@01c28400 {
            compatible = "snps,dw-apb-uart";
            reg = <0x01c28400 0x400>;
            interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
            reg-shift = <2>;
            reg-io-width = <4>;
            clocks = <&ccu CLK_BUS_UART1>;
            resets = <&ccu RST_BUS_UART1>;
            status = "disabled";
        };

        uart2: serial@01c28800 {
            compatible = "snps,dw-apb-uart";
            reg = <0x01c28800 0x400>;
            interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
            reg-shift = <2>;
            reg-io-width = <4>;
            clocks = <&ccu CLK_BUS_UART2>;
            resets = <&ccu RST_BUS_UART2>;
            status = "disabled";
        };

        i2c0: i2c@01c2ac00 {
            compatible = "allwinner,sun6i-a31-i2c";
            reg = <0x01c2ac00 0x400>;
            interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&ccu CLK_BUS_I2C0>;
            resets = <&ccu RST_BUS_I2C0>;
            pinctrl-names = "default";
            pinctrl-0 = <&i2c0_pins>;
            status = "disabled";
            #address-cells = <1>;
            #size-cells = <0>;
        };

        i2c1: i2c@01c2b000 {
            compatible = "allwinner,sun6i-a31-i2c";
            reg = <0x01c2b000 0x400>;
            interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&ccu CLK_BUS_I2C1>;
            resets = <&ccu RST_BUS_I2C1>;
            status = "disabled";
            #address-cells = <1>;
            #size-cells = <0>;
        };

        spi0: spi@1c68000 {
            compatible = "allwinner,sun8i-h3-spi";
            reg = <0x01c68000 0x1000>;
            interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>;
            clock-names = "ahb", "mod";
            pinctrl-names = "default";
            pinctrl-0 = <&spi0_pins>;
            resets = <&ccu RST_BUS_SPI0>;
            status = "disabled";
            #address-cells = <1>;
            #size-cells = <0>;
        };

        gic: interrupt-controller@01c81000 {        // interrupt-controller是独立字段,表示中断控制器。gic是其一个实例,地址在01c81000
            compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic"; //驱动匹配
            reg = <0x01c81000 0x1000>,      //中断控制器的寄存器地址,和大小
                <0x01c82000 0x1000>,
                <0x01c84000 0x2000>,
                <0x01c86000 0x2000>;
            interrupt-controller;
            #interrupt-cells = <3>; //调用该实例需要三个参数
            interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
        };
    };
};

系统中的每个设备由device tree的一个节点来表示;

驱动代码
 static struct of_device_id beep_table[] = {
     {.compatible = "fs4412,beep"},
 };
 static struct platform_driver beep_driver=
 {
     .probe = beep_probe,
     .remove = beep_remove,
     .driver={
         .name = "bigbang",
         .of_match_table = beep_table,
     },
 };
设备树代码
 fs4412-beep{
         compatible = "fs4412,beep";
         reg = <0x114000a0 0x4 0x139D0000 0x14>;
 };
compatible,关键属性,驱动中使用of_match_table,即of_device_id列表,其中就使用compatible字段来匹配设备。
简单地说就是,内核启动后会把设备树加载到树状结构体中,当insmod的时候,就会在树中查找匹配的设备节点来加载。

reg,描述寄存器基址和长度,可以有多个。

pwm: pwm@01c21400 {
            compatible = "allwinner,sun8i-h3-pwm";
            reg = <0x01c21400 0x8>;
            clocks = <&osc24M>;
            #pwm-cells = <3>;
            status = "okay";
    };
实例 : 类 @ 地址 {
compatible: “供应商, 设备id”
reg
clocks
}

基于设备树的driver的结构体的填充

static struct of_device_id beep_table[] = {
    {.compatible = "fs4412,beep"},
};
static struct platform_driver beep_driver=
{
    .probe = beep_probe,
    .remove = beep_remove,
    .driver={
        .name = "bigbang",
        .of_match_table = beep_table,
    },
};

make dtbs 在内核根目录

sys下设备树查看

/sys/firmware/devicetree 可以查看实际使用的是设备树

Zero外设把玩

本节介绍zero各种外设资源的适配。

GPIO

本节介绍zero 各种GPIO操作的适配。

在这里先给出几种操作方法的IO翻转速率比较:

操作方式 翻转速率 备注
Shell调用gpio.sh ~18Hz 调用在磁盘上的文件,读取时间比较慢
直接操作 /sys/class/gpio/gpio192/value 6KHz 由于系统其它任务,偶尔有0.4ms左右延迟,仔细看能感受到闪烁
C语言mmap操作    

文件IO方式操作

GPIO编号及复用功能

在Linux中,GPIO 使用0~MAX_INT之间的整数标识。

对于32位CPU,每组GPIO 32个,引脚号就是按顺序排列。

LicheePi Zero的所有IO的复用功能及GPIO标识号为:

>> 总共52个IO,所有IO上电默认状态为高阻态, 驱动电流强度20mA >> PB和PG具有中断功能

Pin Name Pin Number Func2 Func3 Func4 Func5 Func6
PB0 32 UART2_TX - - - PB_EINT0
PB1 33 UART2_RX - - - PB_EINT1
PB2 34 UART2_RTS - - - PB_EINT2
PB3 35 UART2_CTS - - - PB_EINT3
PB4 36 PWM0 - - - PB_EINT4
PB5 37 PWM1 - - - PB_EINT5
PB6 38 TWI0_SCK - - - PB_EINT6
PB7 39 TWI0_SDA - - - PB_EINT7
PB8 40 TWI1_SCK UART0_TX - - PB_EINT8
PB9 41 TWI1_SDA UART0_RX - - PB_EINT9
PC0 64 SDC2_CLK SPI0_MISO - - -
PC1 65 SDC2_CMD SPI0_CLK - - -
PC2 66 SDC2_RST SPI0_CS - - -
PC3 67 SDC2_D0 SPI0_MOSI -    
PE0 128 CSI_PCLK LCD_CLK - - -
PE1 129 CSI_MCLK LCD_DE - - -
PE2 130 CSI_HSYNC LCD_HSYNC - - -
PE3 131 CSI_VSYNC LCD_VSYNC - - -
PE4 132 CSI_D0 LCD_D2 - - -
PE5 133 CSI_D1 LCD_D3 - - -
PE6 134 CSI_D2 LCD_D4 - - -
PE7 135 CSI_D3 LCD_D5 - - -
PE8 136 CSI_D4 LCD_D6 - - -
PE9 137 CSI_D5 LCD_D7 - - -
PE10 138 CSI_D6 LCD_D10 - - -
PE11 139 CSI_D7 LCD_D11 - - -
PE12 140 CSI_D8 LCD_D12 - - -
PE13 141 CSI_D9 LCD_D13 - - -
PE14 142 CSI_D10 LCD_D14 - - -
PE15 143 CSI_D11 LCD_D15 - - -
PE16 144 CSI_D12 LCD_D18 - - -
PE17 145 CSI_D13 LCD_D19 - - -
PE18 146 CSI_D14 LCD_D20 - - -
PE19 147 CSI_D15 LCD_D21 - - -
PE20 148 CSI_FIELD CSI_MIPI_MCLK - - -
PE21 149 CSI_SCK TWI1_SCK UART1_TX - -
PE22 150 CSI_SDA TWI1_SDA UART1_RX - -
PE23 151 - LCD_D22 UART1_RTS - -
PE24 152 - LCD_D23 UART1_CTS - -
PF0 160 SDC0_D1 JTAG_MS - - -
PF1 161 SDC0_D0 JTAG_DI - - -
PF2 162 SDC0_CLK UART0_TX - - -
PF3 163 SDC0_CMD JTAG_DO - - -
PF4 164 SDC0_D3 UART0_RX - - -
PF5 165 SDC0_D2 JTAG_CK - - -
PF6 166 - - - - -
PG0 192 SDC1_CLK - - - PG_EINT0
PG1 193 SDC1_CMD - - - PG_EINT1
PG2 194 SDC1_D0 - - - PG_EINT2
PG3 195 SDC1_D1 - - - PG_EINT3
PG4 196 SDC1_D2 - - - PG_EINT4
PG5 197 SDC1_D3 - - - PG_EINT5
sysfs操作GPIO

/sys/class/gpio目录下的三种文件:

  • export/unexport文件
  • gpioN指代具体的gpio引脚
  • gpio_chipN指代gpio控制器

export/unexport:

  • /sys/class/gpio/export,只写,写入GPIO编号来向内核申请GPIO控制权(前提是没有内核代码申请这个gpio端口), 成功后会在目录下生成gpioN目录。
  • /sys/class/gpio/unexport和导出的效果相反。

gpioN:

指代某个具体的gpio端口, 内有以下属性文件:

Attribution Read/Write Value Function
direction RW in,out;low,high 设置输入输出
value RW 0,非零 读取或者写入IO电平
edge RW none , rising , falling , both 配置中断触发方式
active_low RW 0,非零 设置低电平有效

gpiochipN

gpiochipN表示的就是一个gpio_chip,用来管理和控制一组gpio端口的控制器,该目录下存在以下属性文件:

Attribution Function
base 和N相同,表示控制器管理的最小的端口编号。
lable 诊断使用的标志,寄存器地址,1c20800.pinctrl
ngpio 表示控制器管理的gpio端口数量,A~G,224

使用sysfs操作GPIO的例子:

#echo 192 > /sys/class/gpio/export  #导出 PG0, GREEN
#ls /sys/class/gpio/
export     gpio192    gpiochip0  unexport
#ls /sys/class/gpio/gpio192/
active_low direction subsystem/ value device/ power/ uevent
#echo "out" > /sys/class/gpio/gpio192/direction #设置为输出
#echo 0 > /sys/class/gpio/gpio192/value     #亮灯
#echo 1 > /sys/class/gpio/gpio192/value #灭灯
#echo "in" > /sys/class/gpio/gpio192/direction #设置为输入
#cat /sys/class/gpio/gpio192/value #读取电平
0

用户可以参考以上操作进行GPIO控制。

注意对重要引脚的导出操作可能会使系统崩溃。

LicheePi Zero提供了简单的shell脚本进行GPIO读写(代码在https://github.com/Lichee-Pi/lichee-pi-zero/tree/master/SoftWare,下同):

gpio.sh init 192 out
gpio.sh set 192 out
gpio.sh get 192
gpio.sh w 192 1
gpio.sh r 192
gpio.sh deinit 192
附录(gpio.sh源码)
#!/bin/sh
function help()
{
echo "gpio.sh usage:"
echo "        gpio.sh init PG0 out"
echo "        gpio.sh set PG0 out"
echo "        gpio.sh get PG0"
echo "        gpio.sh w PG0 1"
echo "        gpio.sh r PG0"
echo "        gpio.sh deinit PG0"
}

if [ $# -lt 2 ]; then
        help;
        exit;
fi

portpin=`echo $2 | tr 'a-z' 'A-Z'`;
port=${portpin:1:1};
pin=${portpin:2:1};
#echo $port
#echo $pin
num=`printf "%d" "'$port"`;
num=`expr \( $num - 65 \) \* 32 + $pin`;
if [ $? -ne 0 ]; then
        help;
        exit
fi
#echo $num

case $1 in
        init)
        echo $num > /sys/class/gpio/export
        echo $3 > /sys/class/gpio/gpio${num}/direction
        ;;
        set)
        echo $3 > /sys/class/gpio/gpio${num}/direction
        ;;
        get)
        echo `cat /sys/class/gpio/gpio${num}/direction`
        ;;
        w)
        echo $3 > /sys/class/gpio/gpio${num}/value
        ;;
        r)
        echo `cat /sys/class/gpio/gpio{num}/value`
        ;;
        deinit)
        echo $num > /sys/class/gpio/unexport
        ;;
        *)
        help
        ;;
esac

C语言方式(mmap)

这里提供接近单片机寄存器操作的一种应用层GPIO操作方式,也封装成库给大家使用。

GPIO 寄存器介绍

V3S datasheet 第224页是GPIO控制器的相关介绍。

V3S从有PB/C/E/F/G 五个GPIO端口,每个都是32位端口(实际引脚没有引出那么多), 也是32位寄存器。

每个端口由以下几个寄存器组成:

(n=1,2,4,5,6;寄存器基址为0x01C20800)
Register Name Offset) Description 详细描述
Pn_CFG0 n*0x24+0x00 Port n Configure Register 0 (n=1,2,4,5,6) 每个脚4bit,最高位保留。000 输入;001 输出;010 外设功能1;011 外设功能2;100 外设功能3;101 外设功能4;110 EINT中断;111 IO失能
Pn_CFG1 n*0x24+0x04 Port n Configure Register 1 同上
Pn_CFG2 n*0x24+0x08 Port n Configure Register 2 同上
Pn_CFG3 n*0x24+0x0C Port n Configure Register 3 同上
Pn_DAT n*0x24+0x10 Port n Data Register 每位代表输入输出值
Pn_DRV0 n*0x24+0x14 Port n Multi-Driving Register 0 0~3逐级递增
Pn_DRV1 n*0x24+0x18 Port n Multi-Driving Register 1 同上
Pn_PUL0 n*0x24+0x1C Port n PullRegister 0 0浮空,1上拉,2下拉,3保留
Pn_PUL1 n*0x24+0x20 Port n PullRegister 1 同上
Pn_INT_CFG0 0x200+n*0x20+0x00 PIO Interrrupt Configure Register 0 0上升,1下降,2高电平,3低电平,4双边沿
Pn_INT_CFG1 0x200+n*0x20+0x04 PIO Interrrupt Configure Register 1 同上
Pn_INT_CFG2 0x200+n*0x20+0x08 PIO Interrrupt ConfigureRegister 2 同上
Pn_INT_CFG3 0x200+n*0x20+0x0C PIO Interrrupt Configure Register 3 同上
Pn_INT_CTL 0x200+n*0x20+0x10 PIO Interrupt Control Register 0失能,1使能
Pn_INT_STA 0x200+n*0x20+0x14 PIO Interrupt Status Register 0未发生中断,1发生中断。写1清除
Pn_INT_DEB 0x200+n*0x20+0x18 PIO Interrupt Debounce Register bit0,选择中断时钟,0,32Khz,低速时钟;1,24MHz主时钟。bit6:4,去抖时钟分频,选择的时钟源2^n分频,即最大256分频。
寄存器 地址
PB配置 1C20824
PC配置 1C20848
PE配置 1C20890
PF配置 1C208B4
PG配置 1C208D8
mmap简介

mmap简单来说就是把一片物理内存空间(或者文件)映射到应用的虚拟内存空间,这样,直接在应用层就能操作 CPU的寄存器,类似于单片机的寄存器操作。我们只要封装好寄存器操作的库函数,就能在以后的程序里简单调用了~

详细的mmap介绍可以参考附录的链接。

为了操作寄存器,我们需要用到 /dev/mem 设备,这个设备是是物理内存的全映像,可以用来访问物理内存,一般用法是 open("/dev/mem",O_RDWR|O_SYNC),然后mmap,接着就可以用mmap的地址来访问物理内存,这实际上就是实现用户空间驱动的一种方法。

#include <sys/mmap.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
//start: 映射区的起始地址,0的话由接口自动返回
//length: 映射区的长度
//prot: 内存保护标志,不能与文件的打开模式冲突。以下值可以 或 组合。
    //PROT_EXEC  页内容可以被执行
    //PROT_READ  页内容可以被读取
    //PROT_WRITE  页可以被写入
    //PROT_NONE  页不可访问
//flag: 指定映射对象的类型,是否可以共享等。
//fd: 文件描述符
//oft: 被映射对象内容的起点偏移。映射物理内存的话,就是物理内存地址。**必须页对齐。**

int munmap(void *start, size_t length);
//start: 前面获得的地址
//length: 映射区的大小。

int msync ( void * addr , size_t len, int flags)
//一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。
//但是对于映射物理内存来说是直接作用的。

代码片段:

#include <sys/mmap.h>

char dev_name[] = "/dev/mem";
GPIO_REGISTER  *gpio_base;
fd  =  open(dev_name,O_RDWR);
if(fd<0){
    printf("open %s is error\n",dev_name);
    return -1 ;
}
gpio_base = (GPIO_REGISTER *)mmap( 0, 0x32, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0x40060000 );
if(gpio_base == NULL){
    printf("gpio base mmap is error\n");
    close(fd);
    return -1;
}
//后面就是对寄存器操作了
//结束后解除映射
munmap(gpio_base, 0x32);

我基于mmap写了个应用层调试寄存器的小程序,reg-dbger, 在github上可以下载使用。

使用方法为:

reg-dbger r reg_addr                            //读寄存器
reg-dbger rb reg_addr bit_oft bit_cnt       //读寄存器的bit_oft开始的bit_cnt位
reg-dbger w reg_addr value                  //写寄存器
reg-dbger wb reg_addr bit_oft bit_cnt value //写寄存器的bit_oft开始的bit_cnt位
reg-dbger dump reg_addr cnt                 //批量dump出cnt个寄存器值

比如操作gpio寄存器,点亮熄灭Zero上的绿色LED:

# PG0
# 配置寄存器 0x01C20800+6*0x24+0=1C208D8
# 数据寄存器 0x01C20800+6*0x24+0x10 = 1C208E8
reg-dbger r 1C208D8
reg-dbger r 1C208E8
reg-dbger wb 1C208D8 0 3 1    #输出状态
reg-dbger wb 1C208E8 0 1 0     #输出0,点亮

同样基于mmap写了个应用层操作GPIO的小程序,lpi-gpio, 在github上可以下载使用。

使用方法为:

lpi-gpio set PG0 out/in 0/1/2       //设置为输出的话,0低电平,1,2高电平;设置为输入,0下拉,1上拉,2浮空。
lpi-gpio r PG0
lpi-gpio w PG0 0/1
lpi-gpio pwm PG0 100 200    //PG0 pwm输出,两个参数分别表示高低电平的微秒数(>60us)
lpi-gpio test PG0   //测试PG0用函数翻转IO的最大速率,结果为1.85MHz
lpi-gpio tfast PG0  //测试PG0用软件翻转IO的最大速率,结果为10MHz

为方便在C语言里调用,我生成了gpio操作的动态库 libgpio.so,大家可以在c程序中调用。

这里是一个简单的使用例程:

#include "lpi_gpio.h"
#define USLEEP_T 61

int main()
{
        lpi_gpio_initlib();
        lpi_gpio_init(6, 0, 1, 0);
        while(1)
        {   //generate 1KHz PWM
                lpi_gpio_w(6, 0, 1);
                usleep(500-USLEEP_T);
                lpi_gpio_w(6, 0, 0);
                usleep(500-USLEEP_T);
        }
        lpi_gpio_deinitlib();
        return;
}
//gcc -fPIC -shared -o libgpio.so lib_gpio.c        //编译生成动态库
gcc test_gpio.c -L. -lgpio -o test_gpio             //编译生成应用程序
LD_LIBRARY_PATH=. ./test_gpio       //运行应用程序,手工指定动态库位置
//or add libgpio.so to /etc/ld.so.conf, ldconfig

Python 操作GPIO

本文目录

为了大家使用方便,这里同时提供了pyhton的gpio库:lpi_gpio.so

接口名与C的函数库相同,这里是一个简单的使用示例:

test_gpio.py
 import lpi_gpio
 import signal
 import time

 def exit(signum, frame):
         lpi_gpio.deinitlib()
         print('exit test_gpio')
         exit()

 signal.signal(signal.SIGINT, exit)
 signal.signal(signal.SIGTERM, exit)

 lpi_gpio.initlib()
 lpi_gpio.init(6,0,1,0)
 while True:         #闪烁PG0 绿灯
         lpi_gpio.w(6,0,0)
         time.sleep(0.5)
         lpi_gpio.w(6,0,1)
         time.sleep(0.5)

pinctrl-sunxi介绍

本文目录

drivers/pinctrl/sunxi/pinctrl-sunxi.c

drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c //具体的引脚功能复用情况

gpio_in

gpio_out

uboot下gpio操作

#define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk())

arch/arm/mach-sunxi/cpu_info.c

arch/arm/lib/bootm.c

#include <asm/arch/cpu.h>
#include <asm/arch/clock.h>
#include <cli.h>

unsigned int * cfg_reg=(unsigned int *)(0x01C20800+1*0x24+0);
unsigned int * data_reg=(unsigned int *)(0x01C20800+1*0x24+0x10);
unsigned int tmp,tmp0,tmp1;
uint64_t t1;
int i;

tmp = *cfg_reg;
tmp &= ~0x0f00;
tmp |= 0x0100;
*cfg_reg = tmp;

tmp=*data_reg;
tmp0 = tmp&(~0x04);
tmp1 = tmp|0x04;

t1=get_ticks()+get_tbclk()/12;
for(i=0;i<3;i++)
{
*data_reg=tmp0;
puts("beep 0\n");
while(get_ticks()<t1);
t1=get_ticks()+get_tbclk()/10;
*data_reg=tmp1;
puts("beep 1\n");
while(get_ticks()<t1);
t1=get_ticks()+get_tbclk()/10;
}
*data_reg=tmp0;

UART操作

基础操作

首先在dts里使能UART:

sun8i-v3s.dtsi:
uart0_pins_a: uart0@0 { pins = "PB8", "PB9";function = "uart0";bias-pull-up; };
uart1_pins_a: uart1@0 { pins = "PE21", "PE22";function = "uart1";bias-pull-up; };
uart2_pins_a: uart2@0 { pins = "PB0", "PB1";function = "uart2";bias-pull-up; };

sun8i-v3s-licheepi-zero.dts:
&uart0 { pinctrl-0 = <&uart0_pins_a>;pinctrl-names = "default";status = "okay"; };
&uart1 { pinctrl-0 = <&uart1_pins_a>;pinctrl-names = "default";status = "okay"; };
&uart2 { pinctrl-0 = <&uart2_pins_a>;pinctrl-names = "default";status = "okay"; };

然后启动后就能看到ttyS0~ttyS2了

再使用常见串口软件就能使用

波特率分频问题

为了串口通信稳定,一般要求波特率误差在2.5%以内。

V3S的uart是挂在APB2下,而APB2时钟是24M,所以对一些高速率的波特率,难以分频到合适的频率。

以921600为例,下面进行修改:

进入 uboot的arch/arm/mach-sunxi/clock_sun6i.c,修改uart时钟:

void clock_init_uart(void)
{
#if CONFIG_CONS_INDEX < 5
        struct sunxi_ccm_reg *const ccm =
                (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;

        /* uart clock source is apb2 */
        writel(APB2_CLK_SRC_OSC24M|      //这里改为APB2_CLK_SRC_PLL6,从内部pll6时钟分频
            APB2_CLK_RATE_N_1|               //这里预分频不变
            APB2_CLK_RATE_M(1),
            &ccm->apb2_div);

pll6时钟默认为600MHz,可以分出比较高的串口波特率。

600/0.9216/16=40.69, 舍入为41,相对误差为0.75%

然后再修改include/configs/sunxi-common.h
 /* ns16550 reg in the low bits of cpu reg */
 #define CONFIG_SYS_NS16550_CLK          24000000    //这里改为600000000
 #ifndef CONFIG_DM_SERIAL

剩余的就是按照原有方法修改波特率了。

I2C操作

使能设备树节点

arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
 &i2c0 {
         status = "okay";

         ns2009: ns2009@48 {
                 compatible = "nsiway,ns2009";
                 reg = <0x48>;
         };
         sht21: sht21@40 {
                 compatible = "sht21";
                 reg = <0x40>;
         };
         atmel_mxt_ts@4a {
                 compatible = "atmel,atmel_mxt_ts";
                 reg = <0x4a>;
                 /*interrupt-parent = <&pio>;
                 interrupts = <6 5 IRQ_TYPE_LEVEL_LOW>;*/ //省引脚,使用轮训方式
         };
 };

使用i2c-tools

root@LicheePi:~# i2cdetect -l         #查看系统使能的i2c总线,这里只有i2c0一个
i2c-0       i2c             mv64xxx_i2c adapter                     I2C adapter
root@LicheePi:~# i2cdetect -r -y 0  //检测总线上的设备。-y表示省去交互式
    0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: 40 -- -- -- -- -- -- -- UU -- 4a -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

这里的40,48,4a 分别是sht21温湿度传感器,ns2009电阻式触摸传感器,mxt336T电容式触摸传感器。

i2c上读写数据:

i2cset -y 1 0x40 0x00 0x13
i2cget -y 1 0x40 0x00

sht21 传感器使用

insmod sht21.ko
echo sht21 0x40 > /sys/bus/i2c/devices/i2c-0/new_device
ls /sys/class/hwmon/hwmon0
    device           name   subsystem    uevent
    humidity1_input  power  temp1_input
cat /sys/class/hwmon/hwmon0/temp1_input
    25201           #毫摄氏度,即25.201摄氏度
cat /sys/class/hwmon/hwmon0/humidity1_input
    58872           #毫百分之1,即58.872%RH

PWM输出

PWM驱动

dtsi: 增加PWM0,1的引脚

pwm0_pins: pwm0 {
        pins = "PB4";
        function = "pwm0";
};
pwm1_pins: pwm1 {
        pins = "PB5";
        function = "pwm1";
};
pwm: pwm@01c21400 {
            compatible = "allwinner,sun7i-a20-pwm";         //这里选a20是因为v3s和a20一样有两路pwm
            reg = <0x01c21400 0xC>;
            clocks = <&osc24M>;
            #pwm-cells = <3>;
            status = "okay";
    };

dts中使能PWM:

&pwm {
        pinctrl-names = "default";
        pinctrl-0 = <&pwm0_pins>, <&pwm1_pins>;
        status = "okay";
};

sysfs里使能:

echo 0 > /sys/class/pwm/pwmchip0/export
echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period
echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

echo 1 > /sys/class/pwm/pwmchip0/export
echo 1000000 > /sys/class/pwm/pwmchip0/pwm1/period
echo 200000 > /sys/class/pwm/pwmchip0/pwm1/duty_cycle
echo 1 > /sys/class/pwm/pwmchip0/pwm1/enable                //注意一定要配置好参数后再使能,否则会报参数错误
  • polarity:接受normal或inversed两个参数.
  • period:表示pwm波的周期(单位:纳秒);
  • duty_cycle:在normal模式下,表示一个周期内高电平持续的时间(单位:纳秒),所以duty_cycle <= period;在reversed模式下,表示一个周期中低电平持续的时间(单位:纳秒);
  • enable:向其中写入1表示启动pwm,写入0,表示关闭pwm;

注意V3S的PWM由24M分频而来,无法生成太高频的pwm。

PWM驱动分析

PWM驱动在 drivers/pwm/pwm-sun4i.c 中。

插入驱动:

static int sun4i_pwm_probe(struct platform_device *pdev)
{
    struct sun4i_pwm_chip *pwm;
    struct resource *res;
    int ret;
    const struct of_device_id *match;

    match = of_match_device(sun4i_pwm_dt_ids, &pdev->dev);  //在设备树中查找节点

    pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
    if (!pwm)
        return -ENOMEM;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    pwm->base = devm_ioremap_resource(&pdev->dev, res);     //申请寄存器的内存空间
    if (IS_ERR(pwm->base))
        return PTR_ERR(pwm->base);

    pwm->clk = devm_clk_get(&pdev->dev, NULL);
    if (IS_ERR(pwm->clk))
        return PTR_ERR(pwm->clk);

    pwm->data = match->data;
    pwm->chip.dev = &pdev->dev;
    pwm->chip.ops = &sun4i_pwm_ops;
    pwm->chip.base = -1;
    pwm->chip.npwm = pwm->data->npwm;
    pwm->chip.of_xlate = of_pwm_xlate_with_flags;
    pwm->chip.of_pwm_n_cells = 3;

    spin_lock_init(&pwm->ctrl_lock);

    ret = pwmchip_add(&pwm->chip);  //在/sys/class/pwm/下创建pwmchip0X
    if (ret < 0) {
        dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
        return ret;
    }

    platform_set_drvdata(pdev, pwm);

    return 0;
}

CODEC的使用

默认dts中使能了codec

需要使用的话,在buildroot中勾选 alsa-utils相关命令

CODEC 设备

# ls /dev/snd
controlC0  pcmC0D0c   pcmC0D0p   timer
  • controlC0表示控制器
  • pcmC0D0c 表示capture
  • pcmC0D0p 表示play
  • timer 表示定时器

使用该设备编程可以参考:http://blog.csdn.net/zhenwenxian/article/details/5901239

出现了该设备说明codec驱动被正确加载。

alsa-utils 使用

查看设备
# arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: Codec [V3s Audio Codec], device 0: CDC PCM Codec-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0
调节音量

alsamixer是图形化的amixer工具,如下图所示

https://box.kancloud.cn/ecce93c9bc9cc3cfd3b7391265d3b76d_727x459.jpg
https://box.kancloud.cn/e81cf1f866ed39a7110cb92bc8892c4a_725x457.jpg

从左到右是:耳机音量,耳机输出源,Mic增益,mic1 boost, DAC增益。

可以很方便地调整音频输出设置

00表示当前音量正常,MM表示此声道是静音.可以通过键盘上的M键来切换静音和正常状态.

开机后默认状态是静音状态,需要取消掉静音状态

amixer -c 0 sset 'Headphone',0 100% unmute
查看控制器 amixer contents
numid=13,iface=MIXER,name='Headphone Source Playback Route'
; type=ENUMERATED,access=rw------,values=2,items=2
; Item #0 'DAC'
; Item #1 'Mixer'
: values=0,0
numid=3,iface=MIXER,name='Headphone Playback Switch'
; type=BOOLEAN,access=rw------,values=2
: values=off,off
numid=2,iface=MIXER,name='Headphone Playback Volume'
; type=INTEGER,access=rw---R--,values=1,min=0,max=63,step=0
: values=7
| dBscale-min=-63.00dB,step=1.00dB,mute=1
numid=5,iface=MIXER,name='Mic1 Boost Volume'
; type=INTEGER,access=rw---R--,values=1,min=0,max=7,step=0
: values=4
| dBrange-
    rangemin=0,,rangemax=0
    | dBscale-min=0.00dB,step=0.00dB,mute=0
    rangemin=1,,rangemax=7
    | dBscale-min=24.00dB,step=3.00dB,mute=0

numid=12,iface=MIXER,name='Mic1 Capture Switch'
; type=BOOLEAN,access=rw------,values=2
: values=on,on
numid=9,iface=MIXER,name='Mic1 Playback Switch'
; type=BOOLEAN,access=rw------,values=2
: values=off,off
numid=4,iface=MIXER,name='Mic1 Playback Volume'
; type=INTEGER,access=rw---R--,values=1,min=0,max=7,step=0
: values=3
| dBscale-min=-4.50dB,step=1.50dB,mute=0
numid=6,iface=MIXER,name='ADC Gain Capture Volume'
; type=INTEGER,access=rw---R--,values=1,min=0,max=7,step=0
: values=3
| dBscale-min=-4.50dB,step=1.50dB,mute=0
numid=7,iface=MIXER,name='DAC Playback Switch'
; type=BOOLEAN,access=rw------,values=2
: values=off,off
numid=1,iface=MIXER,name='DAC Playback Volume'
; type=INTEGER,access=rw---R--,values=1,min=0,max=63,step=0
: values=63
| dBscale-min=-73.08dB,step=1.16dB,mute=0
numid=8,iface=MIXER,name='DAC Reversed Playback Switch'
; type=BOOLEAN,access=rw------,values=2
: values=off,off
numid=10,iface=MIXER,name='Mixer Capture Switch'
; type=BOOLEAN,access=rw------,values=2
: values=off,off
numid=11,iface=MIXER,name='Mixer Reversed Capture Switch'
; type=BOOLEAN,access=rw------,values=2
: values=off,off
录音测试

耳机输入内置了放大器。

耳机,linein,同时只能使用1种。

使用输入端口前需要设置mixer控制器,ADC input Mux

ADC Input Mux 和对应的输入端口

0   linein
1   fmin
2   mic1
3   mic2
4   mic1,mic2
5   mic1+mic2
6   output mixer
7   linein,mic1

使用amixer来设置通道

amixer -c <"card"> cset numid=<"control#"> <"input_port">

where:
<"card"> is the card, 0 for the sunxi-codec and 1 for the hdmi audio output
<"input_port"> is the input port from the table
<"control#"> is the control # showed using: aximer contents

使用下面命令使能耳机并录音

amixer -c 0 cset numid=12 2         使能mic1
arecord -D hw:0,0 -d 3 -f S16_LE -r 16000 tmp.wav   录音测试
播放
amixer -c 0 sset 'Headphone',0 100% unmute
speaker-test -twav -c2
atest
aplay  tmp.wav

配置文件

/etc/asound.conf(简易配置) :

{{{
    pcm.!default{
        type hw
        card 0
        devive 0
        }
    ctl.!default{
        type hw
        card 0
        device 0
        }
}}}

其中card代表声卡号, device代表设备号, asound.conf的配置极其强大和复杂,详情可查阅 http://www.alsa-project.org/main/index.php/Main_Page

card, device的确定方法

card, device必须对应hdmi的声卡号和设备号,可以使用aplay -l查看对应的hdmi设备,可能会有出现多个hdmi设备,确定当前可以使用的hdmi设备的方法如下:

#. cat /proc/asound/cards 查看nvidia设备对应的ID号(假设=1)

#. alsamixer -c 1 打开声音设置,其中<S/PDIF>即为HDMI输出,“MM”代表静音,alsa在每次重启声音设备时都会默认为静音,所以必须首先打开音量再进行后续的操作。

#. alsactl store 保存上述配置

#. aplay -D hw:1,7  /usr/share/sounds/alsa/test.wav  "hw后的1代表声卡号,7代表设备号,需要根据aplay -l的输出来确定这两个数字"找到对应的hdmi输出口。

参考 http://linux-sunxi.org/Audio_Codec

以太网使用指南

U-Boot适配Ethernet

U-Boot 2017已经支持了sun8i-emac的驱动,只需要在编译时选上并且修改dts就行。

  • 进入u-boot源码目录:
make LicheePi_Zero_defconfig
make menuconfig
https://box.kancloud.cn/b23c8b14ce63d6f887aa2372e68db411_972x582.png
  • 选择 Device Drivers --->
https://box.kancloud.cn/a2fa42f1f4fad923ac8734b2e1626a0a_972x582.png
  • 选择 Network device support ---> 并选中 Allwinner Sun8i Ethernet MAC support
https://box.kancloud.cn/a4718b669f8b9bd3daa750d3a5095e26_972x582.png
  • 修改dts

sun8i-v3s-licheepi-zero.dts:

diff --git a/arch/arm/dts/sun8i-v3s-licheepi-zero.dts b/arch/arm/dts/sun8i-v3s-licheepi-zero.dts
index 3d9168c..b8b9fc3 100644
--- a/arch/arm/dts/sun8i-v3s-licheepi-zero.dts
+++ b/arch/arm/dts/sun8i-v3s-licheepi-zero.dts
@@ -49,6 +49,7 @@
        compatible = "licheepi,licheepi-zero", "allwinner,sun8i-v3s";

        aliases {
+               ethernet0 = &emac;
                serial0 = &uart0;
        };

@@ -81,3 +82,14 @@
        usb0_id_det-gpio = <&pio 5 6 GPIO_ACTIVE_HIGH>;
        status = "okay";
};
+
+&emac {
+       phy = <&phy0>;
+       phy-mode = "mii";
+       allwinner,use-internal-phy;
+       allwinner,leds-active-low;
+       status = "okay";
+       phy0: ethernet-phy@0 {
+               reg = <1>;
+       };
+};

sun8i-v3s.dtsi:

diff --git a/arch/arm/dts/sun8i-v3s.dtsi b/arch/arm/dts/sun8i-v3s.dtsi
index ebefc0f..cb81dd5 100644
--- a/arch/arm/dts/sun8i-v3s.dtsi
+++ b/arch/arm/dts/sun8i-v3s.dtsi
@@ -96,6 +96,11 @@
                #size-cells = <1>;
                ranges;

+               syscon: syscon@01c00000 {
+                       compatible = "allwinner,sun8i-h3-syscon","syscon";
+                       reg = <0x01c00000 0x34>;
+               };
+
                mmc0: mmc@01c0f000 {
                        compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c0f000 0x1000>;
@@ -208,6 +213,17 @@
                        interrupt-controller;
                        #interrupt-cells = <3>;

+                       emac_rgmii_pins: emac0@0 {
+                               allwinner,pins = "PD0", "PD1", "PD2", "PD3",
+                                               "PD4", "PD5", "PD7",
+                                               "PD8", "PD9", "PD10",
+                                               "PD12", "PD13", "PD15",
+                                               "PD16", "PD17";
+                               allwinner,function = "emac";
+                               allwinner,drive = <SUN4I_PINCTRL_40_MA>;
+                               allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+                       };
+
                        uart0_pins_a: uart0@0 {
                                pins = "PB8", "PB9";
                                function = "uart0";
@@ -270,6 +286,20 @@
                        status = "disabled";
                };

+               emac: ethernet@1c30000 {
+                       compatible = "allwinner,sun8i-h3-emac";
+                       reg = <0x01c30000 0x104>, <0x01c00030 0x4>;
+                       reg-names = "emac", "syscon";
+                       interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
+                       resets = <&ccu RST_BUS_EMAC>, <&ccu RST_BUS_EPHY>;
+                       reset-names = "ahb", "ephy";
+                       clocks = <&ccu CLK_BUS_EMAC>, <&ccu CLK_BUS_EPHY>;
+                       clock-names = "ahb", "ephy";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "disabled";
+               };
+
                gic: interrupt-controller@01c81000 {
                        compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic";
                        reg = <0x01c81000 0x1000>,
  • 编译:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
  • 烧写:
dd if=u-boot-sunxi-with-spl.bin of=${card} bs=1024 seek=8
  • 使用Ethernet:
https://box.kancloud.cn/e0b2b778262db0e1b19e3316d39ac9cb_1054x417.png

如果出现: *** ERROR:ipaddr' not set ,就需要设置下自己的ip: setenv ipaddr 192.168.1.111

Kernel适配Ethernet

在最新的linux 4.14内核中,已经增加了对以太网的支持。

目前Linux 4.12还没又对sun8i-emac进行支持,所以Kernel要使用V3s的以太网要打sun8i-emac的补丁还有修改dts文件。

  • 打上sun8i-emac补丁:

拉下我已经适配好的内核源码:https://github.com/techping/linux/tree/licheepi-zero

  • 修改dts(上面git仓库是已经修改完的):

sun8i-v3s-licheepi-zero.dts:

index 387fc2a..904e60e 100644
--- a/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
+++ b/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
@@ -50,6 +50,7 @@

        aliases {
                serial0 = &uart0;
+               ethernet0 = &emac;
        };

        chosen {
@@ -101,3 +102,10 @@
        usb0_id_det-gpio = <&pio 5 6 GPIO_ACTIVE_HIGH>;
        status = "okay";
};
+
+&emac {
+       phy-handle = <&int_mii_phy>;
+       phy-mode = "mii";
+       allwinner,leds-active-low;
+       status = "okay";
+};

sun8i-v3s.dtsi:

diff --git a/arch/arm/boot/dts/sun8i-v3s.dtsi b/arch/arm/boot/dts/sun8i-v3s.dtsi
index 7107596..65be2ab 100644
--- a/arch/arm/boot/dts/sun8i-v3s.dtsi
+++ b/arch/arm/boot/dts/sun8i-v3s.dtsi
@@ -40,7 +40,10 @@
*     OTHER DEALINGS IN THE SOFTWARE.
*/

+#include <dt-bindings/clock/sun8i-v3s-ccu.h>
+#include <dt-bindings/reset/sun8i-v3s-ccu.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/pinctrl/sun4i-a10.h>

/ {
        #address-cells = <1>;
@@ -93,6 +96,12 @@
                #size-cells = <1>;
                ranges;

+               syscon: syscon@1c00000 {
+                       compatible = "allwinner,sun8i-h3-system-controller",
+                               "syscon";
+                       reg = <0x01c00000 0x1000>;
+               };
+
                mmc0: mmc@01c0f000 {
                        compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c0f000 0x1000>;
@@ -205,6 +214,17 @@
                        interrupt-controller;
                        #interrupt-cells = <3>;

+                       emac_rgmii_pins: emac0@0 {
+                               allwinner,pins = "PD0", "PD1", "PD2", "PD3",
+                                               "PD4", "PD5", "PD7",
+                                               "PD8", "PD9", "PD10",
+                                               "PD12", "PD13", "PD15",
+                                               "PD16", "PD17";
+                               allwinner,function = "emac";
+                               allwinner,drive = <SUN4I_PINCTRL_40_MA>;
+                               allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+                       };
+
                        i2c0_pins: i2c0 {
                                pins = "PB6", "PB7";
                                function = "i2c0";
@@ -295,6 +315,31 @@
                        #size-cells = <0>;
                };

+               emac: ethernet@1c30000 {
+                       compatible = "allwinner,sun8i-h3-emac";
+                       syscon = <&syscon>;
+                       reg = <0x01c30000 0x104>;
+                       interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "macirq";
+                       resets = <&ccu RST_BUS_EMAC>;
+                       reset-names = "stmmaceth";
+                       clocks = <&ccu CLK_BUS_EMAC>;
+                       clock-names = "stmmaceth";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "disabled";
+
+                       mdio: mdio {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               int_mii_phy: ethernet-phy@0 {
+                                       compatible = "ethernet-phy-ieee802.3-c22";
+                                       reg = <1>;
+                                       clocks = <&ccu CLK_BUS_EPHY>;
+                                       resets = <&ccu RST_BUS_EPHY>;
+                               };
+                       };
+               };
                gic: interrupt-controller@01c81000 {
                        compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic";
                        reg = <0x01c81000 0x1000>,
  • 进入内核目录:
make sunxi_defconfig ARCH=arm
make menuconfig ARCH=arm
  • 选择 Device Drivers --->

  • 选择 Network device support --->

  • 选择 Ethernet driver support --->

  • 选择

    [*]   STMicroelectronics devices                                                         x x
    <*>     STMicroelectronics 10/100/1000/EQOS Ethernet driver
    <*>       STMMAC Platform bus support                                                    x x
    < >         Support for snps,dwc-qos-ethernet.txt DT binding.
    <*>         Generic driver for DWMAC                                                     x x
    <*>         Allwinner GMAC support                                                       x x
    <*>         Allwinner sun8i GMAC support
    
  • 编译

    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

得到zImage和sun8i-v3s-licheepi-zero.dtb

  • 烧写

将内核、dtb、rootfs刷入sd卡中,启动:

ifup eth0
https://box.kancloud.cn/d7b86a2a5f8686786fb155528c0d09d0_1465x250.png

eth0启动成功!

https://box.kancloud.cn/8f807f27c88c2bff281629f8e7874398_1802x264.png

Ethernet驱动适配成功!

设备驱动

本节介绍zero与各种模块的适配。

GPU/DRM驱动

本文目录

代码在driver/gpu/drm下

The Direct Rendering Manager (DRM) is a subsystem of the Linux kernel responsible for
interfacing with GPUs of modern video cards. DRM exposes an API that user space programs
can use to send commands and data to the GPU, and perform operations such as configuring
the mode setting of the display. DRM was first developed as the kernel space component of
the X Server's Direct Rendering Infrastructure,[1] but since then it has been used by other
graphic stack alternatives such as Wayland.

DRM(图形渲染架构)是linux的一个内核子系统,负责GPU的交互接口。

DRM暴露API,用户空间的程序可以发送命令和数据给GPU。

如果没有DRM:

http://odfef978i.bkt.clouddn.com/Free-Converter.com-access_to_video_card_without_drm-85644921.png

使用DRM:

https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Access_to_video_card_with_DRM.svg/1200px-Access_to_video_card_with_DRM.svg.png

查看DRM驱动代码:

root@bf756b445919:~/linux/drivers/gpu/drm/sun4i# ls *.c
sun4i_backend.c  sun4i_dotclock.c  sun4i_framebuffer.c   sun4i_hdmi_enc.c       sun4i_layer.c  sun4i_tcon.c  sun6i_drc.c    sun8i_mixer.c
sun4i_crtc.c     sun4i_drv.c       sun4i_hdmi_ddc_clk.c  sun4i_hdmi_tmds_clk.c  sun4i_rgb.c    sun4i_tv.c    sun8i_layer.c

参考现成的dts:

sun5i-a13-q8-tablet.dts

#include "sun5i-a13.dtsi"
#include "sun5i-reference-design-tablet.dtsi"

/ {
        model = "Q8 A13 Tablet";
        compatible = "allwinner,q8-a13", "allwinner,sun5i-a13";

        panel: panel {
                compatible = "urt,umsh-8596md-t", "simple-panel";
                #address-cells = <1>;
                #size-cells = <0>;

                port@0 {
                        reg = <0>;
                        /* TODO: lcd panel uses axp gpio0 as enable pin */
                        backlight = <&backlight>;
                        #address-cells = <1>;
                        #size-cells = <0>;

                        panel_input: endpoint@0 {
                                reg = <0>;
                                remote-endpoint = <&tcon0_out_lcd>;
                        };
                };
        };
};

&be0 {
        status = "okay";
};

&tcon0 {
        pinctrl-names = "default";
        pinctrl-0 = <&lcd_rgb666_pins>;
        status = "okay";
};

&tcon0_out {
        tcon0_out_lcd: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&panel_input>;
        };
};

这里引用了 compatible = "urt,umsh-8596md-t", "simple-panel";

drivers/gpu/drm/panel/panel-simple.c 里有很多屏幕型号,选取合适的屏幕型号即可

开启DRM还需要使能以下内核

  1. support for simple panels
  2. CMA
  3. DMA_CMA

然后需要失能(注释)掉原来的simplefb在dts中的节点

atmel触摸屏驱动分析

最新代码在:https://github.com/atmel-maxtouch/linux 3847行

从下往上走读:

版权信息

/* Module information */
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
MODULE_LICENSE("GPL");

设备树匹配的名称:

static const struct i2c_device_id mxt_id[] = {
    { "qt602240_ts", 0 },
    { "atmel_mxt_ts", 0 },
    { "atmel_mxt_tp", 0 },
    { "maxtouch", 0 },
    { "mXT224", 0 },
    { }
};

待机模式操作

static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);

#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
const struct dev_pm_ops name = { \
    SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
}

移除操作

static int mxt_remove(struct i2c_client *client)
{
    struct mxt_data *data = i2c_get_clientdata(client);

    sysfs_remove_group(&client->dev.kobj, &mxt_fw_attr_group);
    mxt_debug_msg_remove(data);
    mxt_sysfs_remove(data);

#ifndef __POLL
    if (data->irq)
        free_irq(data->irq, data);
#endif

    regulator_put(data->reg_avdd);
    regulator_put(data->reg_vdd);
    mxt_free_input_device(data);
    mxt_free_object_table(data);
    kfree(data);

    return 0;
}

插入操作

static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct mxt_data *data;
    const struct mxt_platform_data *pdata;
    int error;

    pdata = mxt_get_platform_data(client);  //获取平台数据
    if (IS_ERR(pdata))
        return PTR_ERR(pdata);

    data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
        client->adapter->nr, client->addr);

    data->client = client;
    data->pdata = pdata;
    i2c_set_clientdata(client, data);       //保存数据

    if (data->pdata->cfg_name)      //配置文件名
        mxt_update_file_name(&data->client->dev,
                    &data->cfg_name,
                    data->pdata->cfg_name,
                    strlen(data->pdata->cfg_name));

    init_completion(&data->chg_completion);
    init_completion(&data->reset_completion);
    init_completion(&data->crc_completion);
    mutex_init(&data->debug_msg_lock);

    if (pdata->suspend_mode == MXT_SUSPEND_REGULATOR) {
        __D;
        error = mxt_acquire_irq(data);
        if (error)
            goto err_free_mem;

        error = mxt_probe_regulators(data);
        if (error)
            goto err_free_irq;

        disable_irq(data->irq);
    }

    error = sysfs_create_group(&client->dev.kobj, &mxt_fw_attr_group);
    if (error) {
        dev_err(&client->dev, "Failure %d creating fw sysfs group\n",
            error);
        return error;
    }

    error = mxt_initialize(data);
    if (error)
        goto err_free_irq;

    return 0;

err_free_irq:
    if (data->irq)
        free_irq(data->irq, data);
err_free_mem:
    kfree(data);
    return error;
}

获取平台数据

static const struct mxt_platform_data *
mxt_get_platform_data(struct i2c_client *client)
{
    const struct mxt_platform_data *pdata;

    pdata = dev_get_platdata(&client->dev);  //已经获取过就直接返回
    if (pdata)
        return pdata;

    pdata = mxt_parse_dt(client);           //解析dts//初始化gpio_reset,cfg_name,input_name,gpio-keymap,suspend_mode
    if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
        return pdata;

    pdata = mxt_parse_acpi(client);
    if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
        return pdata;

    pdata = mxt_default_pdata(client);
    if (!IS_ERR(pdata))
        return pdata;

    dev_err(&client->dev, "No platform data specified\n");
    return ERR_PTR(-EINVAL);
}

获取中断

static int mxt_acquire_irq(struct mxt_data *data)
{
    int error;
#ifndef __POLL
    if (!data->irq) {       //没有中断的话申请中断线程
        error = request_threaded_irq(data->client->irq, NULL,
                mxt_interrupt,
                data->pdata->irqflags | IRQF_ONESHOT,
                data->client->name, data);
        if (error) {
            dev_err(&data->client->dev, "Error requesting irq\n");
            return error;
        }

        /* Presence of data->irq means IRQ initialised */
        data->irq = data->client->irq;
    } else {        //存在中断,则使能
        enable_irq(data->irq);
    }
#endif
    if (data->object_table && data->use_retrigen_workaround) {
        error = mxt_process_messages_until_invalid(data);
        if (error)
            return error;
    }

    return 0;
}

中断服务例程

static irqreturn_t mxt_interrupt(int irq, void *dev_id)
{
    struct mxt_data *data = dev_id;

    complete(&data->chg_completion);

    if (data->in_bootloader) {
        if (data->flash && &data->flash->work)
            cancel_delayed_work_sync(&data->flash->work);

        return IRQ_RETVAL(mxt_check_bootloader(data));
    }

    if (!data->object_table)
        return IRQ_HANDLED;

    if (data->T44_address) {        //有T44地址
        return mxt_process_messages_t44(data);
    } else {
        return mxt_process_messages(data);
    }
}

处理消息

static irqreturn_t mxt_process_messages(struct mxt_data *data)
{
    int total_handled, num_handled;
    u8 count = data->last_message_count;

    if (count < 1 || count > data->max_reportid)
        count = 1;

    /* include final invalid message */
    total_handled = mxt_read_and_process_messages(data, count + 1);
    if (total_handled < 0)
        return IRQ_NONE;
    /* if there were invalid messages, then we are done */
    else if (total_handled <= count)
        goto update_count;

    /* keep reading two msgs until one is invalid or reportid limit */
    do {
        num_handled = mxt_read_and_process_messages(data, 2);
        if (num_handled < 0)
            return IRQ_NONE;

        total_handled += num_handled;

        if (num_handled < 2)
            break;
    } while (total_handled < data->num_touchids);

update_count:
    data->last_message_count = total_handled;

    if (data->update_input) {
        mxt_input_sync(data);
        data->update_input = false;
    }

    return IRQ_HANDLED;
}

atmel触摸屏中断改轮询

普通电容式触摸屏是中断方式的,由于v3s引脚较少,所以尝试改为轮询方式驱动,节省一个IO。

static int __devexit mxt_remove(struct i2c_client *client)
#ifndef _TS_POLL
    free_irq(data->irq, data);
#endif
static int __devinit mxt_probe(struct i2c_client *client,
        const struct i2c_device_id *id)
err_free_irq:
#ifndef _TS_POLL
    free_irq(client->irq, data);
#endif

#ifndef _TS_POLL
    error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
            pdata->irqflags, client->dev.driver->name, data);
    if (error) {
        dev_err(&client->dev, "Failed to register interrupt\n");
        goto err_free_object;
    }
    error = input_register_device(input_dev);
    if (error)
        goto err_free_irq;
#else
    {
        struct input_polled_dev *polled_dev;
        polled_dev = devm_input_allocate_polled_device(dev);
        if (!polled_dev) {
            dev_err(dev,
                "Failed to allocate polled input device\n");
            return -ENOMEM;
        }
        polled_dev->private = data;
        polled_dev->poll = mxt_poll;
        polled_dev->poll_interval = POLL_INTERVAL;
        polled_dev->input = input_dev;

        error = input_register_polled_device(polled_dev);
    }
#endif

#ifdef _TS_POLL
#define POLL_INTERVAL 5

static void mxt_poll(struct input_polled_dev *dev)
{
    struct mxt_data *data = dev->private;
    struct mxt_message message;
    struct mxt_object *object;
    struct device *dev = &data->client->dev;
    int id;
    u8 reportid;
    u8 max_reportid;
    u8 min_reportid;

    do {
        if (mxt_read_message(data, &message)) {
            dev_err(dev, "Failed to read message\n");
            goto end;
        }

        reportid = message.reportid;

        /* whether reportid is thing of MXT_TOUCH_MULTI_T9 */
        object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
        if (!object)
            goto end;

        max_reportid = object->max_reportid;
        min_reportid = max_reportid - object->num_report_ids + 1;
        id = reportid - min_reportid;

        if (reportid >= min_reportid && reportid <= max_reportid)
            mxt_input_touchevent(data, &message, id);
        else
            mxt_dump_message(dev, &message);
    } while (reportid != 0xff);

end:
    return;
}
#endif
#ifndef _FLIP_X
    input_report_abs(input_dev, ABS_MT_POSITION_X, finger[id].x);
    #else
    input_report_abs(input_dev, ABS_MT_POSITION_X, 800-finger[id].x);
    #end
    #ifndef _FLIP_Y
    input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[id].y);
    #else
    input_report_abs(input_dev, ABS_MT_POSITION_Y, 480-finger[id].y);
    #endif

看门狗的使用

本文目录

echo 0 >/dev/watchdog0
echo V >/dev/watchdog0

设置和获得超时值:

对于某些驱动来说,在上层使用SETTIMEOUT ioctl命令改变watchdog的超时值是可能的,那些驱动在他们的选项与中有WDIOF_SETTIMEOUT标志。参数是一个代表以秒为单位的超时值,驱动将在同一个变量中返回实际使用的超时值,这个超时值可能由于硬件的限制,而不同于所请求的超时值

int timeout = 45;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
printf("The timeout was set to %d seconds\n", timeout);

如果设备的超时值的粒度只能到分钟,则这个例子可能实际打印"The timeout was set to 60 seconds"。

自从Linux 2.4.18内核,通过GETTIMEOUT ioctl命令查询当前超时值也是可能的:

ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
printf("The timeout was is %d seconds\n", timeout);

点屏之RGB屏

Zero默认支持800x480和480x272这两种常见分辨率的的RGB屏幕。

这两种分辨率的屏幕,直接在编译时候选择对应的分辨率即可。

Zero还可以接RGB2VGA小板或者RGB2LVDS小板来驱动VGA液晶屏或者LVDS屏幕,这时候就需要自己改动屏幕参数了。

Uboot屏幕参数

修改FB大小

FB大小为 分辨率x4:

800x480x4=1.5M 800x600x4=1.8M 1024x600x4=2.4M 1024x768x4=3M 1024x1024x4=4M

默认uboot里预留了2M的FB,对于1024x600以上的屏幕无法显示。

需要修改 u-boot/include/configs/sunxi-common.h 文件

296 #define CONFIG_SUNXI_MAX_FB_SIZE (2 << 20)

改为

296 #define CONFIG_SUNXI_MAX_FB_SIZE (3 << 20)

增加时序文件

默认配置文件在u-boot/configs/LicheePi_Zero_800x480LCD_defconfig等,可以根据自己的需要来新增文件,比如:

u-boot/configs/LicheePi_Zero_1024x768LCD_defconfig

7CONFIG_VIDEO_LCD_MODE="x:800,y:480,depth:18,pclk_khz:33000,le:87,ri:40,up:31,lo:13,hs:1,vs:1,sync:3,vmode:0"

改为

CONFIG_VIDEO_LCD_MODE="x:1024,y:768,depth:24,pclk_khz:32000,le:198,ri:120,up:21,lo:821,hs:2,vs:2,sync:3,vmode:0"

(时钟太高>60M貌似会hang?)

这里有个小脚本可以把fex文件的时序转换成uboot的时序:

#!/usr/bin/env ruby

if !ARGV[0] || !File.exists?(ARGV[0]) then
abort "Usage: ruby #{__FILE__} [fex_file_name]\n"
end

def parse_fex_section(filename, section)
results = {}
current_section = ""
File.open(filename).each_line {|l|
    current_section = $1 if l =~ /^\[(.*?)\]/
    next if current_section != section
    results[$1] = $2.strip if l =~ /^(\S+)\s*\=\s*(.*)/
    results[$1] = $2.to_i if l =~ /^(\S+)\s*\=\s*(\d+)\s*$/
}
return results
end

def print_video_lcd_mode(lcd0_para, vt_div)
x        = lcd0_para["lcd_x"]
y        = lcd0_para["lcd_y"]
depth    = { 0 => 24, 1 => 18 }[lcd0_para["lcd_frm"]]
pclk_khz = lcd0_para["lcd_dclk_freq"] * 1000
hs       = [1, (lcd0_para["lcd_hv_hspw"] || lcd0_para["lcd_hspw"])].max
vs       = [1, (lcd0_para["lcd_hv_vspw"] || lcd0_para["lcd_vspw"])].max
le       = lcd0_para["lcd_hbp"] - hs
ri       = lcd0_para["lcd_ht"] - x - lcd0_para["lcd_hbp"]
up       = lcd0_para["lcd_vbp"] - vs
lo       = lcd0_para["lcd_vt"] / vt_div - y - lcd0_para["lcd_vbp"]

abort "Unsupported 'lcd_frm' parameter" if !depth

printf("CONFIG_VIDEO_LCD_MODE=\"" +
        "x:#{x},y:#{y},depth:#{depth},pclk_khz:#{pclk_khz}," +
        "le:#{le},ri:#{ri},up:#{up},lo:#{lo},hs:#{hs},vs:#{vs}," +
        "sync:3,vmode:0\"\n")
end

lcd0_para = parse_fex_section(ARGV[0], "lcd0_para")
abort "Not a valid 'lcd0_para' section" if lcd0_para["lcd_used"] != 1

printf("== for sun[457]i ==\n")
print_video_lcd_mode(lcd0_para, 2)

printf("\n== for sun[68]i ==\n")
print_video_lcd_mode(lcd0_para, 1)

点屏之SPI屏

荔枝派配套的5寸LCD尺寸比较大,如果需要较小的屏幕,可以选择SPI屏。

目前最新的4.1x内核均包含了市面上常见的SPI液晶屏的驱动(fbtft),我们所要做的仅仅是在设备树中添加节点。

不过目前为止fbtft并未转正,依然存放在 drivers/staging 目录中。

配置内核添加fbtft驱动

使用make menuconfig配置内核,加入ili9341驱动。fbtft还支持更多型号的SPI总线的液晶屏。关于支持列表这里就不一一列出,可以进入menuconfig中查看。

Device Drivers --->
[] Staging drivers --->
<> Support for small TFT LCD display modules --->
<> FB driver for the ILI9341 LCD Controller
<> Generic FB driver for TFT LCD displays

修改设备树注册ili9341

设备树有包含和覆盖特性,所以我们可以在不修改默认配置文件的情况下,新增我们的板子的修改。

新建 arch/arm/boot/dts/sun8i-v3s-licheepi-zero-spitft.dts

里面加入如下内容,一个就是 删除 原来的simplefb节点(uboot里有使能fb的操作,必须删除而不是disable才行)

二就是增加ili9341 挂载在spi0上。这样fbtft驱动在加载的时候就会自动找到这个节点,挂载驱动,显示。

//这里spi速率使用了50M,超出9341的手册范围,但实际测试可以使用。

注意这里删除后,uboot仍然会初始化RGB的驱动,只是内核会使用spi显示。

如果需要完全去除RGB上的显示,需要在uboot里关闭显示。

/dts-v1/;
#include "sun8i-v3s-licheepi-zero.dts"

/{
        chosen {
                /delete-node/ framebuffer@0;
        };
};

&spi0 {
    status = "okay";

    ili9341@0 {
            compatible = "ilitek,ili9341";
            reg = <0>;

            spi-max-frequency = <50000000>;
            rotate = <270>;
            bgr;
            fps = <30>;
            buswidth = <8>;
            reset-gpios = <&pio 1 7 GPIO_ACTIVE_LOW>;
            dc-gpios = <&pio 1 5 GPIO_ACTIVE_LOW>;
            debug = <0>;
    };
};

修改好保存后,在dtsMakefile里加入编译对象:sun8i-v3s-licheepi-zero-spitft.dtb

注:

dc-gpios = <&pio 1 5 GPIO_ACTIVE_LOW>;

在设备树中,PA对应&pio 0, PB对应&pio 1, 以此类推。因此dc-gpios实际表示的是PB5,也就是zero丝印上的PWM1。

reset-gpios = <&pio 1 7 GPIO_ACTIVE_LOW>;

如果我的屏幕的RESET引脚连接了高电平,或者接了一个RC回路作为上电复位的信号,那么这里的复位引脚是不是可以不指定呢?

这样也是不可以的。因为在程序中,首先读取reset-gpios,若reset-gpios在设备树中不存在,那么直接忽略其余的信号。这样导致无法控制最关键的dc-gpios引脚。因此至少在不更改程序的前提下,这条信号是一定要写上的。

static int fbtft_request_gpios_dt(struct fbtft_par *par)
{
    int i;
    int ret;

    if (!par->info->device->of_node)
        return -EINVAL;

    ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset);
    if (ret)
        return ret;
    ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc);
    if (ret)
        return ret;

烧写运行后,可以在 /sys/firmware/devicetree 下查看实际的设备树

硬件连接

SPI屏 zero
3v3 3v3
GND GND
DC PWM1
RST 3v3
CS CS
CLK CLK
MISO MISO
MOSI MOSI

上电启动

linux内核启动时会加载fbtft驱动,注册framebuffer设备,打印如下信息:

[    1.131698] fbtft_of_value: buswidth = 8
[    1.135680] fbtft_of_value: debug = 0
[    1.139345] fbtft_of_value: rotate = 270
[    1.143268] fbtft_of_value: fps = 30
[    1.192746] mmc0: host does not support reading read-only switch, assuming write-enable
[    1.202997] mmc0: new high speed SD card at address b368
[    1.208984] mmcblk0: mmc0:b368 UD    968 MiB
[    1.214696]  mmcblk0: p1 p2
[    1.439491] Console: switching to colour frame buffer device 40x30
[    1.447560] graphics fb0: fb_ili9341 frame buffer, 320x240, 150 KiB video memory, 16 KiB buffer memory, fps=33, spi0.0 at 50 MHz

说明帧率在33帧左右,适合常见场合。

显示效果如下图:

https://box.kancloud.cn/8eff79352c83073950b62b690fe7b5cc_1443x1080.jpg

使用mplayer播放视频效果如下, 还是很流畅的。

http://v.youku.com/v_show/id_XMzM3NjY0NTU3Ng==.html

系统占用如下:

PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
105    98 root     T    19692  35%   0% mplayer dcp1.mp4

开机自启动

buildroot 根文件系统

修改/etc/inittab:

ttyS0::respawn:/root/logintest -L ttyS0 115200 vt100

新建logintest:

#!/bin/sh
/bin/login -f root

自启动任务在 /etc/init.d/rcS 中加入即可

export 相关环境变量在 /etc/profile 中加入。

开机免登陆

修改mingetty:

http://www.filewatcher.com/m/mingetty-1.07.tar.gz.13435-0.html

修改mingetty.c中的代码
 第352行的char *logname, *s;
 更改为char *logname = "root", *s;
 把第409-415行注释
 /* if (autologin) {
 do_prompt (0);
 printf ("login: %s (automatic login)\n", autologin);
 logname = autologin;
 } else
 while ((logname = get_logname ()) == 0)
 /* do nothing */ /*; */

修改makefile

CC=arm-none-linux-gnueabi-gcc //本机编译则不用 编译生成mingetty 将生成的mingetty程序拷贝到根文件系统的/sbin目录下

修改login

http://www.filewatcher.com/_/?q=util-linux-2.13-pre7.tar.bz2

修改login.c中的代码

把344行的passwd_req = 1;
更改为passwd_req = 0;
在文件中添加locale.h头文件
#include <locale.h>

编译生成login

gcc -o login login.c ../lib/setproctitle.c checktty.c -Wall -lcrypt -I ../include/

更新login

将生成的login 程序拷贝到根文件系统的/bin目录下,结束。

修改inittab文件

将跟文件系统/etc/inittab文件中的最后的登录语句

S2:2345:respawn:/sbin/mingetty ttyS0

将修改后的根文件系统重新下载到系统中,系统启动时就会直接登录。不需要输入用户名及密码了。

开机屏幕不显示log

boot.scr 中去掉tty0即可

Segment Fault调试

在调试linux程序时经常会出现段错误,这里介绍常规的段错误定位方法,即使用core dump文件。

配置core dump

先设置允许的core dump文件大小。

echo "ulimit -c 102400" >> /etc/profile             #ulimit -c unlimited 可以设置为无限
source /etc/profile         #生效

使用 ulimit -c 来查看当前的core文件大小。

配置core 文件名

/proc/sys/kernel/core_uses_pid 可以控制产生的 core 文件的文件名中是否添加 pid 作为扩展 ,如果添加则文件内容为 1 ,否则为 0

配置core保存位置

core保存位置默认为当前目录下core名字

proc/sys/kernel/core_pattern 可以设置格式化的 core 文件保存位置或文件名 ,比如原来文件内容是 core-%e

可以这样修改 :

echo "/corefile/core-%e-%p-%t" > core_pattern

将会控制所产生的 core 文件会存放到 /corefile 目录下,产生的文件名为 core- 命令名 -pid- 时间戳

以下是参数列表 :

%p - insert pid into filename 添加 pid
%u - insert current uid into filename 添加当前 uid
%g - insert current gid into filename 添加当前 gid
%s - insert signal that caused the coredump into the filename 添加导致产生 core 的信号
%t - insert UNIX time that the coredump occurred into filename 添加 core 文件生成时的 unix 时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加命令名

Zero通过otg与PC共享网络

在内核选项中勾选上:composite gadget: Serial and Ethernet. 就可以让Zero与PC通过usb共享网络。

确认usb虚拟网口被使能

使用usb线连接Zero和PC,在Zero和PC上查看网络接口:

zp@ubuntu64:~$ ifconfig
...
usb0 Link encap:Ethernet HWaddr 66:36:e9:13:fd:44

root@Lichee:~# ifconfig
...
usb0 Link encap:Ethernet HWaddr 2e:cf:e1:3f:ad:61

确认有usb0接口后,手工设置两者在同一网段下:

on PC: sudo ifconfig usb0 192.168.2.1
on Zero: sudo ifconfig usb0 192.168.2.100
Test PC ping Zero:

zp@ubuntu64:~$ ping 192.168.2.100
PING 192.168.2.100 (192.168.2.100) 56(84) bytes of data.
64 bytes from 192.168.2.100: icmp_seq=1 ttl=64 time=2.74 ms
64 bytes from 192.168.2.100: icmp_seq=2 ttl=64 time=2.19 ms
...
Everything is ok now, let's edit network config(/etc/network/interfaces) to save it:
On PC add:
allow-hotplug usb0
auto usb0
iface usb0 inet static
address 192.168.2.1
netmask 255.255.255.0
On Zero add:
allow-hotplug usb0
auto usb0
iface usb0 inet static
address 192.168.2.100
netmask 255.255.255.0
gateway 192.168.2.1
Share Network from PC
Enable forwarding on your PC:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward > /dev/null
sudo iptables -P FORWARD ACCEPT
sudo iptables -A POSTROUTING -t nat -j MASQUERADE -s 192.168.2.0/24
test Ping google (if you are in china, ping baidu.com please...)
ping google.com

If everything goes ok, your Zero is online now~

You can ssh to Zero on your PC or any PC in the local net.

zp@ubuntu64:~$ ssh root@192.168.2.100
root@192.168.2.100's password:
Linux Lichee 4.10.2-licheepi-zero+ #12 SMP Wed Mar 15 23:22:13 CST 2017 armv7l
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed Feb 15 23:39:33 2017 from 192.168.2.1
root@Lichee:~#
And you can execute any command on Zero via ssh.
Zero is used as a "headless" board now.

USB摄像头使用

内核选项

插入USB摄像头,lsusb 可见:(摄像头PID:VID视你插入的USB摄像头型号而定)

Bus 001 Device 003: ID 1908:2311 GEMBIRD

在dev下可见 video0 设备。

fswebcam

fswebcam可以用来抓取摄像头图片。可以通过apt-get直接安装。

fswebcam -d /dev/video0 --no-banner -r 320x240 capture.jpg

抓取一帧图片。

root@LicheePi:~# fswebcam --help
Usage: fswebcam [<options>] <filename> [[<options>] <filename> ... ]

Options:

-?, --help                   Display this help page and exit.
-c, --config <filename>      Load configuration from file.
-q, --quiet                  Hides all messages except for errors.
-v, --verbose                Displays extra messages while capturing
    --version                Displays the version and exits.
-l, --loop <seconds>         Run in loop mode.
-b, --background             Run in the background.
-o, --output <filename>      Output the log to a file.
-d, --device <name>          Sets the source to use.
-i, --input <number/name>    Selects the input to use.
-t, --tuner <number>         Selects the tuner to use.
-f, --frequency <number>     Selects the frequency use.
-p, --palette <name>         Selects the palette format to use.
-D, --delay <number>         Sets the pre-capture delay time. (seconds)
-r, --resolution <size>      Sets the capture resolution.
    --fps <framerate>        Sets the capture frame rate.
-F, --frames <number>        Sets the number of frames to capture.
-S, --skip <number>          Sets the number of frames to skip.
    --dumpframe <filename>   Dump a raw frame to file.
-s, --set <name>=<value>     Sets a control value.
    --revert                 Restores original captured image.
    --flip <direction>       Flips the image. (h, v)
    --crop <size>[,<offset>] Crop a part of the image.
    --scale <size>           Scales the image.
    --rotate <angle>         Rotates the image in right angles.
    --deinterlace            Reduces interlace artifacts.
    --invert                 Inverts the images colours.
    --greyscale              Removes colour from the image.
    --swapchannels <c1c2>    Swap channels c1 and c2.
    --no-banner              Hides the banner.
    --top-banner             Puts the banner at the top.
    --bottom-banner          Puts the banner at the bottom. (Default)
    --banner-colour <colour> Sets the banner colour. (#AARRGGBB)
    --line-colour <colour>   Sets the banner line colour.
    --text-colour <colour>   Sets the text colour.
    --font <[name][:size]>   Sets the font and/or size.
    --no-shadow              Disables the text shadow.
    --shadow                 Enables the text shadow.
    --title <text>           Sets the main title. (top left)
    --no-title               Clears the main title.
    --subtitle <text>        Sets the sub-title. (bottom left)
    --no-subtitle            Clears the sub-title.
    --timestamp <format>     Sets the timestamp format. (top right)
    --no-timestamp           Clears the timestamp.
    --gmt                    Use GMT instead of local timezone.
    --info <text>            Sets the info text. (bottom right)
    --no-info                Clears the info text.
    --underlay <PNG image>   Sets the underlay image.
    --no-underlay            Clears the underlay.
    --overlay <PNG image>    Sets the overlay image.
    --no-overlay             Clears the overlay.
    --jpeg <factor>          Outputs a JPEG image. (-1, 0 - 95)
    --png <factor>           Outputs a PNG image. (-1, 0 - 10)
    --save <filename>        Save image to file.
    --exec <command>         Execute a command and wait for it to complete.

mjpeg-streamer

前置软件:

sudo apt-get update
sudo apt-get install g++ libjpeg62-turbo-dev imagemagick libv4l-dev cmake git
sudo git clone https://github.com/jacksonliam/mjpg-streamer.git
cd mjpg-streamer/mjpg-streamer-experimental
make all
sudo make install

设置环境变量

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/mjpg-streamer

开启web服务器

mjpg_streamer -i "input_uvc.so -d /dev/video0 -r 640x480" -o "output_http.so -p 8080 -w /usr/local/share/mjpg-streamer/www"

然后在同个局域网内的电脑的浏览器上访问 zero ip:8080即可看到图像。

mjpg_streamer -i "input_uvc.so -f 10 -r 320*240 -y" -o "output_http.so -c "ruoyun:liufeng" -w www -p 8888" -o "output_file.so -d 1000 -f /mnt "
-i 输入
"input_uvc.so -f 10 -r 320*240 -y"
input_uvc.so:UVC输入组件
-f  10             :表示10帧
-r 320*240     :分辨率
-y                   :YUV格式输入(有卡顿),不加表示MJPG输入(需要摄像头支持)

-o输出
    "output_http.so -c "ruoyun:liufeng" -w www -p 8888"
    output_http.so          :网页输出组件
    -c "ruoyun:liufeng"       :用户名:ruoyun        密码:liufeng
    -w www                                  : 网页输出
    -p 8888                                   :端口   8888

    "output_file.so -d 1000 -f /mnt "
    output_file.so                  :图片输出组件
    -d 1000                                   : 时间1S
    -f /mnt                                       :输出图片放在哪,我是开机直接把/mnu挂载到本地文件夹了

总体帮助

root@LicheePi:~# mjpg_streamer --help
-----------------------------------------------------------------------
Usage: mjpg_streamer
-i | --input "<input-plugin.so> [parameters]"
-o | --output "<output-plugin.so> [parameters]"
[-h | --help ]........: display this help
[-v | --version ].....: display version information
[-b | --background]...: fork to the background, daemon mode
-----------------------------------------------------------------------
Example #1:
To open an UVC webcam "/dev/video1" and stream it via HTTP:
mjpg_streamer -i "input_uvc.so -d /dev/video1" -o "output_http.so"
-----------------------------------------------------------------------
Example #2:
To open an UVC webcam and stream via HTTP port 8090:
mjpg_streamer -i "input_uvc.so" -o "output_http.so -p 8090"
-----------------------------------------------------------------------
Example #3:
To get help for a certain input plugin:
mjpg_streamer -i "input_uvc.so --help"
-----------------------------------------------------------------------
In case the modules (=plugins) can not be found:
* Set the default search path for the modules with:
export LD_LIBRARY_PATH=/path/to/plugins,
* or put the plugins into the "/lib/" or "/usr/lib" folder,
* or instead of just providing the plugin file name, use a complete
path and filename:
mjpg_streamer -i "/path/to/modules/input_uvc.so"
-----------------------------------------------------------------------

输入插件帮助

root@LicheePi:~# mjpg_streamer -i "input_uvc.so --help"
MJPG Streamer Version.: 2.0
---------------------------------------------------------------
Help for input plugin..: UVC webcam grabber
---------------------------------------------------------------
The following parameters can be passed to this plugin:

[-d | --device ].......: video device to open (your camera)
[-r | --resolution ]...: the resolution of the video device,
                        can be one of the following strings:
                        QQVGA QCIF CGA QVGA CIF PAL
                        VGA SVGA XGA HD SXGA UXGA
                        FHD
                        or a custom value like the following
                        example: 640x480
[-f | --fps ]..........: frames per second
                        (activates YUYV format, disables MJPEG)
[-q | --quality ] .....: set quality of JPEG encoding
[-m | --minimum_size ].: drop frames smaller then this limit, useful
                        if the webcam produces small-sized garbage frames
                        may happen under low light conditions
[-e | --every_frame ]..: drop all frames except numbered
[-n | --no_dynctrl ]...: do not initalize dynctrls of Linux-UVC driver
[-l | --led ]..........: switch the LED "on", "off", let it "blink" or leave
                        it up to the driver using the value "auto"
[-t | --tvnorm ] ......: set TV-Norm pal, ntsc or secam
[-u | --uyvy ] ........: Use UYVY format, default: MJPEG (uses more cpu power)
[-y | --yuv  ] ........: Use YUV format, default: MJPEG (uses more cpu power)
[-fourcc ] ............: Use FOURCC codec 'argopt',
                        currently supported codecs are: RGBP
---------------------------------------------------------------

Optional parameters (may not be supported by all cameras):

[-br ].................: Set image brightness (auto or integer)
[-co ].................: Set image contrast (integer)
[-sh ].................: Set image sharpness (integer)
[-sa ].................: Set image saturation (integer)
[-cb ].................: Set color balance (auto or integer)
[-wb ].................: Set white balance (auto or integer)
[-ex ].................: Set exposure (auto, shutter-priority, aperature-priority, or integer)
[-bk ].................: Set backlight compensation (integer)
[-rot ]................: Set image rotation (0-359)
[-hf ].................: Set horizontal flip (true/false)
[-vf ].................: Set vertical flip (true/false)
[-pl ].................: Set power line filter (disabled, 50hz, 60hz, auto)
[-gain ]...............: Set gain (auto or integer)
[-cagc ]...............: Set chroma gain control (auto or integer)
---------------------------------------------------------------

input_init() return value signals to exit

输出插件帮助

root@LicheePi:~# mjpg_streamer -o "output_http.so --help"
MJPG Streamer Version.: 2.0
---------------------------------------------------------------
Help for output plugin..: HTTP output plugin
---------------------------------------------------------------
The following parameters can be passed to this plugin:

[-w | --www ]...........: folder that contains webpages in
                        flat hierarchy (no subfolders)
[-p | --port ]..........: TCP port for this HTTP server
[-l ] --listen ]........: Listen on Hostname / IP
[-c | --credentials ]...: ask for "username:password" on connect
[-n | --nocommands ]....: disable execution of commands
---------------------------------------------------------------
output_init() return value signals to exit

基于QT的GUI开发

本节介绍各类QT操作

移植tslib

tslib是电阻式触摸屏用于校准的一个软件库,是一个开源的程序,能够为触摸屏驱动获得的采样提供诸如滤波、去抖、校准等功能,通常作为触摸屏驱动的适配层,为上层的应用提供了一个统一的接口。 所以这里先编译安装tslib,这样在后面编译Qt的时候才能打包编译进去。

下载编译tslib

sudo apt-get install libtool automake autogen autoconf libsysfs-dev
git clone https://github.com/kergoth/tslib.git
cd tslib
echo  "ac_cv_func_malloc_0_nonnull=yes"  > tmp.cache
./autogen.sh
./configure --host=arm-linux-gnueabihf --cache-file=tmp.cache   --prefix=/opt/tslib CC=/opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc
make
sudo make install

完成后tslib就被安装到了/opt/tslib目录下

移植QT5.9.1

移植完成tslib之后,就可以进行QT本体的移植了,目前最新版本为5.9.1。

注意QT本体编译时间较长,笔记本上会耗时2~3小时,我使用的32线程服务器的编译时间为9分钟左右。

下载QT5.9.1

wget http://download.qt.io/official_releases/qt/5.9/5.9.1/single/qt-everywhere-opensource-src-5.9.1.tar.xz
tar xf qt-everywhere-opensource-src-5.9.1.tar.xz    #这里解压也需要30s左右,别以为死机了。。
cd qt-everywhere-opensource-src-5.9.1
./configure --help >help.txt   #QT的配置较为复杂,进来先看看帮助文档,在本节最后附录里有翻译

一般来说,我们需要编译主机和目标板两个版本的qt:

主机版可以用于前期的gui的设计调试;目标板用于实际产品的验证。

编译X11版本

主机版一般是X11,配置如下:

cfg_X11.sh
 #!/bin/bash
 ./configure -prefix /opt/qt-5.9.1-x11 -opensource -make tools
 #安装位置,开源版本,编译qt工具(makeqpf,qtconfig)
 make -j32
 sudo make install

运行该脚本,首次配置用时1分钟左右。编译一次参考时间:

real        9m17.309s
user        206m59.052s
sys 17m30.300s

所以单核虚拟机编译需要三小时左右。

编译完成后install也需要1分钟左右。

安装完成后在/opt/qt-5.9.1-x11下可见安装的文件。

交叉编译arm版本

注意,在第二次编译前,先 make clean 下。如果编译时候仍有错误,可以重新解压编译。

交叉编译,需要配置 xplatform选项,比如要在arm-linux平台上移植Qt的话,就在配置项中加上 -xplatform linux-arm-gnueabi-g++ ,这个是平台名字,Qt5支持的交叉平台都可在源码顶层目录中的 /qtbase/mkspecs/ 下找到。

首先我们需要编辑 qtbase/mkspecs/linux-arm-gnueabi-g++/qmake.conf

加上:(注意,和tslib类似,这里要加上其它什么支持的话,也是交叉编译的库的路径)

QT_QPA_DEFAULT_PLATFORM = linuxfb
QMAKE_CFLAGS_RELEASE += -O2 -march=armv7-a -lts
QMAKE_CXXFLAGS_RELEASE += -O2 -march=armv7-a -lts
QMAKE_INCDIR += /opt/tslib/include /opt/sqlite3/include
QMAKE_LIBDIR += /opt/tslib/lib /opt/sqlite3/lib

arm-linux-gnueabihf-gcc 改成 arm-linux-gnueabi-gcc 等。

然后再编辑配置脚本cfg_arm.sh

#!/bin/sh
./configure -verbose \
-prefix /opt/qt5.9.1-arm \
-confirm-license \
-opensource \
-release \
-make libs \
-xplatform linux-arm-gnueabi-g++ \
-optimized-qmake \
-pch \
-sql-sqlite -sqlite \
-qt-libjpeg \
-qt-libpng \
-qt-zlib \
-tslib \
-no-opengl \
-no-sse2 \
-no-openssl \
-no-cups \
-no-glib \
-no-dbus \
-no-xcb \
-no-separate-debug-info \
-I/opt/tslib/include -L/opt/tslib/lib \
-make examples -make tools -nomake tests -no-iconv
make -j32
sudo make install

完成后,相关文件在 /opt/qt5.9.1-arm 下。

向开发板添加Qt库

首先将 /opt/qt5.9.1-arm/opt/tslib 复制到开发板的对应目录下

然后设置开发板 Qt 环境变量, vi /etc/bash.bashrc

添加下面内容:

export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
export TSLIB_TSDEVICE=/dev/input/event1
export TSLIB_CONFFILE=/opt/tslib/etc/ts.conf
export TSLIB_PLUGINDIR=/opt/tslib/lib/ts
export TSLIB_CALIBFILE=/etc/pointercal
export LD_LIBRARY_PATH=/lib:/usr/lib:/opt/tslib/lib:/opt/qt5.9.1-arm/lib
export PATH=/bin:/sbin:/usr/bin/:/usr/sbin:/opt/tslib/bin
export QT_QPA_PLATFORM_PLUGIN_PATH=/opt/qt5.9.1-arm/plugins
export QT_QPA_PLATFORM=linuxfb:tty=/dev/fb0
export QT_QPA_FONTDIR=/opt/qt5.9.1-arm/lib/fonts
export QT_QPA_GENERIC_PLUGINS=tslib:$TSLIB_TSDEVICE

保存后生效上述内容: source /etc/bash.bashrc

附录:QT配置帮助文件 翻译

Usage: configure [options] [assignments]

在命令行使用VAR=value来配置变量。

每个大写的库名(用-list-libraries可以查看),支持这些后缀:

_INCDIR, _LIBDIR, _PREFIX (INCDIR=PREFIX/include, LIBDIR=PREFIX/lib),
_LIBS, and - on Windows and Darwin - _LIBS_DEBUG and _LIBS_RELEASE. E.g.,
ICU_PREFIX=/opt/icu42 ICU_LIBS="-licui18n -licuuc -licudata".

同时支持操作 QMAKE_* 变量,来修改mkspec里指定的值,比如QMAKE_CXXFLAGS+=-g3.

顶层安装路径:

-prefix ...... 目标路径(在开发板上的绝对路径)
[如果使能了developer-build,就是/usr/local/Qt-$QT_VERSION, $PWD]
-extprefix ... 安装路径(在主机上的路径,比prefix更多一个前置路径),可不写
[SYSROOT/PREFIX]
-hostprefix [dir] .. The installation directory for build tools running on
the host machine. If [dir] is not given, the current
build directory will be used. [EXTPREFIX]
-external-hostbindir ... Path to Qt tools built for this machine.
Use this when -platform does not match the current
system, i.e., to make a Canadian Cross Build.

微调安装路径的分布 ,注意除了-sysconfdir外的所有目录需要在 -prefix/-hostprefix 下。

(这里基本可以不配置)

-bindir ......... Executables [PREFIX/bin]
-headerdir ...... Header files [PREFIX/include]
-libdir ......... Libraries [PREFIX/lib]
-archdatadir .... Arch-dependent data [PREFIX]
-plugindir ...... Plugins [ARCHDATADIR/plugins]
-libexecdir ..... Helper programs [ARCHDATADIR/bin on Windows,
ARCHDATADIR/libexec otherwise]
-importdir ...... QML1 imports [ARCHDATADIR/imports]
-qmldir ......... QML2 imports [ARCHDATADIR/qml]
-datadir ........ Arch-independent data [PREFIX]
-docdir ......... Documentation [DATADIR/doc]
-translationdir . Translations [DATADIR/translations]
-sysconfdir ..... Settings used by Qt programs [PREFIX/etc/xdg]
-examplesdir .... Examples [PREFIX/examples]
-testsdir ....... Tests [PREFIX/tests]

-hostbindir ..... Host executables [HOSTPREFIX/bin]
-hostlibdir ..... Host libraries [HOSTPREFIX/lib]
-hostdatadir .... Data used by qmake [HOSTPREFIX]

对剩余的选项的约定:

当一个选项描述符在一系列方括号内的变量之后,这个选项的解释是:
空选项表示yes; 所有其他值是可能的前缀,比如-no-gui.
Values are listed in the order they are tried if not specified;
'auto' 是 'yes/no'的简写. 单个 'yes' 和 'no' 表示没有自动检测的二进制选项。

可配置的meta:

-help, -h ............ Display this help screen
-verbose, -v ......... Print verbose messages during configuration
-continue ............ Continue configure despite errors
-redo ................ Re-configure with previously used options.
Additional options may be passed, but will not be
saved for later use by -redo.
-recheck ............. Discard cached negative configure test results.
Use this after installing missing dependencies.
-recheck-all ......... Discard all cached configure test results.
-feature- ... Enable
-no-feature- Disable [none]
-list-features ....... List available features. Note that some features
have dedicated command line options as well.

-list-libraries ...... List possible external dependencies.

构建选项:

-opensource .......... Build the Open-Source Edition of Qt
-commercial .......... Build the Commercial Edition of Qt
-confirm-license ..... Automatically acknowledge the license
-release ............. Build Qt with debugging turned off [yes]
-debug ............... Build Qt with debugging turned on [no]
-debug-and-release ... Build two versions of Qt, with and without
debugging turned on [yes] (Apple and Windows only)
-optimize-debug ...... Enable debug-friendly optimizations in debug builds
[auto] (Not supported with MSVC)
-optimize-size ....... Optimize release builds for size instead of speed [no]
-optimized-tools ..... Build optimized host tools even in debug build [no]
-force-debug-info .... Create symbol files for release builds [no]
-separate-debug-info . Split off debug information to separate files [no]
-strip ............... Strip release binaries of unneeded symbols [yes]
-force-asserts ....... Enable Q_ASSERT even in release builds [no]
-developer-build ..... Compile and link Qt for developing Qt itself
(exports for auto-tests, extra checks, etc.) [no]
-shared .............. Build shared Qt libraries [yes] (no for UIKit)
-static .............. Build static Qt libraries [no] (yes for UIKit)
-framework ........... Build Qt framework bundles [yes] (Apple only)
-platform ... Select host mkspec [detected]
-xplatform .. Select target mkspec when cross-compiling [PLATFORM]
-device ....... Cross-compile for device
-device-option <key=value> ... Add option for the device mkspec

-appstore-compliant .. Disable code that is not allowed in platform app stores.
This is on by default for platforms which require distribution
through an app store by default, in particular Android,
iOS, tvOS, watchOS, and Universal Windows Platform. [auto]
-qtnamespace .. Wrap all Qt library code in 'namespace {...}'.
-qtlibinfix .. Rename all libQt5*.so to libQt5*.so.

-testcocoon .......... Instrument with the TestCocoon code coverage tool [no]
-gcov ................ Instrument with the GCov code coverage tool [no]
-sanitize {address|thread|memory|undefined}
Instrument with the specified compiler sanitizer.
-c++std .... Select C++ standard [c++1z/c++14/c++11]
(Not supported with MSVC)

-sse2 ................ Use SSE2 instructions [auto]
-sse3/-ssse3/-sse4.1/-sse4.2/-avx/-avx2/-avx512
Enable use of particular x86 instructions [auto]
Enabled ones are still subject to runtime detection.
-mips_dsp/-mips_dspr2 Use MIPS DSP/rev2 instructions [auto]
-qreal ........ typedef qreal to the specified type. [double]
Note: this affects binary compatibility.

-R .......... Add an explicit runtime library path to the Qt
libraries. Supports paths relative to LIBDIR.
-rpath ............... Link Qt libraries and executables using the library
install path as a runtime library path. Similar to
-R LIBDIR. On Apple platforms, disabling this implies
using absolute install names (based in LIBDIR) for
dynamic libraries and frameworks. [auto]

-reduce-exports ...... Reduce amount of exported symbols [auto]
-reduce-relocations .. Reduce amount of relocations [auto] (Unix only)
-plugin-manifests .... Embed manifests into plugins [no] (Windows only)
-static-runtime ...... With -static, use static runtime [no] (Windows only)
-pch ................. Use precompiled headers [auto]
-ltcg ................ Use Link Time Code Generation [no]
-use-gold-linker ..... Use the GNU gold linker [auto]
-incredibuild-xge .... Use the IncrediBuild XGE [no] (Windows only)
-make-tool .... Use to build qmake [nmake] (Windows only)
-mp .................. Use multiple processors for compilation (MSVC only)

-warnings-are-errors . Treat warnings as errors [no; yes if -developer-build]
-silent .............. Reduce the build output so that warnings and errors
can be seen more easily

构建环境:

-sysroot ....... Set as the target sysroot
-gcc-sysroot ......... With -sysroot, pass --sysroot to the compiler [yes]

-pkg-config .......... Use pkg-config [auto] (Unix only)

-D .......... Pass additional preprocessor define
-I .......... Pass additional include path
-L .......... Pass additional library path
-F .......... Pass additional framework path (Apple only)

-sdk ........... Build Qt using Apple provided SDK . The argument
should be one of the available SDKs as listed by
'xcodebuild -showsdks'.
Note that the argument applies only to Qt libraries
and applications built using the target mkspec - not
host tools such as qmake, moc, rcc, etc.

-android-sdk path .... Set Android SDK root path [$ANDROID_SDK_ROOT]
-android-ndk path .... Set Android NDK root path [$ANDROID_NDK_ROOT]
-android-ndk-platform Set Android platform
-android-ndk-host .... Set Android NDK host (linux-x86, linux-x86_64, etc.)
[$ANDROID_NDK_HOST]
-android-arch ........ Set Android architecture (armeabi, armeabi-v7a,
arm64-v8a, x86, x86_64, mips, mips64)
-android-toolchain-version ... Set Android toolchain version
-android-style-assets Automatically extract style assets from the device at
run time. This option makes the Android style behave
correctly, but also makes the Android platform plugin
incompatible with the LGPL2.1. [yes]

组件选择

-skip ......... Exclude an entire repository from the build.
-make ......... Add to the list of parts to be built.
Specifying this option clears the default list first.
[libs and examples, also tools if not cross-building,
also tests if -developer-build]
-nomake ....... Exclude from the list of parts to be built.
-compile-examples .... When unset, install only the sources of examples [yes]
-gui ................. Build the Qt GUI module and dependencies [yes]
-widgets ............. Build the Qt Widgets module and dependencies [yes]
-no-dbus ............. Do not build the Qt D-Bus module
[default on Android and Windows]
-dbus-linked ......... Build Qt D-Bus and link to libdbus-1 [auto]
-dbus-runtime ........ Build Qt D-Bus and dynamically load libdbus-1 [no]
-accessibility ....... Enable accessibility support [yes]
Note: Disabling accessibility is not recommended.
-qml-debug ........... Enable QML debugging support [yes]

Qt comes with bundled copies of some 3rd party libraries. These are used
by default if auto-detection of the respective system library fails.

核心选项

-doubleconversion .... Select used double conversion library [system/qt/no]
No implies use of sscanf_l and snprintf_l (imprecise).
-glib ................ Enable Glib support [no; auto on Unix]
-eventfd ............. Enable eventfd support
-inotify ............. Enable inotify support
-iconv ............... Enable iconv(3) support [posix/sun/gnu/no] (Unix only)
-icu ................. Enable ICU support [auto]
-pcre ................ Select used libpcre2 [system/qt]
-pps ................. Enable PPS support [auto] (QNX only)
-zlib ................ Select used zlib [system/qt]

日志后端

-journald .......... Enable journald support [no] (Unix only)
-syslog ............ Enable syslog support [no] (Unix only)
-slog2 ............. Enable slog2 support [auto] (QNX only)

网络选择

-ssl ................. Enable either SSL support method [auto]
-no-openssl .......... Do not use OpenSSL [default on Apple and WinRT]
-openssl-linked ...... Use OpenSSL and link to libssl [no]
-openssl-runtime ..... Use OpenSSL and dynamically load libssl [auto]
-securetransport ..... Use SecureTransport [auto] (Apple only)
-sctp ................ Enable SCTP support [no]

-libproxy ............ Enable use of libproxy [no]
-system-proxies ...... Use system network proxies by default [yes]

Gui, 打印, 挂件选择

-cups ................ Enable CUPS support [auto] (Unix only)

-fontconfig .......... Enable Fontconfig support [auto] (Unix only)
-freetype ............ Select used FreeType [system/qt/no]
-harfbuzz ............ Select used HarfBuzz-NG [system/qt/no]
(Not auto-detected on Apple and Windows)
-gtk ................. Enable GTK platform theme support [auto]

-lgmon ............... Enable lgmon support [auto] (QNX only)

-no-opengl ........... Disable OpenGL support
-opengl ........ Enable OpenGL support. Supported APIs:
es2 (default on Windows), desktop (default on Unix),
dynamic (Windows only)
-opengles3 ........... Enable OpenGL ES 3.x support instead of ES 2.x [auto]
-angle ............... Use bundled ANGLE to support OpenGL ES 2.0 [auto]
(Windows only)
-combined-angle-lib .. Merge LibEGL and LibGLESv2 into LibANGLE (Windows only)

-qpa .......... Select default QPA backend (e.g., xcb, cocoa, windows)
-xcb-xlib............. Enable Xcb-Xlib support [auto]

平台后端:

-direct2d .......... Enable Direct2D support [auto] (Windows only)
-directfb .......... Enable DirectFB support [no] (Unix only)
-eglfs ............. Enable EGLFS support [auto; no on Android and Windows]
-gbm ............... Enable backends for GBM [auto] (Linux only)
-kms ............... Enable backends for KMS [auto] (Linux only)
-linuxfb ........... Enable Linux Framebuffer support [auto] (Linux only)
-mirclient ......... Enable Mir client support [no] (Linux only)
-xcb ............... Select used xcb-* libraries [system/qt/no]
(-qt-xcb still uses system version of libxcb itself)

输入后端

-evdev ............. Enable evdev support [auto]
-imf ............... Enable IMF support [auto] (QNX only)
-libinput .......... Enable libinput support [auto]
-mtdev ............. Enable mtdev support [auto]
-tslib ............. Enable tslib support [auto]
-xinput2 ........... Enable XInput2 support [auto]
-xkbcommon-x11 ..... Select xkbcommon used in combination with xcb
[system/qt/no]
-xkb-config-root ... With -qt-xkbcommon-x11, set default XKB config
root [detect]
-xkbcommon-evdev ... Enable X-less xkbcommon in combination with libinput
[auto]

图像格式

-gif ............... Enable reading support for GIF [auto]
-ico ............... Enable support for ICO [yes]
-libpng ............ Select used libpng [system/qt/no]
-libjpeg ........... Select used libjpeg [system/qt/no]

数据库选项

-sql- ........ Enable SQL plugin. Supported drivers:
db2 ibase mysql oci odbc psql sqlite2 sqlite tds
[all auto]
-sqlite .............. Select used sqlite3 [system/qt]

Qt3D 选项

-assimp .............. Select used assimp library [system/qt/no]
-qt3d-profile-jobs ... Enable jobs profiling [no]
-qt3d-profile-gl ..... Enable OpenGL profiling [no]

多媒体选项

-pulseaudio .......... Enable PulseAudio support [auto] (Unix only)
-alsa ................ Enable ALSA support [auto] (Unix only)
-no-gstreamer ........ Disable support for GStreamer
-gstreamer [version] . Enable GStreamer support [auto]
With no parameter, 1.0 is tried first, then 0.10.
-mediaplayer-backend ... Select media player backend (Windows only)
Supported backends: directshow (default), wmf

移植QT4.8.7

之前移植了QT5.9.1,这里移植QT4.8.7就简单介绍下

下载QT4.8.7

wget http://download.qt.io/official_releases/qt/4.8/4.8.7/qt-everywhere-opensource-src-4.8.7.tar.gz
tar xf qt-everywhere-opensource-src-4.8.7.tar.gz
cd qt-everywhere-opensource-src-4.8.7
./configure --help >help.txt   #QT的配置较为复杂,进来先看看帮助文档,在本节最后附录里有翻译

一般来说,我们需要编译主机和目标板两个版本的qt:

主机版可以用于前期的gui的设计调试;目标板用于实际产品的验证。

编译X11版本

主机版一般是X11,配置如下:

cfg_X11.sh
 #!/bin/bash
 ./configure -prefix /opt/qt-4.8.7-x11 -opensource -make tools
 #安装位置,开源版本,编译qt工具(makeqpf,qtconfig)
 make -j32           #约10分钟
 sudo make install

安装完成后在 /opt/qt-4.8.7-x11 下可见安装的文件。

交叉编译arm版本

注意,在第二次编译前,先 make clean 下。如果编译时候仍有错误,可以重新解压编译。

交叉编译,需要配置 xplatform选项,比如要在arm-linux平台上移植Qt的话,就在配置项中加上 -xplatform linux-arm-gnueabi-g++ ,这个是平台名字,Qt5支持的交叉平台都可在源码顶层目录中的 mkspecs/ 下找到。

首先我们需要编辑 mkspecs/qws/linux-arm-gnueabi-g++/qmake.conf

加上:(注意,和tslib类似,这里要加上其它什么支持的话,也是交叉编译的库的路径)

QT_QPA_DEFAULT_PLATFORM = linuxfb
QMAKE_CFLAGS_RELEASE += -O2 -march=armv7-a -lts
QMAKE_CXXFLAGS_RELEASE += -O2 -march=armv7-a -lts
QMAKE_INCDIR += /opt/tslib/include /opt/sqlite3/include
QMAKE_LIBDIR += /opt/tslib/lib /opt/sqlite3/lib

arm-linux-gnueabihf-gcc 改成 arm-linux-gnueabi-gcc 等。

然后再编辑配置脚本cfg_arm.sh

#/bin/sh
./configure -verbose \
-opensource \
-confirm-license \
-release -shared \
-embedded arm \
-xplatform qws/linux-arm-gnueabi-g++ \
-depths 4,8,16,32 \
-fast \
-optimized-qmake \
-pch \
-qt-sql-sqlite \
-qt-libjpeg \
-qt-zlib \
-qt-libpng \
-qt-freetype \
-little-endian -host-little-endian \
-no-qt3support \
-no-libtiff -no-libmng \
-no-opengl \
-no-mmx -no-sse -no-sse2 \
-no-3dnow \
-no-openssl \
-no-webkit \
-no-qvfb \
-no-phonon \
-no-nis \
-no-opengl \
-no-cups \
-no-glib \
-qt-gfx-transformed \
-no-xcursor -no-xfixes -no-xrandr -no-xrender \
-no-separate-debug-info \
-nomake examples -make tools -make docs \
-qt-mouse-tslib -I/opt/tslib/include -L/opt/tslib/lib

make -j32
sudo make install

完成后,相关文件在 /opt/qt4.8.7-arm 下。

常见编译错误

配置QT的时候,如果指定了-webkit,编译的时候会报错:

../3rdpartyjavascriptcoreJavaScriptCore/wtf/TypeTraits.h:173:69:error: 'std::tr1' has not been declared

解决方法:

修改QT源码目录下mkspecs/qws/linux-arm-gnueabi-g++/qmake.conf文件,加上一行:QMAKE_CXXFLAGS = $$QMAKE_CFLAGS -std=gnu++98
>>> /opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/6.3.1/../../../../arm-linux-gnueabihf/bin/ld: warning: libts.so.0, needed by /home/wcz/qt-everywhere-opensource-src-4.8.7_arm/lib/libQtGui.so, not found (try using -rpath or -rpath-link)

提示没有tslib的库,当然了,提示中也清楚的说了(try using -rpath or -rpath-link)。

我们可以:

  1. 进入到编译报错的目录,在这里是“examples/network/broadcastreceiver“
  2. 修改这个目录中的Makefile文件,找到LFLAGS一行,在最后面加上”-Wl,-rpath,/opt/tslib/lib“,也就是手动指定tslib的目录。(这里还没清楚为何configure时的参数没起作用)
  3. 就地make
  4. 退回到主目录
  5. 继续make

上面的操作,如果每个Makefile都要编辑一遍的话,那就太麻烦了,我们偷偷懒。

针对Makefile的LFLAGS一行的特点,我们使用 find、grep、awk、sed、xargs 来完成这个操作,如下:

进入到出错目录的顶层,如上示例,我们进入到examples。

cd examples

find . -name Makefile | xargs grep rpath-link | grep -v tslib | awk -F: '{fname[NR]=$1} END {for (i=1;i<=NR;i++){print fname[i]}}' | xargs sed -i 's/LFLAGS.*/& -Wl,-rpath,\/opt\/tslib\/lib/'

<注意,这个命令会直接修改目录中最底层的每一个Makefile,请试验成功后再使用。>

向开发板添加Qt库

首先将 /opt/qt5.9.1-arm和/opt/tslib 复制到开发板的对应目录下

然后设置开发板 Qt 环境变量, vi /etc/bash.bashrc

添加下面内容:

export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
export TSLIB_TSDEVICE=/dev/input/event1
export TSLIB_CONFFILE=/opt/tslib/etc/ts.conf
export TSLIB_PLUGINDIR=/opt/tslib/lib/ts
export TSLIB_CALIBFILE=/etc/pointercal
export LD_LIBRARY_PATH=/lib:/usr/lib:/opt/tslib/lib:/opt/qt5.9.1-arm/lib
export PATH=/bin:/sbin:/usr/bin/:/usr/sbin:/opt/tslib/bin
export QT_QPA_PLATFORM_PLUGIN_PATH=/opt/qt5.9.1-arm/plugins
export QT_QPA_PLATFORM=linuxfb:tty=/dev/fb0
export QT_QPA_FONTDIR=/opt/qt5.9.1-arm/lib/fonts
export QT_QPA_GENERIC_PLUGINS=tslib:$TSLIB_TSDEVICE
export QWS_MOUSE_PROTO=Tslib:/dev/input/event0
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/optslib/lib:/opt/qt5.9.1-arm/lib:/usr/lib/arm-linux-gnueabihf/lib

保存后生效上述内容: source /etc/bash.bashrc

br生成的文件系统则为:

export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
export TSLIB_TSDEVICE=/dev/input/event1
export TSLIB_CONFFILE=/etc/ts.conf
export TSLIB_PLUGINDIR=/usr/lib/ts
export TSLIB_CALIBFILE=/etc/pointercal
export LD_LIBRARY_PATH=/lib:/usr/lib
export PATH=/bin:/sbin:/usr/bin/:/usr/sbin
export QT_QPA_PLATFORM_PLUGIN_PATH=/usr/lib/qt/plugins
export QT_QPA_PLATFORM=linuxfb:tty=/dev/fb0
export QT_QPA_FONTDIR=/usr/lib/qt/lib/fonts
export QT_QPA_GENERIC_PLUGINS=tslib:$TSLIB_TSDEVICE
export QWS_MOUSE_PROTO=Tslib:/dev/input/event1

QtCreator使用

完成了tslib和QT的编译后,就成功搭建了Qt环境,接下来就可以使用QtCreator来开发图形界面了~

QtCreator安装

wget http://download.qt.io/official_releases/qtcreator/4.4/4.4.1/qt-creator-opensource-linux-x86_64-4.4.1.run

下载好后,在图形界面里双击运行即可。

注意,由于docker开图形界面比较麻烦,所以qtcreator不包含在docker镜像中,大家可以自行下载到本机安装。

QtCreator配置

打开qtcreator,打开 Tools ‣ option ‣ BuildRun

首先配置Qt版本,这里需要用到前面编译好的qmake,点击add,把前面编译好的qmake指给它

https://box.kancloud.cn/9810b28b6aa4278c5308deea58be8f3b_1037x646.jpg

然后配置工具链,把我们使用的linaro工具链指给它

https://box.kancloud.cn/53ef609937feda59071f3ae54197a580_1008x675.jpg

最后配置 构建套件(Kit),就是组合Qt版本和工具链版本,我们这里新建arm v7套件。

https://box.kancloud.cn/6cd1cf0107a18514fbe7c833e40de84b_1128x636.jpg

保存应用退出即可。

QtCreator的简单使用

新建工程,一路默认下去(选择arm v7套件),完成。

https://box.kancloud.cn/9810b28b6aa4278c5308deea58be8f3b_1037x646.jpg

随便拉些控件:

https://box.kancloud.cn/53ef609937feda59071f3ae54197a580_1008x675.jpg

然后点左下角运行按键就会自动构建,在工程目录下生成对应二进制文件。

把生成的程序拷贝到目标板上,运行:

root@LicheePi:~# ./test
./test: /usr/lib/arm-linux-gnueabihf/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by ./test)
./test: /usr/lib/arm-linux-gnueabihf/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /opt/qt5.9.1-arm/lib/libQt5Widgets.so.5)
./test: /usr/lib/arm-linux-gnueabihf/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /opt/qt5.9.1-arm/lib/libQt5Gui.so.5)
./test: /usr/lib/arm-linux-gnueabihf/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /opt/qt5.9.1-arm/lib/libQt5Core.so.5)

检查现有的libstdc++:

root@LicheePi:~# strings /usr/lib/arm-linux-gnueabihf/libstdc++.so.6 | grep CXXA
CXXABI_1.3
CXXABI_1.3.1
CXXABI_1.3.2
CXXABI_1.3.3
CXXABI_1.3.4
CXXABI_1.3.5
CXXABI_1.3.6
CXXABI_1.3.7
CXXABI_1.3.8
CXXABI_TM_1
CXXABI_ARM_1.3.3

说明系统里的libstdc++6使用了较老的编译器,导致test程序里链接后,无法正常使用库中的函数。

于是从linaro的 arm-linux-gnueabihf/lib 下拷出新的 libstdc++.so.6.0.22 覆盖即可。

再次运行,会发现界面上没有文字,终端提示没有字体。

于是下载任意字体,如simsun.ttf,放到 /opt/qt5.9.1-arm/lib/fonts 下,再重新运行,就有字体显示了。

https://box.kancloud.cn/edfde3383b6b4eeeab844a3ab5df28f4_886x592.jpg

此时会发现无法触摸,于是运行下触摸校准程序:

ts_calibrate
ts_test

校准,测试通过后,再次运行,即可触摸控制了。

但此时会看到终端光标在左下角闪烁,而且有时候内核信息会覆盖图像,所以:

禁止printk,隐藏光标。

echo 0 > /proc/sys/kernel/printk
echo -e "\033[?25l" > /dev/tty0

Qt5.x移植到Qt4.8

在使用Qt5.x之后,发现Qt5.x还是比Qt4.8臃肿很多,所以在空间受限的设备上,还是建议使用Qt4.8.

这里记录Qt5.x的程序移植到Qt4.8上会遇到的一些问题。

QtSerialPort 移植

QtSerialPort是Qt5.3之后自带的,所以在Qt4.8上我们需要手工安装。

Qt串口模块5.5开始不再支持Qt4,所以建议用最后一个版本,qtserialport-opensource-src-5.4.2

pro里加上CONFIG += serialport

在这里下载: https://pan.baidu.com/s/1o6UVlk2

解压,打开其中的工程

sudo apt-get install libblkid-dev libkmod-dev libgirepository1.0-dev

./configure --prefix=/opt/qt4.8.7-arm/ --target=arm-linux-gnueabihf --host=arm-linux-gnueabihf LD=arm-linux-gnueabihf-ld --with-pci-ids-path=/var/share/pci.ids

线程延时

QThread::msleep(100);

线程延时100ms改成:

QEventLoop eventloop;
QTimer::singleShot(100, &eventloop, SLOT(quit()));
eventloop.exec();

中文乱码

在最前面加上:

QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QTextCodec::setCodecForTr(codec);
QTextCodec::setCodecForLocale(codec);
QTextCodec::setCodecForCStrings(codec);

C++11

/home/zepan/develop/Second/wifiscanthread.h:16: warning: non-static data member initializers only available with -std=c++11 or -std=gnu++11
    bool stopped = false;
                    ^~~~~

无法打开显示设备

root@LicheePi:~# ./Second4.8
QWSSocket::connectToLocalFile could not connect:: Connection refused
QWSSocket::connectToLocalFile could not connect:: Connection refused
^C
root@LicheePi:~#./Second4.8 -qws
QLock::QLock: Cannot create semaphore /tmp/qtembedded-0/QtEmbedded-0 'd' (38, Function not implemented)
Cannot get display lock
Aborted

最近查看Qt源码才发现,Qt需要用到System V IPC进行进程间通信,而Android的进程间通信用的是谷歌自己的方式,所以Qt根本无法实现进程间的通信,自然无法正常启动。

重新配置内核,打开 System V IPC选项,问题解决。

Qt字体相关

支持的字体

嵌入式Qt支持4中格式的字体,分别是:
  1. TrueType(TTF) 可缩放字体格式,桌面系统中主要使用的字体
  2. PostscripType(PFA/PFB) 可缩放字体格式,打印主要使用的字体,可TTF类似
  3. **Bitmap Distribution Format fonts (BDF) ** 不可缩放字体的标准格式,在X11平台系统中可以找到
  4. Qt Prerendered Font (QPF) Qt预渲染字体,专用于嵌入式Qt的一种字体格式

QPF字体格式是嵌入式Qt内置支持的字体,可以使用QT SDK中提供的makeqpf工具利用前三种字体文件来生成。

而其它的字体格式是否支持则是可以进行裁剪定制的,可以去掉对TTF、BDF字体的支持以降低Qt库的空间占用,通过定义下面的Qt特性宏:

/*
TrueType (TTF and TTC) font file format, only used by Qt/Embedded.
*/
#define QT_NO_TRUETYPE

/*
Bitmap Distribution Format (BDF) font file format, only used by Qt/Embedded.
*/
#define QT_NO_BDF

当然,不定义这两个宏则表示支持TTF、BDF字体。

因为QPF是预渲染的,嵌入式Qt使用它时不需要读取和解析它,只需要进行简单快速的映射,因此它对内存的消耗是最小的。

因此一种方法是利用TTF和BDF字体文件制作好需要的QPF字体文件,然后移除对两者的支持。

TTF、PFA和QPF字体都支持字体的反锯齿,即使字体尽量显示的圆滑而不会有锯齿感,从而提供更好的可读性和观感,特别是在低分辨率设备上,但是这是以消耗更多的内存和空间为代价的,大概是不反锯齿的八倍。

对于所有的字体文件,嵌入式Qt都是使用Unicode编码的格式。

使用字体文件

当Qt应用运行时,它会到以下两个目标之一去寻找规定的字体定义文件fontdir:

$QTDIR/lib/fonts/fontdir    QTDIR是一般使用Qte都会定义的环境变量
/usr/local/qt-embedded/lib/fonts/fontdir  可能是默认的qt安装路径

fontdir文件定义了Qt应用可以使用的字体格式、大小和文件等内容。fontdir文件的内容和字体条目定义的格式可以参考如下的示例文件,文件中有相应的说明:

一个典型的FONTDIR文件的内容如下所示:

fixed fixed_120_50.qpf QPF n 50 120
helvetica helvetica_80_50.qpf QPF n 50 80
helvetica helvetica_120_50.qpf QPF n 50 120 u
helvetica helvetica_120_75.qpf QPF n 75 120 u
helvetica helvetica_140_75.qpf QPF n 75 140
helvetica helvetica_180_75.qpf QPF n 75 180

文件中每行都标识一个特定的字库,每个段的含义是:

第一列为name,
第二列为file,
第三列为renderer,相当于字型格式,所以有BDF,TTT,QPF等选择。
第四列n表示iitalic,表示是否为斜体字。
第五列表示weight,其中50表示Normal,75表示Bold。
第六列表示size,例如:120表示12pt。
第七列为flags,有下面三个选择:
    s=smooth(anti-aliased)
    u =unicode range when saving
        (default is Latin 1 a = ASCII range when saving(default is Latin 1))

你可以在文件中添加其它的字体格式条目,前提是你有相应的字体文件,不然加了也没用。

关于文件中的渲染引擎renderer

上面也提到了,renderer只能是BDF或者FT,BDF对应BDF字体文件,而FT是FreeType的缩写,FreeType是一个强大的库用来实现渲染TTF/TTC和PFA字体,在嵌入式QT库的编译配置时可以选择是否支持。 注意到没有QPF渲染引擎的说法,这是因为QPF字体文件的使用是不需要在fontdir文件中定义的,只需要将QPF字体文件放到和fontdir文件同一级目录下就可以,但是QPF字体文件的命名必须符合Qte的规范:

name_size_weightitalicflag.qpf

e.g:
    wenquanyi_160_75.qpf   16pt文泉驿粗体,不支持斜体
    wenquanyi_160_50i.qpf  16pt文泉驿正常,支持斜体

关于文件中的字体大小size

size的大小是字体大小*10,也就是12pt字体的size是120,但是我对pt这个单位没有什么概念,所以我特意查找对比了一下字体大小的单位表示,整理如下:

  • 小五号:9pt, 五号:10.5pt, 小四号:12pt
  • 四号:13.75pt(磅), 三号:15.75pt(磅), 二号:21pt
  • 中文最小字号:八号;2.83pt = 1mm, 28.3pt = 1cm = 一号字体(27.5pt)

字体大小对应的点阵:

  • 9pt:1212(96dpi),1515(120dpi);
  • 12pt: 1515(96dpi),1919(120dpi)

不过在实际的嵌入式设备上显示时好像和这种字体大小在word上的显示不太一致,如前面的16pt文泉驿字体在设备上显示的大小感觉和word中的小四号字体差不多大。

关于中文字体支持

步入重点,如果需要支持中文字体,显然就需要有对应的中文字体文件。

前面提到的unifont.bdf文件和文泉驿字体文件都是可以支持中文的,也可以从对应的网站上下载到官方的字体文件。

而文泉驿字体在Linux系统X11平台上也是使用的比较多的,所以可以直接取用。

关于QPF字体文件生成的捷径

前面提到可以使用makeqpf工具通过TTF、BDF字体文件来生成QPF字体文件,这就需要编译makeqpf工具,然后自己一个个去进行相应的转换,你还可能不确定会使用到哪些字体。

另一种更方便快捷的方法是让Qt应用自动完成需要使用字体的QPF生成,方法就是在运行Qt应用时,加上 -savefonts 选项。此时应用会将所有使用到的非QPF字体自动转换生成QPF字体文件并保存下来。

然后你就可以去掉TTF、BDF支持,支持使用QPF字体文件进行部署了。

Qt移植总结

PC端步骤

交叉编译好qt-everywhere,内含关键的qmake

qmake用于配置qtcreator

目标板步骤

  1. 拷贝qt5.9.1-arm, sqlite3,tslib(内含ts_calibrate) 到/opt下

  2. 拷贝字体到/opt/qt5.9.1-arm/lib/fonts下

  3. 之前交叉编译qt的工具链里的/usr/lib/arm-linux-gnueabihf/lib/libstdc++.so.6.0.22 覆盖当前的so

  4. 编辑/etc/bash.bashrc, 加入环境变量:

    export TSLIB_CONSOLEDEVICE=none
    export TSLIB_FBDEVICE=/dev/fb0
    export TSLIB_TSDEVICE=/dev/input/event1
    export TSLIB_CONFFILE=/opt/tslib/etc/ts.conf
    export TSLIB_PLUGINDIR=/opt/tslib/lib/ts
    export TSLIB_CALIBFILE=/etc/pointercal
    export LD_LIBRARY_PATH=/lib:/usr/lib:/opt/tslib/lib:/opt/qt5.9.1-arm/lib
    export PATH=/bin:/sbin:/usr/bin/:/usr/sbin:/opt/tslib/bin
    export QT_QPA_PLATFORM_PLUGIN_PATH=/opt/qt5.9.1-arm/plugins
    export QT_QPA_PLATFORM=linuxfb:tty=/dev/fb0
    export QT_QPA_FONTDIR=/opt/qt5.9.1-arm/lib/fonts
    export QT_QPA_GENERIC_PLUGINS=tslib:$TSLIB_TSDEVICE
    
  5. 拷贝编译好的基于qt的应用程序到自定义目录下

Qt裁剪

默认编译出来的Qt库是很大的,有几十M,甚至上百M。

为了将Qt库放入小容量的SPI flash中,我们需要去除不必要的库,裁剪必要的库。

去除不必要的库

裁剪feature
./src/corelib/global/qfeatures.h
./configure -qconfig myfile
针对嵌入式版本的配置

Qt的嵌入式版本本身就支持feature裁剪,我们可以充分利用这一特性让Qt库尽量变小。具体的做法是要做一个自己的 qconfig-[myconfig].h 特性文件,该文件中定义你要去掉Qt中的哪些feature。在configure的时候加“ -qconfig myconfig ” 选项, Qt就会根据你给出的配置文件来编译,以达到裁剪的目的。这里要强调一下,这种裁剪方式只适用于嵌入式版本。这里的myconfig可以用任何你喜欢的名字来代替。

在qt的代码中已经给出了一些qconfig头文件的例子,默认编译采用full config也就是

不裁剪任何feature。所有Qt预定义好的qconfig文件,可以在 src/corelib/global/ 下找到,包括 qconfig-minimal.h, qconfig-small.h, qconfig.medium.h,qconfig-large.h和qconfig-dist.h,也就是从裁剪量由多到少都有据可依。如果要添加你自己的配置文件,要在 src/corelib/global 下建立一个形如qconfig-xxx.h的文件,这个xxx也就是你要在configure的时候传入的qconfig参数。

笔者测试使用的Qt版本是4.4.1,这个版本的build system有个小毛病,就是如果你指定的qconfig参数实际上没有 qconfig-xxx.h 文件对应, build不会停止,它只会给出一个不起眼的提示,编译过程会继续, 这一点挺让人费解的。而且这种情况下Qt编译使用的配置基本上和fullconfig相同,鉴于它的让人迷惑的举动,个人觉得有必要提醒大家一下,使用自定义qconfig的时候一定要确定配置文件放对了位置,而且qconfig参数给的正确。

一般我们的建议是在桌面上测试阶段编译一个full的版本,再根据你的项目使用Qt feature的情况总结哪些可去掉的feature。 feature之间有千丝万缕的依赖关系,这个问题也是困扰很多人的难点所在。具体的依赖可以查阅 src/corelib/global/qfeatures.hsrc/corelib/global/qfeatures.txt (描述依赖关系的文档)。另外,Qt里还提供了一个可视化的配置依赖的工具,叫做qconfig,在 QTDIR/tools/qconfig 目录。该工具需要基于Qt桌面版本编译。

如在我的linux系统下可以用下面的命令来编译:

cd qt-embedded-linux-commercial-4.4.1/tools/qconfig
/usr/local/Trolltech/Qt-4.4.3/bin/qmake
make

编译成功后运行 ./qconfig,初始要打开qfeatures.txt. Qconfig读取该文件生成一个树状图,该图很清楚的显示出feature之间的依赖关系。如下图所示,如果你去掉了LINEEDIT这个feature,用到该控件的combobox也就不能继续使用了。有了这个工具裁剪Qt变得简洁直观,方便了很多。

选定了你要去掉的feature后点击菜单 File ‣ Save As.. 会弹出保存文件的页面,文件名字应该定义成 qconfig-xxx.h 的形式,这样你在configure的时候就可以传入相应的qconfig参数了。

你还可以通过选择 File ‣ Open 打开现有的 qconfig-xxx.h 文件,通过修改已经有的文件更快的编辑配置。

根据笔者测试,未经裁剪的qte4.4.1编译出来为:

libQtCore.so是2.6M
libQtGui.so是9.5M

如果用small来编译,就能缩小为:

libQtCore.so是2.0M
libQtGui.so是5.7M

差异还是比较明显的。

使用静态编译

  • 在编译Qt库时使用 -static 选项
  • 重新编译qtserial等库,并install
  • 在pro里加上 QT += core gui sql serialport
  • 程序改为release

实测减少有限

Qt去除鼠标指针显示

qt全屏显示

主要是设置setWindowFlags 可以这样使用全屏幕

yourwidget->setWindowFlags(Qt::window | Qt::FramelessWindowHint); //第一个Qt::window表示此widget是窗口类型,第二个参数使用无框架就是没有标题,状态栏等。具体参考
$QTPATH/examples/widgets/windowflags/

Qt Embedded Linux下隐藏鼠标箭头

  1. 编译Qt库的时候添加编译选项QT_NO_CURSOR,这样cursor相关的代码统统不会被编译进去,自然鼠标光标也不会出现在程序中。

    -no-mouse-tslib

  2. 只希望在某个QWidget下不出现鼠标光标,则只要对这个widget调用

    QWidget::setCursor(QCursor(Qt::BlankCursor)),其它的窗口仍将出现鼠标。

3.在main函数中,实例化了APPLICATION后,调用

QApplication::setOverrideCursor(Qt::BlankCursor);
  1. 任一控件下显示与关闭鼠标

    this->setCursor(Qt::BlankCursor); //隐藏鼠标
    
    this->setCursor(Qt::ArrowCursor); //显示正常鼠标
    

    this改为需要隐藏鼠标的部件,就可以令当鼠标移动到该部件时候,效果生效。

    以上的都需要动一下鼠标才会消失,不知道不是我没有搞好,下面一启动就可以隐藏起来

  2. 调用下面函数

    QWSServer::setCursorVisible(false);

    这是一个静态函数,可以在main()函数中,实例化QApplication以后调用,这样整个程序将不会出现鼠标的光标。注意必须包含头文件。 //但是在程序启动时会闪现一下光标

源代码

src/corelib/global/qfeatures.h

荔枝派TODO任务领取

前言

本文档详细介绍荔枝派Zero的使用,将由群主设计目录框架,群友众包完成。

完成每篇文档的群友均可获得Zero一片,有难度的文档会酌情提高回报。

需要什么教程的朋友可以在这里留言,我将添加到TODO List里。

本文不定期更新开发者任务,有荔枝派相关的,也有其他小项目的,有兴趣的朋友可以在其下留言或者直接小窗联系群主QQ:715805855, 或者邮箱 zepanwucai@gmail.com 领取任务。

tips 普通简单任务请在一周内完成,困难任务酌情放宽时间。

tips 每个任务同时可由多人认领,由最先完成者获取回报。也可组队认领,回报由队内协商分配

LicheePi Zero TODO List

大项 Item 小项 SubItem 详述 description 预估难度 difficulty(1~5) 预估耗时 timecost 回报 reward 领取人 contributor
教程撰写 Tutorial 开箱指南 OpenBoxTutorialCN 1.Zero各部分简介 2.启动方式介绍 3.系统镜像组成部分介绍 4.系统镜像分类介绍 5.镜像烧录及简单演示 1 1~2h Zero dž~木耳多糖 5.4 sendout
- OpenBox Tutorial EN 1.Zero element intro 2.boot method intro 3.system image partion intro 4.system image intro 5.image download and demonstration 1 1~2h Zero 熟悉的陌生人 5.1 sendout
- WiFi/BT module Usage CN 0.模块的焊接演示 1.WiFi的使用及iperf测试 2.BT的使用及连接蓝牙外设的测试 2 2h ZeroW  
- WiFi/BTmodule Usage EN 0.solder method 1.WiFi usgae and iperf test 2.BT usage and test 2 2h ZeroW  
- Uboot compile CN 介绍Uboot的结构,menuconfig里的常见配置选项,编译步骤等 1 1h Zero 飞跃5.4 sendout
- Uboot compile EN intro Uboot struct,menuconfig items,compile setps and soon 1 1h Zero  
- Linux kernel compile CN 介绍Zero的开发环境搭建,内核配置编译过程,常见驱动的配置等 2 1~2h ZeroW 亦∮魔 5.4 sendout
- Linux kernel compile EN Zero's Linux development environment setup,kernel config,driver config,etc. 2 1~2h ZeroW  
Uboot调试 Uboot Works SPI启动SPI Nor boot up SPI NOR flash启动到内核,撰写文档SPI NOR Flash boot up to kernel,write the document 4 4~8h ZeroW+5 inch LCD  
驱动调试 DriverWorks 适配以太网驱动 Adapt Ethernet driver 尝试编译以太网驱动,发送给群主验证,并撰写过程文档compile and debug ethernet driver,write the document 3 2~4h ZeroWDock techping 5.4 sendout & finished
- Adapt SPI LCD 尝试编译SPI LCD驱动,作为系统主显示,发给群主验证,并记录文档 compile and debug SPI LCD Driver,write the document 3 2~4h ZeroWDock QianFan 5.1 sendout
- Adapt I2C OLED 尝试编译I2C OLED驱动,发给群主验证,并记录文档 compile and debug I2C OLED driver,write the document 3 2~4h ZeroWDock 星空 5.4 finished
- 适配OV5647摄像头主线驱动 MIPI CSI and OV5647 driver 尝试移植OV5647驱动,主要是drivers/media/i2c下寄存器操作和drivers/media/platform下平台相关操作。发给群主验证,并撰写移植文档 write and debug MIPICSI,OV5647 driver,write the document 5 ??? $500  
- 官方SDK驱动摄像头 AW SDK MCSI driver 剥离官方SDK的Linux内核,构建可使用摄像头的系统镜像 strip AW SDK's linux,and run camera on it 4 4~8h $100 王伟 5.4 sendout
- 适配HDMI转接板驱动 Adapt HDMI driver IT66121FN驱动 compile and debug IT66121 driver,write the document 3 2~4h ZeroWDock techping 5.4 sendout
- 适配按键手柄驱动 Adapt I2C joystick driver TCA8418驱动 TCA8418 driver compile and debug,write the document 2 2h ZeroW  
PCB相关PCB Works 底板修改 Dock optimizing 修改优化初版底板 Dock optimizing 2 2~4h ZeroWDock  
- I2C手柄修改 I2C joystick optimizing 优化手柄握感 I2C joystick optimizing 2 2h ZeroW  
- RGB40P转50P转接板设计 RGB40P to 50P 根据群创通用50P RGB屏的定义,设计40P转50P转接板 RGB40P to 50P adapt or board design 2 2h 报销打样,元件费用 freePCB and bom shadow 5.5
系统镜像 rootfs for lxde 新增lxde的根文件系统 2 2h ZeroW Anonym 5.1 finished
- 打包镜像 package image 打包uboot,内核,根文件系统到单独的dd镜像package "dd" image 1 1~2h Zero Anonym 5.1 Finished
- Qt image 打包Qt镜像,并撰写使用文档 Package Qt image,write document 2 2~4h ZeroWDock fu登ck 5.1
- openwrt image 打包Openwrt镜像,包含压缩版和不压缩版,撰写编译,使用文档 package openwrt image(compress and uncompress),write document 2 4h ZeroWDock+5 inch LCD -
杂项 MISC 名片式说明书设计 specification card design 90x54mm标准名片大小的简易说明书,一面标注Zero的引脚位,一面是各资源链接 1 1~2h Zero 小白杨 5.2
其它Other 可以提出其它建议,加入到TODOlist清单中 Add more task to this List - - - - -

其它项目 快餐式外包

项目 详述 预估难度 预估耗时 回报 领取人
某LED灯板原理图及PCB修改 从单色灯改成双色灯和RGBW灯,单面板 2 2~4h 200RMB Sandwich à lane 5.1
某LED灯板闪灯程序修改 从单色灯修改为双色灯和RGBW灯的闪烁方式 2 2~4h 200RMB Sandwich à lane 5.1
某GPRS模组MQTT调试 使用上海合宙的Air200模块,Lua编程,自带MQTT库,与我方后端工程师对接调试,需先付开发板押金 2 4~6h 500RMB MARtinT3CH 5.1

投稿文章精选

感谢各位的辛勤付出与技术分享,现将各篇优秀技术文章集合如下:

荔枝派zero开箱指南

1. Zero各部分简介

正面:
https://box.kancloud.cn/81ef02fb6e5e3d8325011cce1d0b8e3d_1472x833.jpg
  1. 主控v3s芯片,Allwinner V3s (ARM Cortex-A7 CPU, 1.2GHz, 512Mbit DDR2 integrated)
  2. tf卡插槽,tf卡金手指朝下插入。
  3. micro usb口,可以用来给板子供电,也可以通过micro usb otg转接线转成标准usb大口然后连接各种usb外设,比如usb无线网卡。也可以接一个usb hub方便插多个usb设备。
  4. 一个RGB灯。
  5. 板子3.3V输出,注意方形焊盘是正。
  6. 板子5V供电输入,也可以接3.7v锂电池给板子供电,注意方形焊盘是正。
  7. UART0扩展接口,主要用来通过串口工具连接PC调试使用。
  8. 上边两排分别有两排2.54排针焊接孔和两排1.27排针焊接孔(是邮票半孔),方便扩展,一般扩展只需焊接上下两排2.54排针即可。
反面:
https://box.kancloud.cn/459b696c9d632f0c658408cb7cf2cebf_1441x785.jpg
  1. 40P 通用RGB屏幕接口
  2. spi flash焊盘,默认没有焊接。

2. 启动方式介绍

最常用的启动方式是sd卡启动,网盘有可以直接使用的linux系统镜像,直接烧入sd卡后便可以启动并使用linux系统。也支持其他启动方式spi flash、网络启动、usb下载启动等。

3. 系统镜像组成部分介绍

SD卡中的系统镜像一般分为三个区,第一个区称为boot区或者引导区,该部分没有文件系统而是直接将二进制的bootloader(uboot)文件直接写入。第二个区可以被称为linux内核区,fat文件系统,存放linux内核、内核参数文件还有设备数dtb文件。第三个区是root分区,用来存放根文件系统和用户数据等,一般是ext4文件分区格式。

系统镜像下载地址链接: https://pan.baidu.com/s/1nv7Fq2X 密码: 5gec

4. 镜像烧录及简单演示

在tf卡上创建分区文件系统

首先需要一台linux操作系统的电脑或者在vmware虚拟机上安装linux系统,推荐ubuntu14 64位版本,ubuntu系统默认安装即可。

我使用的是vmware虚拟机的形式,在vmware上安装完毕ubuntu后推荐安装vmware tools工具,安装这个工具后可以在windows和ubuntu桌面之间直接无缝复制粘贴文件。

下面制作tf卡启动系统,需要一张大于8g的tf卡和一个读卡器,将tf卡插入读卡器并插入电脑。如果是虚拟机请检查下虚拟机右下角这个图标的状态,如下图

https://box.kancloud.cn/8a73d6817530e644b7554623eba98b42_517x274.jpg

如果是这种灰色的表示读卡器的操作权在windows系统,需要点击一个这个图标,选择链接(断开与主机连接),这样读卡器的控制器才能到虚拟机linux系统中。

https://box.kancloud.cn/f3e1c9f518571e27b822a691d15716e6_551x276.jpg

在桌面环境搜索gparted分区编辑器并打开。

https://box.kancloud.cn/c53e6753317919ced72cec043d0971c0_695x606.jpg

输入超级用户密码

https://box.kancloud.cn/115c141bf031efe81f421e4b4cbbfe60_570x304.jpg

在右上角中选择tf卡对应的设备

https://box.kancloud.cn/c5242b6da29fffef89467df8fb1c7684_495x283.jpg

依次选中tf卡上已经存在的分区,右键【删除】来删除分区

https://box.kancloud.cn/b17ecf1f8f745ade851a58b13fe1e671_641x466.jpg

如果只有【卸载】选择,那么需要先点击卸载,然后再删除分区。

https://box.kancloud.cn/b67a250dc1c03e7959b9840714d22968_725x480.jpg

所有分区删除完毕后,点击右上角对勾,将操作应用到磁盘。

https://box.kancloud.cn/05fe5db20051a1c487b8ced77cdf2c60_731x394.jpg

点击左上角加号创建新分区,之前剩余空间输入2MB(主要用来存放uboot),新大小输入20,文件系选择fat16,点击添加。

https://box.kancloud.cn/110b374f4c265f63e7b2d0683e5a1292_746x571.jpg

再次点击左上角加号创建新分区,这次使用所有的默认参数如下图所示,分区大小将使用tf卡剩余的所有空间,文件系统是ext4.

https://box.kancloud.cn/110b374f4c265f63e7b2d0683e5a1292_746x571.jpg

点击对勾,应用创建分区到tf卡。

https://box.kancloud.cn/466d58aee7d33489daf143c9dde03979_733x572.jpg

打开linux终端,输入命令sudo fdisk –l 可以看到刚才我们创建的两个分区。

https://box.kancloud.cn/855ef93f1a7537e209de7e0d1f8490ee_711x481.jpg

/dev/sdb即代表tf卡,/dev/sdb1代表的是tf的第一个分区,/dev/sdb2代表的是tf的第二个分区

烧写镜像:

从百度网盘镜像及SDK:链接: https://pan.baidu.com/s/1nv7Fq2X 密码: 5gec 下载镜像文件,zero_imager.zip包含内核启动文件、内核镜像和烧写脚本。rootfs-xxxx.tar.gz是根文件系统,根据不同的需求打包制作出了多个根文件系统

rootfs-brmin.tar.gz是最精简的根文件系统只有1.5M
rootfs-brpy.tar.gz 在brmin基础上包含python环境
rootfs-minmin.tar.gz debian(包含 apt, network)
rootfs-mindb.tar.gz debian(包含apt, network, gcc, python...)
rootfs-minX.tar.gz debian(包含桌面系统)

将zero_imager.zip解压到某个目录下,并将需要的根文件系统放到这个目录下,本例以rootfs-minX.tar.gz为例子。打开终端,执行如下命令

unzip zero_imager.zip(解压)
cp rootfs-minX.tar.gz  zero_imager/(将rootfs-minX.tar.gz复制到zero_imager目录)
cd zero_imager/(切换当前路径到zero_imager)

第一步,将uboot写入到sd卡8k偏移处。

sudo dd if=u-boot-sunxi-with-spl_480800.bin of=/dev/sdb bs=1024 seek=8
https://box.kancloud.cn/09d5b5dfb9c780875fa9468033e2102e_722x109.jpg

第二步,将内核文件,启动参数文件,dtb写入到tf卡的第一分区。

sudo mount /dev/sdb1 mnt/
sudo cp zImage mnt/
sudo cp sun8i-v3s-licheepi-zero*.dtb mnt/
sudo cp boot.scr mnt/
sync
sudo umount /dev/sdb1
https://box.kancloud.cn/d71557d71082e65d5a6a0af5060f2aa6_676x129.jpg

第三步,将根文件系统内容写入到tf卡的第二分区。

sudo mount /dev/sdb2 mnt/
sudo rm -rf mnt/*
sudo tar xzvf rootfs-minX.tar.gz -C mnt/
sudo cp -r overlay_rootfs-base/* mnt/
sudo cp -r overlay_rootfs-minX/* mnt/
sudo dd if=/dev/zero of=mnt/swap bs=1M count=128
sudo mkswap mnt/swap
sudo echo "/swap swap swap defaults 0 0" >> mnt/etc/fstab
sync
sudo umount /dev/sdb2
启动系统:

使用串口工具连接LicheePiZero,注意rx接tx,tx接rx,插入电脑,打开串口工具,我使用的是PuTTY_0.67.0.0.exe。有屏幕的插入屏幕。

https://box.kancloud.cn/7a8728436e5decf4813a54e407bd5489_1194x620.jpg
https://box.kancloud.cn/6daaa82f6d0b7f626195b4814f7fd978_674x330.jpg
https://box.kancloud.cn/9d03405f1227e42ca4cd918e695b0c5c_984x571.jpg

输入账号root密码licheepi,登陆到系统。

Zero i2c oled使用指南

1. 硬件连接

一般市面上买到的单色oled屏幕模块一般都是同时支持spi和i2c接口的,而默认一般都是spi接口模式,需要根据说明书调整模模块上电阻的位置来转换到i2c模式。我手中这个模块一般比较常见,分辨率是128*64,需要焊接R1和R4,注意下方那个电阻位置要短接。

https://box.kancloud.cn/b5224472a382d668d6cf0c12f0892538_739x557.jpg
https://box.kancloud.cn/ce7036790bfaf95c2cf550c4bdac079b_833x431.jpg

然后连接到zero的i2c0的位置,reset引脚我连接到了uart2的tx脚位置。我手中暂时没有dock,如果是使用dock,请根据实际情况插到对应位置。

2. ssd1307fb驱动配置

Oled使用的控制芯片是 ssd1306,最新版本的linux中包含Ssd1306的i2c驱动,驱动加载后会注册成功linux framebuffer,驱动文件路径是: /drivers/video/fbdev/ssd1307fb.c ,该驱动可以通过配置支持ssd130x系列芯片。

所以要使用oled只需要在dts配置好就可以了

进入linux目录调出配置菜单

/linux-zero-4.10.y$ make ARCH=arm menuconfig

选中 <*> Solomon SSD1307 framebuffer support

https://box.kancloud.cn/aa91247dad30974e7c3058c7e5e28dea_850x391.jpg
https://box.kancloud.cn/7d224ee935d3bd91da53556f8af4a5ed_918x404.jpg

修改dts资源文件

vi arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts

添加 ssd1306fb-i2c 节点,0x3c 是i2c设备的地址,reset-getio是复位脚我选择的是**PB0**

https://box.kancloud.cn/0f551b1cb47649be68f5dd5629a7ea90_755x450.jpg

接下来编译内核和编译dtb

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j24 uImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

然后将内核和dtbs更新到zero上,插入串口上电。

我是将驱动直接编译到内核里,可以在内核日志中看到驱动加载成功。

https://box.kancloud.cn/eafc995e356935d537d55d8599a5b62c_1512x268.jpg

并且屏幕被点亮,该驱动默认不清空屏幕ram中内容,屏幕初始化后默认是花屏内容是正常的。如下状态。

https://box.kancloud.cn/c0bb555e7e7bfd8dc0a27cc56fc19514_630x545.jpg

驱动注册后一般会挂载为/dev/fb0或者/dev/fb1设备文件。

写一个简单的oled模块测试程序,读取屏幕信息,并刷将荔枝派logo显示上去效果如下:

https://box.kancloud.cn/5331e69baa77045940264e9f4c7d70ca_760x586.jpg

首先用photoshop做出目标图片,然后保存成bmp格式,然后使用windows自带的画图工具打开bmp,然后点击另存为保存为1位位图格式。使用字模软件将bmp转换成程序用的字节序列,我用的是zimo221这款字模工具软件

https://box.kancloud.cn/66d0a0ff9a97c502f5e989765e2b921f_1196x802.jpg

注意在参数设置中要勾选字节倒叙选项。

下面写一个测试程序,加载驱动并把图像显示到屏幕上

C测试程序
 #include <unistd.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <linux/fb.h>
 #include <sys/mman.h>
 #include <stdlib.h>
 #include <string.h>

     /*seconds: the seconds; mseconds: the micro seconds*/
     void setTimer(int seconds, int mseconds)
 {
     struct timeval temp;
     temp.tv_sec = seconds;
     temp.tv_usec = mseconds;
     printf("timer1\n");
     select(0, NULL, NULL, NULL, &temp);
     printf("timer2\n");
     return ;
 }

 int main ()
 {
     char lichee[] = {
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0xE0,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0x60,0x00,0x06,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x07,0x3C,0x74,0xE0,0x81,0x03,0xE0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x04,0x22,0x8C,0x10,0x43,0x04,0xF0,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x04,0x42,0x84,0x10,0x22,0x0C,0xF8,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x04,0x03,0x84,0x08,0x22,0x08,0xFC,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x04,0x01,0x84,0x18,0x22,0x08,0xFE,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x04,0x01,0x84,0x18,0x20,0x00,0xFE,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x00,0x04,0x01,0x84,0x08,0x20,0x00,0xFE,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,
             0x40,0x10,0x04,0x03,0x84,0x18,0x20,0x00,0xFE,0xFF,0x00,0x00,0x00,0x00,0x00,0x08,
             0x40,0x10,0x04,0x02,0x84,0x10,0x22,0x00,0xFF,0xFF,0x01,0x00,0x00,0x00,0x00,0x0C,
             0x40,0x18,0x04,0x06,0x84,0x30,0x40,0x04,0xFF,0xFF,0x03,0x00,0x00,0x00,0x00,0x06,
             0xE0,0x0F,0x1F,0x1C,0xCE,0xC1,0x80,0x03,0xFF,0xFF,0x07,0x00,0x00,0x00,0x00,0x07,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x80,0x07,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xBF,0xFF,0x1F,0x00,0x00,0x00,0xC0,0x07,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xDF,0xFF,0x7F,0x00,0x00,0x00,0xE0,0x07,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC7,0xFF,0xFF,0x00,0x00,0x00,0xF0,0x03,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xC9,0xFF,0xFF,0x01,0x00,0x00,0xF0,0x03,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0xC0,0xFF,0xFF,0x03,0x00,0x00,0xF8,0x03,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xC0,0xFF,0xFF,0x07,0x00,0x00,0xF0,0x03,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0xC0,0xF7,0xFF,0x0F,0x00,0x00,0xF8,0x03,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xE3,0xFF,0xFF,0x1F,0x00,0x00,0xF8,0x01,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xEF,0xFF,0xFF,0x7F,0x00,0x00,0xF8,0x01,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFB,0xFF,0xFF,0xFF,0x00,0x00,0xFC,0x01,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0xFF,0xFF,0xFF,0x03,0x00,0xFC,0x00,
             0x40,0x30,0x00,0x06,0x04,0x00,0x00,0x80,0xFF,0xFF,0xFF,0xFF,0x07,0x00,0xFE,0x00,
             0x40,0x10,0x02,0x06,0x04,0xC0,0x00,0x9C,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0xFF,0x00,
             0xFE,0xFF,0x07,0x06,0x04,0x80,0xC8,0x81,0xFF,0xFF,0xFF,0xFF,0x3F,0x00,0x7F,0x00,
             0x40,0x10,0x00,0x06,0x04,0x80,0x08,0x00,0xFF,0xFF,0xFF,0xFF,0x7F,0x80,0x7F,0x00,
             0x40,0x13,0x00,0x16,0xCC,0x00,0x08,0x10,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x7F,0x00,
             0x00,0xC3,0xC0,0x3F,0x04,0x00,0x08,0x0E,0xFE,0xFF,0xFF,0xFF,0xFF,0xE3,0x3F,0x00,
             0x00,0xC1,0x00,0x06,0x04,0x10,0x48,0x00,0xFE,0xFF,0xFF,0xFF,0xFF,0xF7,0x3F,0x00,
             0x80,0x41,0x00,0x06,0x04,0x20,0x49,0x00,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x00,
             0x80,0x40,0x00,0x0E,0x44,0x20,0x48,0x30,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x00,
             0x60,0x78,0x00,0x9F,0x61,0x80,0x48,0x08,0xF8,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x00,
             0x38,0x30,0x00,0x17,0x60,0x80,0x48,0x02,0xF8,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x00,
             0x20,0x60,0x00,0x17,0x21,0x80,0x48,0x02,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0x00,
             0xFE,0xFB,0x83,0x06,0x30,0x40,0x48,0x02,0xE0,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0x00,
             0x30,0x23,0x02,0x06,0x12,0x70,0x44,0x04,0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,
             0x30,0x23,0x42,0x06,0x1A,0x60,0x44,0x04,0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0x00,
             0x10,0x31,0x02,0x06,0x0C,0x40,0x44,0x08,0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0x03,0x00,
             0x18,0x11,0x03,0x06,0x0E,0x60,0x42,0x18,0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0x00,
             0x88,0x19,0x03,0x06,0x31,0x60,0xC2,0x30,0xE0,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,
             0xC4,0xCD,0x01,0x86,0xE0,0x61,0x41,0x20,0xE0,0xFF,0xFF,0xFF,0xFF,0x7F,0x00,0x00,
             0x00,0x82,0x01,0x36,0x80,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0x1F,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x9F,0xFF,0xFF,0xFF,0x07,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0xFF,0xFF,0xFF,0x01,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFC,0xFF,0x7F,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0x0F,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0x00,
             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
     };
     int fp=0;
     long screensize=0;

     char *fbp = 0;

     struct fb_var_screeninfo vinfo;
     struct fb_fix_screeninfo finfo;
     fp = open ("/dev/fb1",O_RDWR);

     if (fp < 0){
             printf("Error : Can not open framebuffer device/n");
             exit(1);
     }

     if (ioctl(fp,FBIOGET_FSCREENINFO,&finfo)){
             printf("Error reading fixed information/n");
             exit(2);
     }

     if (ioctl(fp,FBIOGET_VSCREENINFO,&vinfo)){
             printf("Error reading variable information/n");
             exit(3);
     }

     printf("The mem is :%d\n",finfo.smem_len);
     printf("The line_length is :%d\n",finfo.line_length);
     printf("The xres is :%d\n",vinfo.xres);
     printf("The yres is :%d\n",vinfo.yres);
     printf("bits_per_pixel is :%d\n",vinfo.bits_per_pixel);

     screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
     printf("screensize: %d\n",screensize);
     fbp =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
             fp,0);
     if ((int) fbp == -1)
     {
             printf ("Error: failed to map framebuffer device to memory./n");
             exit (4);
     }

     int i=0;
     memset(fbp, 0x00, 1024);
     setTimer(3,0);
     while(1){
             for(i=0;i<1024;i++){
                     fbp[i]=lichee[i];
             }
             setTimer(3,0);
             memset(fbp, 0x00, 1024);
             setTimer(3,0);
     }
     munmap (fbp, screensize);
     close (fp);
 }

将字节序在程序中定义为数组在荔枝板中,编译程序并执行。

https://box.kancloud.cn/533e4d22e84e1eaa42218f37d0b43dbd_648x299.jpg

3. 单片机驱动一直为i2c驱动

一般情况下使用ssb1307fb这个驱动就很完美了了,我在发现ssd1307fb这个驱动程序之前,我将显示屏厂家提供的stm8 i2c测试代码移植到了linux上实现了一个驱动,加载驱动后效果如下

https://box.kancloud.cn/0bff0424a51d86f32442b91c374174d1_703x631.jpg

我将此驱动也发出来供参考,该驱动程序直接在linux i2c设备注册中实现了oled的测试显示,该驱动包含一个基本ascii的字库和字符串显示逻辑。

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/fs.h>
#include <asm/uaccess.h>


#define SSD1306_CMD    0
#define SSD1306_DAT    1

#define SSD1306_WIDTH    128
#define SSD1306_HEIGHT  64

#define SSD1306_TS_NAME "ssd1306_ts"

static uint8_t s_chDispalyBuffer[128][8];

const uint8_t c_chFont1608[95][16] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xCC,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
{0x00,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x00,0x00},/*""",2*/
{0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x00,0x00},/*"#",3*/
{0x00,0x00,0x0E,0x18,0x11,0x04,0x3F,0xFF,0x10,0x84,0x0C,0x78,0x00,0x00,0x00,0x00},/*"$",4*/
{0x0F,0x00,0x10,0x84,0x0F,0x38,0x00,0xC0,0x07,0x78,0x18,0x84,0x00,0x78,0x00,0x00},/*"%",5*/
{0x00,0x78,0x0F,0x84,0x10,0xC4,0x11,0x24,0x0E,0x98,0x00,0xE4,0x00,0x84,0x00,0x08},/*"&",6*/
{0x08,0x00,0x68,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x18,0x18,0x20,0x04,0x40,0x02,0x00,0x00},/*"(",8*/
{0x00,0x00,0x40,0x02,0x20,0x04,0x18,0x18,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x00},/*")",9*/
{0x02,0x40,0x02,0x40,0x01,0x80,0x0F,0xF0,0x01,0x80,0x02,0x40,0x02,0x40,0x00,0x00},/*"*",10*/
{0x00,0x80,0x00,0x80,0x00,0x80,0x0F,0xF8,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x00},/*"+",11*/
{0x00,0x01,0x00,0x0D,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
{0x00,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80},/*"-",13*/
{0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
{0x00,0x00,0x00,0x06,0x00,0x18,0x00,0x60,0x01,0x80,0x06,0x00,0x18,0x00,0x20,0x00},/*"/",15*/
{0x00,0x00,0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"0",16*/
{0x00,0x00,0x08,0x04,0x08,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"1",17*/
{0x00,0x00,0x0E,0x0C,0x10,0x14,0x10,0x24,0x10,0x44,0x11,0x84,0x0E,0x0C,0x00,0x00},/*"2",18*/
{0x00,0x00,0x0C,0x18,0x10,0x04,0x11,0x04,0x11,0x04,0x12,0x88,0x0C,0x70,0x00,0x00},/*"3",19*/
{0x00,0x00,0x00,0xE0,0x03,0x20,0x04,0x24,0x08,0x24,0x1F,0xFC,0x00,0x24,0x00,0x00},/*"4",20*/
{0x00,0x00,0x1F,0x98,0x10,0x84,0x11,0x04,0x11,0x04,0x10,0x88,0x10,0x70,0x00,0x00},/*"5",21*/
{0x00,0x00,0x07,0xF0,0x08,0x88,0x11,0x04,0x11,0x04,0x18,0x88,0x00,0x70,0x00,0x00},/*"6",22*/
{0x00,0x00,0x1C,0x00,0x10,0x00,0x10,0xFC,0x13,0x00,0x1C,0x00,0x10,0x00,0x00,0x00},/*"7",23*/
{0x00,0x00,0x0E,0x38,0x11,0x44,0x10,0x84,0x10,0x84,0x11,0x44,0x0E,0x38,0x00,0x00},/*"8",24*/
{0x00,0x00,0x07,0x00,0x08,0x8C,0x10,0x44,0x10,0x44,0x08,0x88,0x07,0xF0,0x00,0x00},/*"9",25*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0C,0x03,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
{0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
{0x00,0x00,0x00,0x80,0x01,0x40,0x02,0x20,0x04,0x10,0x08,0x08,0x10,0x04,0x00,0x00},/*"<",28*/
{0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x00,0x00},/*"=",29*/
{0x00,0x00,0x10,0x04,0x08,0x08,0x04,0x10,0x02,0x20,0x01,0x40,0x00,0x80,0x00,0x00},/*">",30*/
{0x00,0x00,0x0E,0x00,0x12,0x00,0x10,0x0C,0x10,0x6C,0x10,0x80,0x0F,0x00,0x00,0x00},/*"?",31*/
{0x03,0xE0,0x0C,0x18,0x13,0xE4,0x14,0x24,0x17,0xC4,0x08,0x28,0x07,0xD0,0x00,0x00},/*"@",32*/
{0x00,0x04,0x00,0x3C,0x03,0xC4,0x1C,0x40,0x07,0x40,0x00,0xE4,0x00,0x1C,0x00,0x04},/*"A",33*/
{0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x11,0x04,0x0E,0x88,0x00,0x70,0x00,0x00},/*"B",34*/
{0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x08,0x1C,0x10,0x00,0x00},/*"C",35*/
{0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"D",36*/
{0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x17,0xC4,0x10,0x04,0x08,0x18,0x00,0x00},/*"E",37*/
{0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x17,0xC0,0x10,0x00,0x08,0x00,0x00,0x00},/*"F",38*/
{0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x44,0x1C,0x78,0x00,0x40,0x00,0x00},/*"G",39*/
{0x10,0x04,0x1F,0xFC,0x10,0x84,0x00,0x80,0x00,0x80,0x10,0x84,0x1F,0xFC,0x10,0x04},/*"H",40*/
{0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x00,0x00,0x00,0x00},/*"I",41*/
{0x00,0x03,0x00,0x01,0x10,0x01,0x10,0x01,0x1F,0xFE,0x10,0x00,0x10,0x00,0x00,0x00},/*"J",42*/
{0x10,0x04,0x1F,0xFC,0x11,0x04,0x03,0x80,0x14,0x64,0x18,0x1C,0x10,0x04,0x00,0x00},/*"K",43*/
{0x10,0x04,0x1F,0xFC,0x10,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x0C,0x00,0x00},/*"L",44*/
{0x10,0x04,0x1F,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x1F,0xFC,0x10,0x04,0x00,0x00},/*"M",45*/
{0x10,0x04,0x1F,0xFC,0x0C,0x04,0x03,0x00,0x00,0xE0,0x10,0x18,0x1F,0xFC,0x10,0x00},/*"N",46*/
{0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"O",47*/
{0x10,0x04,0x1F,0xFC,0x10,0x84,0x10,0x80,0x10,0x80,0x10,0x80,0x0F,0x00,0x00,0x00},/*"P",48*/
{0x07,0xF0,0x08,0x18,0x10,0x24,0x10,0x24,0x10,0x1C,0x08,0x0A,0x07,0xF2,0x00,0x00},/*"Q",49*/
{0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x11,0xC0,0x11,0x30,0x0E,0x0C,0x00,0x04},/*"R",50*/
{0x00,0x00,0x0E,0x1C,0x11,0x04,0x10,0x84,0x10,0x84,0x10,0x44,0x1C,0x38,0x00,0x00},/*"S",51*/
{0x18,0x00,0x10,0x00,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x00,0x18,0x00,0x00,0x00},/*"T",52*/
{0x10,0x00,0x1F,0xF8,0x10,0x04,0x00,0x04,0x00,0x04,0x10,0x04,0x1F,0xF8,0x10,0x00},/*"U",53*/
{0x10,0x00,0x1E,0x00,0x11,0xE0,0x00,0x1C,0x00,0x70,0x13,0x80,0x1C,0x00,0x10,0x00},/*"V",54*/
{0x1F,0xC0,0x10,0x3C,0x00,0xE0,0x1F,0x00,0x00,0xE0,0x10,0x3C,0x1F,0xC0,0x00,0x00},/*"W",55*/
{0x10,0x04,0x18,0x0C,0x16,0x34,0x01,0xC0,0x01,0xC0,0x16,0x34,0x18,0x0C,0x10,0x04},/*"X",56*/
{0x10,0x00,0x1C,0x00,0x13,0x04,0x00,0xFC,0x13,0x04,0x1C,0x00,0x10,0x00,0x00,0x00},/*"Y",57*/
{0x08,0x04,0x10,0x1C,0x10,0x64,0x10,0x84,0x13,0x04,0x1C,0x04,0x10,0x18,0x00,0x00},/*"Z",58*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFE,0x40,0x02,0x40,0x02,0x40,0x02,0x00,0x00},/*"[",59*/
{0x00,0x00,0x30,0x00,0x0C,0x00,0x03,0x80,0x00,0x60,0x00,0x1C,0x00,0x03,0x00,0x00},/*"\",60*/
{0x00,0x00,0x40,0x02,0x40,0x02,0x40,0x02,0x7F,0xFE,0x00,0x00,0x00,0x00,0x00,0x00},/*"]",61*/
{0x00,0x00,0x00,0x00,0x20,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00},/*"^",62*/
{0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01},/*"_",63*/
{0x00,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/
{0x00,0x00,0x00,0x98,0x01,0x24,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xFC,0x00,0x04},/*"a",65*/
{0x10,0x00,0x1F,0xFC,0x00,0x88,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"b",66*/
{0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x00},/*"c",67*/
{0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x11,0x08,0x1F,0xFC,0x00,0x04},/*"d",68*/
{0x00,0x00,0x00,0xF8,0x01,0x44,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xC8,0x00,0x00},/*"e",69*/
{0x00,0x00,0x01,0x04,0x01,0x04,0x0F,0xFC,0x11,0x04,0x11,0x04,0x11,0x00,0x18,0x00},/*"f",70*/
{0x00,0x00,0x00,0xD6,0x01,0x29,0x01,0x29,0x01,0x29,0x01,0xC9,0x01,0x06,0x00,0x00},/*"g",71*/
{0x10,0x04,0x1F,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"h",72*/
{0x00,0x00,0x01,0x04,0x19,0x04,0x19,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"i",73*/
{0x00,0x00,0x00,0x03,0x00,0x01,0x01,0x01,0x19,0x01,0x19,0xFE,0x00,0x00,0x00,0x00},/*"j",74*/
{0x10,0x04,0x1F,0xFC,0x00,0x24,0x00,0x40,0x01,0xB4,0x01,0x0C,0x01,0x04,0x00,0x00},/*"k",75*/
{0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"l",76*/
{0x01,0x04,0x01,0xFC,0x01,0x04,0x01,0x00,0x01,0xFC,0x01,0x04,0x01,0x00,0x00,0xFC},/*"m",77*/
{0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"n",78*/
{0x00,0x00,0x00,0xF8,0x01,0x04,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0xF8,0x00,0x00},/*"o",79*/
{0x01,0x01,0x01,0xFF,0x00,0x85,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"p",80*/
{0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x05,0x01,0xFF,0x00,0x01},/*"q",81*/
{0x01,0x04,0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x04,0x01,0x00,0x01,0x80,0x00,0x00},/*"r",82*/
{0x00,0x00,0x00,0xCC,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x98,0x00,0x00},/*"s",83*/
{0x00,0x00,0x01,0x00,0x01,0x00,0x07,0xF8,0x01,0x04,0x01,0x04,0x00,0x00,0x00,0x00},/*"t",84*/
{0x01,0x00,0x01,0xF8,0x00,0x04,0x00,0x04,0x00,0x04,0x01,0x08,0x01,0xFC,0x00,0x04},/*"u",85*/
{0x01,0x00,0x01,0x80,0x01,0x70,0x00,0x0C,0x00,0x10,0x01,0x60,0x01,0x80,0x01,0x00},/*"v",86*/
{0x01,0xF0,0x01,0x0C,0x00,0x30,0x01,0xC0,0x00,0x30,0x01,0x0C,0x01,0xF0,0x01,0x00},/*"w",87*/
{0x00,0x00,0x01,0x04,0x01,0x8C,0x00,0x74,0x01,0x70,0x01,0x8C,0x01,0x04,0x00,0x00},/*"x",88*/
{0x01,0x01,0x01,0x81,0x01,0x71,0x00,0x0E,0x00,0x18,0x01,0x60,0x01,0x80,0x01,0x00},/*"y",89*/
{0x00,0x00,0x01,0x84,0x01,0x0C,0x01,0x34,0x01,0x44,0x01,0x84,0x01,0x0C,0x00,0x00},/*"z",90*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x3E,0xFC,0x40,0x02,0x40,0x02},/*"{",91*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00},/*"|",92*/
{0x00,0x00,0x40,0x02,0x40,0x02,0x3E,0xFC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"}",93*/
{0x00,0x00,0x60,0x00,0x80,0x00,0x80,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x20,0x00},/*"~",94*/
};

struct i2c_client *ssd1306_client;

static void ssd1306_write_byte(uint8_t chData, uint8_t chCmd)
{
    uint8_t cmd = 0x00;

    if (chCmd) {
        cmd = 0x40;
    } else {
        cmd = 0x00;
    }

    i2c_smbus_write_byte_data(ssd1306_client, cmd, chData);
}

void ssd1306_display_on(void)
{
    ssd1306_write_byte(0x8D, SSD1306_CMD);
    ssd1306_write_byte(0x14, SSD1306_CMD);
    ssd1306_write_byte(0xAF, SSD1306_CMD);
}

/**
  * @brief  OLED turns off
  *
  * @param  None
  *
  * @retval  None
**/
void ssd1306_display_off(void)
{
    ssd1306_write_byte(0x8D, SSD1306_CMD);
    ssd1306_write_byte(0x10, SSD1306_CMD);
    ssd1306_write_byte(0xAE, SSD1306_CMD);
}

void ssd1306_refresh_gram(void)
{
    uint8_t i, j;

    for (i = 0; i < 8; i ++) {
        ssd1306_write_byte(0xB0 + i, SSD1306_CMD);
        ssd1306_write_byte(0x02, SSD1306_CMD);
        ssd1306_write_byte(0x10, SSD1306_CMD);
        for (j = 0; j < 128; j ++) {
            ssd1306_write_byte(s_chDispalyBuffer[j][i], SSD1306_DAT);
        }
    }
}


void ssd1306_clear_screen(uint8_t chFill)
{
    memset(s_chDispalyBuffer,chFill, sizeof(s_chDispalyBuffer));
    ssd1306_refresh_gram();
}

/**
  * @brief  Draws a piont on the screen
  *
  * @param  chXpos: Specifies the X position
  * @param  chYpos: Specifies the Y position
  * @param  chPoint: 0: the point turns off    1: the piont turns on
  *
  * @retval None
**/

void ssd1306_draw_point(uint8_t chXpos, uint8_t chYpos, uint8_t chPoint)
{
    uint8_t chPos, chBx, chTemp = 0;

    if (chXpos > 127 || chYpos > 63) {
        return;
    }
    chPos = 7 - chYpos / 8; //
    chBx = chYpos % 8;
    chTemp = 1 << (7 - chBx);

    if (chPoint) {
        s_chDispalyBuffer[chXpos][chPos] |= chTemp;

    } else {
        s_chDispalyBuffer[chXpos][chPos] &= ~chTemp;
    }
}

/**
  * @brief  Fills a rectangle
  *
  * @param  chXpos1: Specifies the X position 1 (X top left position)
  * @param  chYpos1: Specifies the Y position 1 (Y top left position)
  * @param  chXpos2: Specifies the X position 2 (X bottom right position)
  * @param  chYpos3: Specifies the Y position 2 (Y bottom right position)
  *
  * @retval
**/

void ssd1306_fill_screen(uint8_t chXpos1, uint8_t chYpos1, uint8_t chXpos2, uint8_t chYpos2, uint8_t chDot)
{
    uint8_t chXpos, chYpos;

    for (chXpos = chXpos1; chXpos <= chXpos2; chXpos ++) {
        for (chYpos = chYpos1; chYpos <= chYpos2; chYpos ++) {
            ssd1306_draw_point(chXpos, chYpos, chDot);
        }
    }

    ssd1306_refresh_gram();
}


/**
  * @brief Displays one character at the specified position
  *
  * @param  chXpos: Specifies the X position
  * @param  chYpos: Specifies the Y position
  * @param  chSize:
  * @param  chMode
  * @retval
**/
void ssd1306_display_char(uint8_t chXpos, uint8_t chYpos, uint8_t chChr, uint8_t chSize, uint8_t chMode)
{
    uint8_t i, j;
    uint8_t chTemp, chYpos0 = chYpos;

    chChr = chChr - ' ';
    for (i = 0; i < chSize; i ++) {
        if (chMode) {
            chTemp = c_chFont1608[chChr][i];
        } else {
            chTemp = ~c_chFont1608[chChr][i];
        }

        for (j = 0; j < 8; j ++) {
            if (chTemp & 0x80) {
                ssd1306_draw_point(chXpos, chYpos, 1);
            } else {
                ssd1306_draw_point(chXpos, chYpos, 0);
            }
            chTemp <<= 1;
            chYpos ++;

            if ((chYpos - chYpos0) == chSize) {
                chYpos = chYpos0;
                chXpos ++;
                break;
            }
        }
    }
}

/**
  * @brief  Displays a string on the screen
  *
  * @param  chXpos: Specifies the X position
  * @param  chYpos: Specifies the Y position
  * @param  pchString: Pointer to a string to display on the screen
  *
  * @retval  None
**/
void ssd1306_display_string(uint8_t chXpos, uint8_t chYpos, const uint8_t *pchString, uint8_t chSize, uint8_t chMode)
{


printk("%s, ssd1306 str = %s\n", __func__, pchString);
    while (*pchString != '\0') {
        if (chXpos > (SSD1306_WIDTH - chSize / 2)) {
            chXpos = 0;
            chYpos += chSize;
            if (chYpos > (SSD1306_HEIGHT - chSize)) {
                chYpos = chXpos = 0;
                ssd1306_clear_screen(0x00);
            }
        }

        ssd1306_display_char(chXpos, chYpos, *pchString, chSize, chMode);
        chXpos += chSize / 2;
        pchString ++;
    }
}


void ssd1306_init(void)
{
    ssd1306_write_byte(0xAE, SSD1306_CMD);//--turn off oled panel
    ssd1306_write_byte(0x00, SSD1306_CMD);//---set low column address
    ssd1306_write_byte(0x10, SSD1306_CMD);//---set high column address
    ssd1306_write_byte(0x40, SSD1306_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    ssd1306_write_byte(0x81, SSD1306_CMD);//--set contrast control register
    ssd1306_write_byte(0xCF, SSD1306_CMD);// Set SEG Output Current Brightness
    ssd1306_write_byte(0xA1, SSD1306_CMD);//--Set SEG/Column Mapping
    ssd1306_write_byte(0xC0, SSD1306_CMD);//Set COM/Row Scan Direction
    ssd1306_write_byte(0xA6, SSD1306_CMD);//--set normal display
    ssd1306_write_byte(0xA8, SSD1306_CMD);//--set multiplex ratio(1 to 64)
    ssd1306_write_byte(0x3f, SSD1306_CMD);//--1/64 duty
    ssd1306_write_byte(0xD3, SSD1306_CMD);//-set display offset    Shift Mapping RAM Counter (0x00~0x3F)
    ssd1306_write_byte(0x00, SSD1306_CMD);//-not offset
    ssd1306_write_byte(0xd5, SSD1306_CMD);//--set display clock divide ratio/oscillator frequency
    ssd1306_write_byte(0x80, SSD1306_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
    ssd1306_write_byte(0xD9, SSD1306_CMD);//--set pre-charge period
    ssd1306_write_byte(0xF1, SSD1306_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    ssd1306_write_byte(0xDA, SSD1306_CMD);//--set com pins hardware configuration
    ssd1306_write_byte(0x12, SSD1306_CMD);
    ssd1306_write_byte(0xDB, SSD1306_CMD);//--set vcomh
    ssd1306_write_byte(0x40, SSD1306_CMD);//Set VCOM Deselect Level
    ssd1306_write_byte(0x20, SSD1306_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
    ssd1306_write_byte(0x02, SSD1306_CMD);//
    ssd1306_write_byte(0x8D, SSD1306_CMD);//--set Charge Pump enable/disable
    ssd1306_write_byte(0x14, SSD1306_CMD);//--set(0x10) disable
    ssd1306_write_byte(0xA4, SSD1306_CMD);// Disable Entire Display On (0xa4/0xa5)
    ssd1306_write_byte(0xA6, SSD1306_CMD);// Disable Inverse Display On (0xa6/a7)
    ssd1306_write_byte(0xAF, SSD1306_CMD);//--turn on oled panel

    ssd1306_display_on();
    ssd1306_clear_screen(0xff);

}


static int ssd1306_ts_probe(struct i2c_client *client,
                           const struct i2c_device_id *id)
{

 printk("ssd1306_ts_probe\n");
 printk("probe %s\n",__func__);
printk("%s, addr = %x, line = %d\n", __func__, client->addr, client->adapter->nr);

printk("%s, name = %s, adapter nr = %d\n", __func__, client->adapter->name, client->adapter->nr);


    ssd1306_client = client;

        ssd1306_init();

        ssd1306_clear_screen(0x00);
    //ssd1306_display_off();
ssd1306_display_on();
    ssd1306_display_string(18, 0, "Welcome!", 16, 1);
    ssd1306_display_string(0, 16, "zero i2c driver", 16, 1);
    ssd1306_refresh_gram();
   // ssd1306_display_on();

        return 0;
};


static const struct i2c_device_id ssd1306_ts_id[] = {
        { "ssd1306", 0 },
        { }
};
MODULE_DEVICE_TABLE(i2c, ssd1306_ts_id);

static struct i2c_driver ssd1306_ts_driver = {
        .probe = ssd1306_ts_probe,
        .id_table = ssd1306_ts_id,
        .driver = {
                .name = SSD1306_TS_NAME,
        },
};
module_i2c_driver(ssd1306_ts_driver);
MODULE_LICENSE("GPL");

相应dts配置如下:

https://box.kancloud.cn/9a68c7be1bf1c3b3b9b9809cf18c156c_537x310.jpg

Lichee Pi zero SPI LCD使用指南

在阅读本文之前,首先要保证你能够成功的编译linux内核,并构建一个完整的根文件系统。关于这部分的知识,之后还会单独写一个文档讨论。

其实lichee pi zero使用的4.10内核已经包含了市面上常见的SPI液晶屏的驱动(fbtft),我们所要做的仅仅是在设备树中添加节点。说起fbtft,它之前独立于内核存在过一段时间,作为单独的代码文件发布,如果你需要它,可以手动把这部分文件复制到内核源码中<参考 fbtft的 github仓库 >。后来被并入内核,具体是在哪一个版本的被并入内核的,这个也不必深究了。不过目前为止fbtft并未转正,依然存放在drivers/staging目录中。

1. 配置内核添加fbtft驱动

使用make menuconfig配置内核,加入ili9341驱动。fbtft还支持更多型号的SPI总线的液晶屏。关于支持列表这里就不一一列出,可以进入menuconfig中查看。

Device Drivers  --->
        [*] Staging drivers  --->
        <*>   Support for small TFT LCD display modules  --->
                        <*>   FB driver for the ILI9341 LCD Controller
                <*>   Generic FB driver for TFT LCD displays

编译内核:

make -j4

2. 修改设备树注册ili9341

lichee pi zero默认注册40Pin RGB液晶屏,并且在启动参数中设置console为tty0 。为了尽可能减少改动,我们在设备树中删除了默认的40Pin液晶屏,这样新添加的ili9341也就顺利成章的成了唯一的太子,启动时的信息会通过他显示。

下面是使用git对比改动前后的细节:

diff --git a/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts b/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
index 929a79e..9c91f75 100644
--- a/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
+++ b/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
@@ -90,3 +90,21 @@
        usb0_id_det-gpio = <&pio 5 6 GPIO_ACTIVE_HIGH>;
        status = "okay";
};
+
+&spi0 {
+       status = "okay";
+
+       ili9341@0 {
+               compatible = "ilitek,ili9341";
+               reg = <0>;
+
+               spi-max-frequency = <15000000>;
+               rotate = <270>;
+               bgr;
+               fps = <10>;
+               buswidth = <8>;
+               reset-gpios = <&pio 1 7 GPIO_ACTIVE_LOW>;
+               dc-gpios = <&pio 1 5 GPIO_ACTIVE_LOW>;
+               debug = <0>;
+       };
+};
diff --git a/arch/arm/boot/dts/sun8i-v3s.dtsi b/arch/arm/boot/dts/sun8i-v3s.dtsi
index 50b8788..b0eb22e 100644
--- a/arch/arm/boot/dts/sun8i-v3s.dtsi
+++ b/arch/arm/boot/dts/sun8i-v3s.dtsi
@@ -54,15 +54,6 @@
                #address-cells = <1>;
                #size-cells = <1>;
                ranges;
-
-               simplefb_lcd: framebuffer@0 {
-                       compatible = "allwinner,simple-framebuffer",
-                                    "simple-framebuffer";
-                       allwinner,pipeline = "de0-lcd0";
-                       clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_BUS_DE>,
-                                <&ccu CLK_DE>, <&ccu CLK_TCON0>;
-                       status = "disabled";
-               };

注解

  • dc-gpios = <&pio 1 5 GPIO_ACTIVE_LOW>;

在设备树中,PA对应&pio 0, PB对应&pio 1, 以此类推。因此dc-gpios实际表示的是PB5,也就是zero丝印上的PWM1。

注解

  • reset-gpios = <&pio 1 7 GPIO_ACTIVE_LOW>;

如果我的屏幕的RESET引脚连接了高电平,或者接了一个RC回路作为上电复位的信号,那么这里的复位引脚是不是可以不指定呢?

这样也是不可以的。因为在程序中,首先读取reset-gpios,若reset-gpios在设备树中不存在,那么直接忽略其余的信号。这样导致无法控制最关键的dc-gpios引脚。因此至少在不更改程序的前提下,这条信号是一定要写上的。

static int fbtft_request_gpios_dt(struct fbtft_par *par)
{
    int i;
    int ret;

    if (!par->info->device->of_node)
        return -EINVAL;

    ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset);
    if (ret)
        return ret;
    ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc);
    if (ret)
        return ret;

3. 更新SD卡中的程序

假设你之前已经创建好了一张可以正确启动的SD卡,那么你要做的很简单:

arch/arm/boot/zImagearch/arm/boot/dts/sun8i-v3s-licheepi-zero.dtb 拷贝到SD卡中的vfat分区,覆盖之前的程序即可。

4. 硬件连接

SPI屏 zero
3v3 3v3
GND GND
DC PWM1
RST 3v3
CS CS
CLK CLK
MISO MISO
MOSI MOSI

5. 上电启动

linux内核启动时会加载fbtft驱动,注册framebuffer设备,打印如下信息:

[    0.860671] fbtft_of_value: buswidth = 8
[    0.864653] fbtft_of_value: debug = 0
[    0.868325] fbtft_of_value: rotate = 270
[    0.872252] fbtft_of_value: fps = 10

[    1.244063] graphics fb0: fb_ili9341 frame buffer, 320x240, 150 KiB video memory, 16 KiB DMA buffer memory, fps=10, spi32766.0 at 15 MHz

显示效果如下图:

https://box.kancloud.cn/9e26c8e7c46319e83c14a660691ca535_4160x3120.jpg

Zero u-boot编译和使用指南

概要:
本文主要介绍Uboot的结构,menuconfig里的常见配置选项,编译步骤等

1. Uboot的基本结构

  • 下面来看看该uboot中的目录结构
├── api                存放uboot提供的API接口函数
├── arch               平台相关的部分我们只需要关心这个目录下的ARM文件夹
│   ├──arm
│   │   └──cpu
│   │   │   └──armv7
│   │   └──dts
│   │   │   └──*.dts 存放设备的dts,也就是设备配置相关的引脚信息
├── board              对于不同的平台的开发板对应的代码
├── cmd                顾名思义,大部分的命令的实现都在这个文件夹下面。
├── common             公共的代码
├── configs            各个板子的对应的配置文件都在里面,我们的Lichee配置也在里面
├── disk               对磁盘的一些操作都在这个文件夹里面,例如分区等。
├── doc                参考文档,这里面有很多跟平台等相关的使用文档。
├── drivers            各式各样的驱动文件都在这里面
├── dts                一种树形结构(device tree)这个应该是uboot新的语法
├── examples           官方给出的一些样例程序
├── fs                 文件系统,uboot会用到的一些文件系统
├── include            头文件,所有的头文件都在这个文件夹下面
├── lib                一些常用的库文件在这个文件夹下面
├── Licenses           这个其实跟编译无关了,就是一些license的声明
├── net                网络相关的,需要用的小型网络协议栈
├── post              上电自检程序
├── scripts           编译脚本和Makefile文件
├── spl               second program loader,即相当于二级uboot启动。
├── test              小型的单元测试程序。
└── tools             里面有很多uboot常用的工具。

了解了uboot的基本结构,我们可以知道一些相关的配置在什么地方了。

  • lichee的uboot配置文件放在confgs文件目录下面,名称为
LicheePi_Zero_480x272LCD_defconfig
LicheePi_Zero_800x480LCD_defconfig
LicheePi_Zero_defconfig

这3个配置是根据不同的Zero显示设备进行的配置,使用其中之一即可,可以在uboot目录下执行命令

make LicheePi_Zero_defconfig

这样配置就生效了。

  • 关于设备的配置引脚信息可以在arch/arm/dts的设备树下面进行查找。

通过查看arch/arm/dts/Makefile我们看到下面这段关于v3s的代码:

dtb-$(CONFIG_MACH_SUN8I_V3S) += \
    sun8i-v3s-licheepi-zero.dtb

我们基本可以找到对应的dtb文件就是sun8i-v3s-licheepi-zero.dtb

打开 sun8i-v3s-licheepi-zero.dts (dtb是object文件,相当于*.o, dts相当于*.c)文件

#include "sun8i-v3s.dtsi"                  //这个文件包含了sun8i-v3s系列的配置
#include "sunxi-common-regulators.dtsi"
/ {
        model = "Lichee Pi Zero";
        compatible = "licheepi,licheepi-zero", "allwinner,sun8i-v3s";

        aliases {
                serial0 = &uart0;
        };

        chosen {
                stdout-path = "serial0:115200n8";
        };
};
&mmc0 {
        pinctrl-0 = <&mmc0_pins_a>;
        pinctrl-names = "default";
        broken-cd;
        bus-width = <4>;
        vmmc-supply = <&reg_vcc3v3>;
        status = "okay";
};

&uart0 {
        pinctrl-0 = <&uart0_pins_a>;
        pinctrl-names = "default";
        status = "okay";
};

&usb_otg {
        dr_mode = "otg";
        status = "okay";
};

&usbphy {
        usb0_id_det-gpio = <&pio 5 6 GPIO_ACTIVE_HIGH>;
        status = "okay";
};

从这个配置文件中可以看出,我们所用的stdout输出是用的uart0

波特率115200,mmc的配置, uart0的引脚采用的是uart0_pins_a 等。

如若需要修改对应的输出,可以在这个文件中修改。

3. 编译步骤

上面已经讲述了如何把配置文件进行生成.config文件。做好这两件事情之后就可以编译了。

官方git上给的命令是:

time make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 2>&1 | tee build.log

第一个time命令完全可以去掉,time主要为了计算该编译需要花费的时间

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

这个之前的config也说了,其实就是给变量ARCH赋值arm

给CROSS_COMPILE赋值arm-linux-gnueabihf-

也可以先省去,剩下的: make 2>&1 | tee build.log

tee 这个命令是把make生成的log写入到build.log文件中,这样编译之后的log文件可以保留存有备份,也可以省去。

make 2>&1

查了下资料 数字2对应stderr 数字1对应stdout

这里即将标准err输出 &作为连接符表示‘与’的意思,即标准输出和标准error输出都进行输出。

其实真正执行编译的是下面的命令:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

小伙伴可以试下,该命令是否可以编译出 u-boot-sunxi-with-spl.bin 文件,其他的命令都是辅助编译的命令,用于调试的时候开启。亲测直接 make CROSS_COMPILE=arm-linux-gnueabihf- 也是可以的。

FAQ:下面的内容可能其他章节会讲到,所以下面就大致讲下编译环境的搭建

本文所描述的uboot是基于Lichee_Pi/uboot.git的版本

先下载代码,执行下面的命令。

先要配置好编译环境可以参照 开发环境搭建

操作系统官方默认是在Ubuntu 14.04 64bit的环境下编译的。

我试了下在Ubuntu16.04.1的ubuntu的版本下进行编译。

装好虚拟机操作系统之后,我们先安装一些依赖包

sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev libc6-dev lib32ncurses5-dev gcc-multilib x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev g++-multilib mingw32 tofrodos python-markdown libxml2-utils
sudo apt-get install gcc-arm-linux-gnueabihf

执行完这两条命令之后,如果安装成功,应该是可以编译的,当然我在尝试的时候最后mingw32会出现一些问题。

可能对于ubuntu16.04版本还要根据

git上 编译坑_Ubuntu16.04编译过程踩坑记录 来修改gcc的版本.然后呢根据

编译坑_Ubuntu16.04编译过程踩坑记录二

来修改部分的mingw32相关的编译选项,对了后面编译uboot的时候还会遇到dtc编译版本低的问题,所以在此还要执行 sudo apt-get install device-tree-compiler 的命令升级dtc。至此编译环境基本搭建好了。

这些都是楼主踩过的坑,感谢楼主,我们应该不需要踩了。还是挺有用的。

接着就可以下载uboot的源代码: https://github.com/Lichee-Pi/u-boot.git

执行这段命令uboot的源代码就下下来了。

TF WiFi使用方法

TF WiFi的硬件安装

TF WiFi可以使用插卡形式或者堆叠形式。

插卡形式安装

直接在Zero上插卡形式安装,会占用SDC0接口,所以只能从SPI Flash启动。

此时,仅能使用WiFi功能,BT由于未接线而无法使用。

https://box.kancloud.cn/7fbc4fa97325397229005658b7ff5822_1116x854.png
堆叠形式安装

对于小体积应用,推荐使用堆叠形式安装,有多种安装方式:

前置步骤

注意,初版Zero有一个阻值错误,导致WiFi无法启用,为下图红圈处电阻。 修复方法为: 去除该电阻(推荐) ,或者更换成510欧电阻。

https://box.kancloud.cn/337af04ad7b806e06cf6aaf906d1af41_654x427.png
堆叠焊接于顶部

此为默认的安装形式如图所示,焊接上方SDIO WiFi的8个引脚,下方BT的4个引脚。

https://box.kancloud.cn/12bd3ae7fa89edd264ff5e19109076ee_1024x768.jpg

TF WiFi 2.54间距的焊盘,可以直接插入插针焊接。

BT有2个1.27偏移的焊盘,建议焊接方法如下所示:

取2Pin 排针,插入焊盘,接于半孔上,焊接上下两侧,最后用剪刀或者钳子去除多余部分。

https://box.kancloud.cn/b75f78d370c6938e3923442b267407c1_1024x768.jpg

此焊接方式,可使得板子最为紧凑,但缺点是发热大户CPU和WiFi直接贴合在一起,可导致温度达到60度以上。

堆叠焊接于底部

堆叠焊于底部,可使散热情况优于前者,但需要注意两点:

  1. 下图红圈处需要剪断,否则无法插屏幕
  2. 天线要弯过来,或者重新焊接到背面。
https://box.kancloud.cn/7eac090889135ebbfd7c749252f57e86_584x968.png
堆叠插拔形式

如果不想把TF WiFi焊死上去,可以使用排座焊接到TF WiFi上,插拔使用:

https://box.kancloud.cn/2930b42d1bb14685b34e7aab276e0629_1163x757.png

WiFi使用方法

网盘中提供的镜像的root目录下提供了r8723bs.ko, 开机后,执行

insmod r8723bs.ko
ifconfig wlan0 up

来启动无线接口

编辑好 /etc/wpa_supplicant.conf 后,执行 connect_wx.sh 来连接网络。

蓝牙使用方法

下载或clone下面的源码,编译 https://github.com/NextThingCo/rtl8723bs_bt

首先在dts里使能UART:

sun8i-v3s.dtsi:
 uart0_pins_a: uart0@0 { pins = "PB8", "PB9";function = "uart0";bias-pull-up; };
 uart1_pins_a: uart1@0 { pins = "PE21", "PE22";function = "uart1";bias-pull-up; };
 uart2_pins_a: uart2@0 { pins = "PB0", "PB1";function = "uart2";bias-pull-up; };
 sun8i-v3s-licheepi-zero.dts:
 &uart0 { pinctrl-0 = <&uart0_pins_a>;pinctrl-names = "default";status = "okay"; };
 &uart1 { pinctrl-0 = <&uart1_pins_a>;pinctrl-names = "default";status = "okay"; };
 &uart2 { pinctrl-0 = <&uart2_pins_a>;pinctrl-names = "default";status = "okay"; };

再在内核里使能蓝牙功能:

[]Networking support->Bluetooth subsystem support
[]Networking support->Bluetooth subsystem support->Bluetooth device->
HC UART driver->Three-wire UART (H5) protocol support

最后就可以在终端里开启蓝牙,扫描使用设备了:

./start_bt.sh ttyS2
hciconfig -a
hciconfig hci0 up
hcitool scan

常见WiFi错误排查

wifi驱动加载成功,但是ifconfig -a没有wlan0
root@LicheePi:~# insmod r8723bs.ko
[   37.054004] r8723bs: loading out-of-tree module taints kernel.
[   37.090084] RTL8723BS: module init start
[   37.094173] RTL8723BS: rtl8723bs v4.3.5.5_12290.20140916_BTCOEX20140507-4E40
[   37.101496] RTL8723BS: rtl8723bs BT-Coex version = BTCOEX20140507-4E40
[   37.108377] RTL8723BS: module init ret =0
root@LicheePi:~# ifconfig -a
lo        Link encap:Local Loopback
        inet addr:127.0.0.1  Mask:255.0.0.0
        UP LOOPBACK RUNNING  MTU:65536  Metric:1
        RX packets:0 errors:0 dropped:0 overruns:0 frame:0
        TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
        collisions:0 txqueuelen:1000
        RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

usb0      Link encap:Ethernet  HWaddr 96:c0:f5:f1:ab:22
        BROADCAST MULTICAST  MTU:1500  Metric:1
        RX packets:0 errors:0 dropped:0 overruns:0 frame:0
        TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
        collisions:0 txqueuelen:1000
        RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

检查第一分区中的boot.scr是否正确:

root@LicheePi:~# mount /dev/mmcblk0p1 /mnt/
root@LicheePi:~# ls /mnt/
boot.scr                     sun8i-v3s-licheepi-zero-dock.dtb  zImage
sun8i-v3s-licheepi-zero-ctc.dtb  sun8i-v3s-licheepi-zero.dtb
root@LicheePi:~# cat /mnt/boot.scr
'V·z?Xցܣ]Mڳetenv bootargs console=ttyS0,115200 panic=5 console=tty0 rootwait root=/dev/mmcblk0p2 earlyprintk rw
load mmc 0:1 0x41000000 zImage
load mmc 0:1 0x41800000 sun8i-v3s-licheepi-zero-ctc.dtb   <--这里使用了错误的dtb
bootz 0x41000000 - 0x41800000

从网盘中获取正确的dtb来覆盖,具有wifi功能的dtb是sun8i-v3s-licheepi-zero-dock.dtb

wifi驱动加载失败
root@LicheePi:~# insmod r8723bs.ko
insmod: ERROR: could not insert module r8723bs.ko: Invalid module format

使用了错误内核的wifi模块,请到网盘或者qq群文件里重新下载最新的ko文件加载。

间隔1s左右打印sdio RTO 0x52之类的信息

wifi模块焊接不良,或者没有去除之前提到的电阻。

Licheepi Zero Ethernet适配指南

1. U-Boot适配Ethernet

U-Boot 2017已经支持了sun8i-emac的驱动,只需要在编译时选上并且修改dts就行。

  • 进入u-boot源码目录:
$ make LicheePi_Zero_defconfig
$ make menuconfig
https://box.kancloud.cn/b23c8b14ce63d6f887aa2372e68db411_972x582.png
  • 选择 Device Drivers --->
https://box.kancloud.cn/a2fa42f1f4fad923ac8734b2e1626a0a_972x582.png
  • 选择 Network device support ---> 并选中 Allwinner Sun8i Ethernet MAC support
https://box.kancloud.cn/a4718b669f8b9bd3daa750d3a5095e26_972x582.png
  • 修改dts
sun8i-v3s-licheepi-zero.dts:
 diff --git a/arch/arm/dts/sun8i-v3s-licheepi-zero.dts b/arch/arm/dts/sun8i-v3s-licheepi-zero.dts
 index 3d9168c..b8b9fc3 100644
 --- a/arch/arm/dts/sun8i-v3s-licheepi-zero.dts
 +++ b/arch/arm/dts/sun8i-v3s-licheepi-zero.dts
 @@ -49,6 +49,7 @@
         compatible = "licheepi,licheepi-zero", "allwinner,sun8i-v3s";

         aliases {
 +               ethernet0 = &emac;
                 serial0 = &uart0;
         };

 @@ -81,3 +82,14 @@
         usb0_id_det-gpio = <&pio 5 6 GPIO_ACTIVE_HIGH>;
         status = "okay";
  };
 +
 +&emac {
 +       phy = <&phy0>;
 +       phy-mode = "mii";
 +       allwinner,use-internal-phy;
 +       allwinner,leds-active-low;
 +       status = "okay";
 +       phy0: ethernet-phy@0 {
 +               reg = <1>;
 +       };
 +};
sun8i-v3s.dtsi:
 diff --git a/arch/arm/dts/sun8i-v3s.dtsi b/arch/arm/dts/sun8i-v3s.dtsi
 index ebefc0f..cb81dd5 100644
 --- a/arch/arm/dts/sun8i-v3s.dtsi
 +++ b/arch/arm/dts/sun8i-v3s.dtsi
 @@ -96,6 +96,11 @@
                 #size-cells = <1>;
                 ranges;

 +               syscon: syscon@01c00000 {
 +                       compatible = "allwinner,sun8i-h3-syscon","syscon";
 +                       reg = <0x01c00000 0x34>;
 +               };
 +
                 mmc0: mmc@01c0f000 {
                         compatible = "allwinner,sun7i-a20-mmc";
                         reg = <0x01c0f000 0x1000>;
 @@ -208,6 +213,17 @@
                         interrupt-controller;
                         #interrupt-cells = <3>;

 +                       emac_rgmii_pins: emac0@0 {
 +                               allwinner,pins = "PD0", "PD1", "PD2", "PD3",
 +                                               "PD4", "PD5", "PD7",
 +                                               "PD8", "PD9", "PD10",
 +                                               "PD12", "PD13", "PD15",
 +                                               "PD16", "PD17";
 +                               allwinner,function = "emac";
 +                               allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 +                               allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 +                       };
 +
                         uart0_pins_a: uart0@0 {
                                 pins = "PB8", "PB9";
                                 function = "uart0";
 @@ -270,6 +286,20 @@
                         status = "disabled";
                 };

 +               emac: ethernet@1c30000 {
 +                       compatible = "allwinner,sun8i-h3-emac";
 +                       reg = <0x01c30000 0x104>, <0x01c00030 0x4>;
 +                       reg-names = "emac", "syscon";
 +                       interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
 +                       resets = <&ccu RST_BUS_EMAC>, <&ccu RST_BUS_EPHY>;
 +                       reset-names = "ahb", "ephy";
 +                       clocks = <&ccu CLK_BUS_EMAC>, <&ccu CLK_BUS_EPHY>;
 +                       clock-names = "ahb", "ephy";
 +                       #address-cells = <1>;
 +                       #size-cells = <0>;
 +                       status = "disabled";
 +               };
 +
                 gic: interrupt-controller@01c81000 {
                         compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic";
                         reg = <0x01c81000 0x1000>,
  • 编译:

    $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

  • 烧写:

    $ dd if=u-boot-sunxi-with-spl.bin of=${card} bs=1024 seek=8

  • 使用Ethernet:

https://box.kancloud.cn/e0b2b778262db0e1b19e3316d39ac9cb_1054x417.png

2. Kernel适配Ethernet

目前Linux 4.12还没又对sun8i-emac进行支持,所以Kernel要使用V3s的以太网要打sun8i-emac的补丁还有修改dts文件。

  • 打上sun8i-emac补丁:

拉下我已经适配好的内核源码:https://github.com/techping/linux/tree/licheepi-zero

  • 修改dts(上面git仓库是已经修改完的):
sun8i-v3s-licheepi-zero.dts:
 index 387fc2a..904e60e 100644
 --- a/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
 +++ b/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts
 @@ -50,6 +50,7 @@

         aliases {
                 serial0 = &uart0;
 +               ethernet0 = &emac;
         };

         chosen {
 @@ -101,3 +102,10 @@
         usb0_id_det-gpio = <&pio 5 6 GPIO_ACTIVE_HIGH>;
         status = "okay";
 };
 +
 +&emac {
 +       phy-handle = <&int_mii_phy>;
 +       phy-mode = "mii";
 +       allwinner,leds-active-low;
 +       status = "okay";
 +};
sun8i-v3s.dtsi:
 diff --git a/arch/arm/boot/dts/sun8i-v3s.dtsi b/arch/arm/boot/dts/sun8i-v3s.dtsi
 index 7107596..65be2ab 100644
 --- a/arch/arm/boot/dts/sun8i-v3s.dtsi
 +++ b/arch/arm/boot/dts/sun8i-v3s.dtsi
 @@ -40,7 +40,10 @@
 *     OTHER DEALINGS IN THE SOFTWARE.
 */

 +#include <dt-bindings/clock/sun8i-v3s-ccu.h>
 +#include <dt-bindings/reset/sun8i-v3s-ccu.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 +#include <dt-bindings/pinctrl/sun4i-a10.h>

 / {
         #address-cells = <1>;
 @@ -93,6 +96,12 @@
                 #size-cells = <1>;
                 ranges;

 +               syscon: syscon@1c00000 {
 +                       compatible = "allwinner,sun8i-h3-system-controller",
 +                               "syscon";
 +                       reg = <0x01c00000 0x1000>;
 +               };
 +
                 mmc0: mmc@01c0f000 {
                         compatible = "allwinner,sun7i-a20-mmc";
                         reg = <0x01c0f000 0x1000>;
 @@ -205,6 +214,17 @@
                         interrupt-controller;
                         #interrupt-cells = <3>;

 +                       emac_rgmii_pins: emac0@0 {
 +                               allwinner,pins = "PD0", "PD1", "PD2", "PD3",
 +                                               "PD4", "PD5", "PD7",
 +                                               "PD8", "PD9", "PD10",
 +                                               "PD12", "PD13", "PD15",
 +                                               "PD16", "PD17";
 +                               allwinner,function = "emac";
 +                               allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 +                               allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 +                       };
 +
                         i2c0_pins: i2c0 {
                                 pins = "PB6", "PB7";
                                 function = "i2c0";
 @@ -295,6 +315,31 @@
                         #size-cells = <0>;
                 };

 +               emac: ethernet@1c30000 {
 +                       compatible = "allwinner,sun8i-h3-emac";
 +                       syscon = <&syscon>;
 +                       reg = <0x01c30000 0x104>;
 +                       interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
 +                       interrupt-names = "macirq";
 +                       resets = <&ccu RST_BUS_EMAC>;
 +                       reset-names = "stmmaceth";
 +                       clocks = <&ccu CLK_BUS_EMAC>;
 +                       clock-names = "stmmaceth";
 +                       #address-cells = <1>;
 +                       #size-cells = <0>;
 +                       status = "disabled";
 +
 +                       mdio: mdio {
 +                               #address-cells = <1>;
 +                               #size-cells = <0>;
 +                               int_mii_phy: ethernet-phy@0 {
 +                                       compatible = "ethernet-phy-ieee802.3-c22";
 +                                       reg = <1>;
 +                                       clocks = <&ccu CLK_BUS_EPHY>;
 +                                       resets = <&ccu RST_BUS_EPHY>;
 +                               };
 +                       };
 +               };
                 gic: interrupt-controller@01c81000 {
                         compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic";
                         reg = <0x01c81000 0x1000>,
  • 进入内核目录:
$ make sunxi_defconfig ARCH=arm
$ make menuconfig ARCH=arm
https://box.kancloud.cn/49566fbdf9bdafecf00d32984956e0bd_972x582.png
  • 选择 Device Drivers --->
https://box.kancloud.cn/f2f193f4fe0609e120d1d2863135b6d5_972x582.png
  • 选择 Network device support --->
https://box.kancloud.cn/c167d76a317803d860fe4f27e76331ae_972x582.png
  • 选择 Ethernet driver support --->
https://box.kancloud.cn/d11a354dbc48c1ea12d6a30c69e5b668_972x582.png
  • 选中 Allwinner sun8i EMAC supportUse dwmac-sun8i bindings
https://box.kancloud.cn/5b505c36e4f002099b88e65f9682b8d0_972x582.png
  • 编译

    $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

得到 zImage 和 sun8i-v3s-licheepi-zero.dtb

  • 烧写

将内核、dtb、rootfs刷入sd卡中,启动:

$ ifup eth0
https://box.kancloud.cn/d7b86a2a5f8686786fb155528c0d09d0_1465x250.png

eth0启动成功!

https://box.kancloud.cn/8f807f27c88c2bff281629f8e7874398_1802x264.png

Ethernet驱动适配成功!

Qt5 移植到 licheepi zero

环境

  • host:Ubuntu14.04 64 位
  • target:licheepi(全志 v3s)
  • 文件系统: mindb
  • 交叉编译链: gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf.tar.xz
  • tslib 源代码: tslib-1.4.tar.gz
  • tslib 安装目标路径: /usr/local/tslib1.4
  • QT 源代码: qt-everywhere-opensource-src-5.4.1.tar.gz
  • QT 安装目标路径: /usr/local/qt5.4.1

安装步骤:

本文主要讲述 QT 的移植过程, tslib 的移植过程可以搜索《 tslib1.4 移植全过程》参考,如果不需要触摸屏,

可以不用移植 tslib。

1. 准备工作

确保以下软件已安装,

sudo apt-get install xorg-dev libfontconfig1-dev \
libfreetype6-dev libx11-dev libxcursor-dev libxext-dev \
libxfixes-dev libxft-dev libxi-dev libxrandr-dev libxrender-dev

2. 配置

  1. 解压源代码 qt-everywhere-opensource-src-5.4.1.tar.gz,并进入源代码文件夹
tar xzf qt-everywhere-opensource-src-5.4.1.tar.gz
cd qt-everywhere-opensource-src-5.4.1/
  1. 指定所用平台的 arm 架构以及交叉编译器:

    vi qtbase/mkspecs/linux-arm-gnueabi-g++/qmake.conf

改成以下内容,实际应需要根据自己的开发环境作出相应改变:

#
# qmake configuration for building with arm-linux-gnueabi-g++
#

MAKEFILE_GENERATOR = UNIX
CONFIG += incremental
QMAKE_INCREMENTAL_STYLE = sublib
QT_QPA_DEFAULT_PLATFORM = linuxfb
QMAKE_CFLAGS_RELEASE += -O2 -march=armv7-a
QMAKE_CXXFLAGS_RELEASE += -O2 -march=armv7-a
include(../common/linux.conf)
include(../common/gcc-base-unix.conf)
include(../common/g++-unix.conf)
QMAKE_INCDIR += /usr/local/tslib/include
QMAKE_LIBDIR += /usr/local/tslib/lib
执行modifications to g++.conf
QMAKE_CC = arm-linux-gnueabihf-gcc -lts
QMAKE_CXX = arm-linux-gnueabihf-g++ -lts
QMAKE_LINK = arm-linux-gnueabihf-g++ -lts
QMAKE_LINK_SHLIB = arm-linux-gnueabihf-g++ -lts
执行modifications to linux.conf
QMAKE_AR = arm-linux-gnueabihf-ar cqs
QMAKE_OBJCOPY = arm-linux-gnueabihf-objcopy
QMAKE_NM = arm-linux-gnueabihf-nm -P
QMAKE_STRIP = arm-linux-gnueabihf-strip
load(qt_config)
  1. 根据自己的实际需求配置 Qt(此处是使用 tslib 的编译):
./configure \
-prefix /usr/local/qt5.4.1 \
-confirm-license \
-opensource \
-release \
-make libs \
-xplatform linux-arm-gnueabi-g++ \
-optimized-qmake \
-pch \
-qt-sql-sqlite \
-qt-libjpeg \
-qt-libpng \
-qt-zlib \
-tslib \
-no-opengl \
-no-sse2 \
-no-openssl \
-no-nis \
-no-cups \
-no-glib \
-no-dbus \
-no-xcb \
-no-xcursor -no-xfixes -no-xrandr -no-xrender \
-no-separate-debug-info \
-make examples -nomake tools -nomake tests -no-iconv

3. 编译安装

sudo make && make install

4. 移植 Qt 到 licheepi 开发板

完成上述步骤后, qt5.4.1 将被安装到 /usr/local/qt5.4.1 中。然后将/usr/local/中的 qt5.4.1 复制到开发板的 /opt/目录中,将/usr/local/中的 tslib 复制到开发板的/usr/local/中。

5. 设置开发板 Qt 环境变量:

vi /etc/bash.bashrc

添加下面内容:

export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
export TSLIB_TSDEVICE=/dev/input/event1
export TSLIB_CONFFILE=/usr/local/tslib/etc/ts.conf
export TSLIB_PLUGINDIR=/usr/local/tslib/lib/ts
export TSLIB_CALIBFILE=/etc/pointercal
export LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/tslib/lib:/opt/qt5.4.1/lib
export PATH=/bin:/sbin:/usr/bin/:/usr/sbin:/usr/local/tslib/bin
export QT_QPA_PLATFORM_PLUGIN_PATH=/opt/qt5.4.1/plugins
export QT_QPA_PLATFORM=linuxfb:tty=/dev/fb0
export QT_QPA_FONTDIR=/opt/qt5.4.1/lib/fonts
export QT_QPA_GENERIC_PLUGINS=tslib:$TSLIB_TSDEVICE

保存退出后使上面的环境变量生效:

source /etc/bash.bashrc

6. 运行一些 example:

/opt/qt5.4.1/examples/touch/pinchzoom/pinchzoom
/opt/qt5.4.1/examples/svg/embedded/fluidlauncher/fluidlauncher

我们可以看到用 linuxfb 方式的运行的 QT 是没有窗体边框的,这是 qt5 的一个特点,似乎是其为了更好的转移到手机等移动终端。

测试程序时候可能会提示某些库文件不存在,可以拷贝 ubuntu 主机中的交叉编译器下相应的库文件到 licheepi 开发板,然后添加环境变量是之生效。 不知道的话就都拷过去吧。

Zero Spi Nor Flash启动系统制作指南

1. Uboot编译配置

首先规划flash分区,我测试用的flash大小为16M,型号为MX25L12873F,规划如下:

[第一分区1MB存放spl和uboot] 0x000000000000-0x000000100000 : "uboot"
[第二分区64KB存放dtb文件] 0x000000100000-0x000000110000 : "dtb"
[第三分区4MB存放内核] 0x000000110000-0x000000510000 : "kernel"
[剩余空间存放根文件系统] 0x000000510000-0x000001000000 : "rootfs"

由于目前Uboot环境变量固定存放在1MB位置之内,所有留给uboot的空间固定到flash前1MB的位置不变。每个分区的大小必须是擦除块大小的整数倍,MX25L12873F的擦除块大小是64KB。

准备uboot

下载包含spi驱动的体验版本uboot,该驱动目前尚未合并到主线

git clone -b v3s-spi-experimental https://github.com/Lichee-Pi/u-boot.git

执行 make ARCH=arm menuconfig 打开uboot菜单配置,进入到 Device Drivers > SPI Flash Support

注意看一下自己flash的厂家名称,例如选上Macronix SPI flash support用来支持我测试用的 flash:MX25L12873F

配置uboot默认环境变量,在文件 include/configs/sun8i.h 中添加默认bootcmd和bootargs的环境变量设置,注意添加的位置在 “#include <configs/sunxi-common.h>” 的前边。

https://box.kancloud.cn/b4cce3d6f353a3aabb326dab402d58a3_1642x622.jpg
vi include/configs/sun8i.h
#define CONFIG_BOOTCOMMAND   "sf probe 0; "                           \
                          "sf read 0x41800000 0x100000 0x10000; "  \
                          "sf read 0x41000000 0x110000 0x400000; " \
                          "bootz 0x41000000 - 0x41800000"

#define CONFIG_BOOTARGS      "console=ttyS0,115200 earlyprintk panic=5 rootwait " \
                             "mtdparts=spi32766.0:1M(uboot)ro,64k(dtb)ro,4M(kernel)ro,-(rootfs) root=31:03 rw rootfstype=jffs2"

该环境变量通过在bootargs中增加mtdparts内核参数来告诉内核flash的分区设置信息,并通过 root=31:03 来告诉内核rootfs的位置。

sf read 0x41800000 0x100000 0x10000; //从flash0x100000(1MB)位置读取dtb放到内存0x41800000偏移处。
sf read 0x41000000 0x110000 0x400000; //从flash0x110000(1MB+64KB)位置读取dtb放到内存0x41000000偏移处。
bootz 0x41000000 (内核地址)- 0x41800000(dtb地址) 启动内核
编译uboot
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4

2. linux内核编译配置

linux内核基于github上的版本 https://github.com/Lichee-Pi/linux.git

执行 make ARCH=arm menuconfig 打开内核菜单配置,进入到 Device Drivers ‣ Memory Technology Device (MTD) support,确保选择上mtd的 <*> Command line partition table parsing 支持,该项目用来解析uboot传递过来的flash分区信息。

https://box.kancloud.cn/3ed4fd5d601aceb7f896521ba4c67cf6_1430x862.jpg

修改dts配置添加spi flash节点

vi arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts

添加spi节点配置:

&spi0 {
        status ="okay";

        mx25l12805d:mx25l12805d@0 {
                compatible = "jedec,spi-nor";
                reg = <0x0>;
                spi-max-frequency = <50000000>;
                #address-cells = <1>;
                #size-cells = <1>;
        };

};
https://box.kancloud.cn/611c8c327abb212991c3d0c02b0cf6d8_954x809.jpg

添加对jffs2文件系统的支持,路径在

File systems ‣ Miscellaneous filesystems ‣ Journalling Flash File System v2 (JFFS2) support

https://box.kancloud.cn/3be64c60667c0aa3a906f095171d1fda_1396x746.png

退出菜单配置并编译内核和dts

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

3. 准备镜像文件

首先选择rootfs文件系统,我是用的是群朋提供的最小根文件系统rootfs-brmin.tar.gz,大小只有3M左右,下载地址在

Flash支持jffs2文件系统格式,所以需要使用此该rootfs制作jffs2文件系统镜像;

下载jffs2文件系统制作工具
apt-get install mtd-utils
解压 rootfs-brmin.tar.gz
tar xzvf rootfs-brmin.tar.gz

总空间是 16M-1M-64K-4M=0xAF0000

mkfs.jffs2 -s 0x100 -e 0x10000 -p 0xAF0000 -d rootfs/ -o jffs2.img

页大小 0x100 256 字节,块大小 0x10000 64k

jffs2分区总空间 0xAF0000

jffs2.img是生成的文件系统镜像。

最后将uboot,dtb,kernel,rootfs打包成一个系统镜像,命令如下:

dd if=/dev/zero of=flashimg.bin bs=16M count=1
dd if=../ubootmainline/u-boot/u-boot-sunxi-with-spl.bin of=flashimg.bin bs=1K conv=notrunc
dd if=../zero41y/linux-zero-4.10.y/arch/arm/boot/dts/sun8i-v3s-licheepi-zero-dock.dtb of=flashimg.bin bs=1K seek=1024  conv=notrunc
dd if=../zero41y/linux-zero-4.10.y/arch/arm/boot/zImage of=flashimg.bin bs=1K seek=1088  conv=notrunc
dd if=../zero_imager/jffs2/jffs2.img of=flashimg.bin  bs=1K seek=5184  conv=notrunc
  1. 生成一个空文件,大小是16MB
  2. 将uboot添加到文件开头
  3. 将dtb放到1M偏移处
  4. 将kernel放到1M+64K偏移处
  5. 将rootfs放到1M+64K+4M偏移处

小技巧

偏移大小是seek,单位是KB。

执行完毕后生成镜像文件flashimg.bin

4. 烧写镜像

下载sunxiflash烧写工具,

git clone -b spi-rebase https://github.com/Icenowy/sunxi-tools.git

注解

请选择spi-rebase分支

进入工具目录执行 make

Zero有一个usb下载模式称为fel模式。zero的启动顺序是先sd卡然后spi flash。如果sd卡启动检测失败(没有sd卡或者sd卡中没有启动ubootspl),然后spiflash也启动检测失败(没有spiflash或者spiflash中没有启动ubootspl),那么会自动进入fel模式。

所以当你的spiflash还没有启动镜像的时候你拔掉sd上电会自动进入fel模式。

如果你的spiflash已经有了启动镜像,那么需要在sd卡中烧入一个sunxi提供的启动工具( dd if=fel-sdboot.sunxi of=/dev/mmcblk0 bs=1024 seek=8 ),那么插入该sd卡启动会进入fel模式;还有另一种方法,擦除spiflash内容上点也会进入fel模式。

进入fel模式后使用usb数据线连接pc和zero,进入sunxi-tool目录

执行 sudo ./sunxi-fel version 会显示硬件版本信息。
执行 sudo ./sunxi-fel spiflash-info 会显示spi flash信息。

执行如下命令烧入我们前边制作好的镜像文件

sudo ./sunxi-fel -p spiflash-write 0 ../bootimg/flashimg.bin
https://box.kancloud.cn/30a15ac70a49ffa8e966700b72d91478_1088x83.jpg

等待大概5分钟,烧写完成,如果没有意外重新上电zero那么就会进入linux系统了,账号是root没有密码。

https://box.kancloud.cn/94cba1c9e4539c2e54836d28a8bbe12b_1281x1002.jpg

Visio-uboot-sunxi流程

注意

暂待修复

lichee 编译踩坑记录(ilichee ZERO)

1. 编译环境

编译的时候原来是正常编译过linux内核的,不过buildroot和里面带的程序还需要其他的一些软件包

需要安装的包
bison flex texinfo zlib1g-dev gawk
填坑方案
sudo apt-get install [包名称]

2. buildroot源码

由于各种各样的原因buildroot里到处是坑- -有的源码可能对新编译器不适应,需要修改

修改时有两个位置,如果还没有进行编译可以在 /buildroot/pakage/[包名称]/ 里找到对应文件/文件夹

编译时使用的是将上面的源码复制到 /out/linux/common/buildroot/build/[包名称]/out/linux/common/buildroot/build/host-[包名称] 下, 所以如果编译的过程中出错除了改buildroot里的源码也需要改后面那两个(如果有)文件夹下的源码

1) host-m4-1.4.15

buildroot过程中

./stdio.h:456:1:error: 'gets' undeclared here (not in a function)
_GL_WARN_ON_USE(gets, "gets is a security hole - use fgets instead");

填坑方法:参考链接:http://www.civilnet.cn/talk/browse.php?topicno=78555

找到: host-m4-1.4.15/lib/stdio.h ,然后对stdio.h文件做出如下改动,必要时连同 stdio.in.h 一起修改:

=== modified file 'host-m4-1.4.15/lib/stdio.h'
@@ -140,8 +140,10 @@
+#if defined gets
#undef gets
_GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
+#endif

修改后测试通过

2) host-autoconf-2.65

buildroot过程中

conftest.c:14625:must be after @defmac' to use@defmacx'

填坑方法:参考链接:http://gnu-autoconf.7623.n7.nabble.com/compile-error-conftest-c-14625-must-be-after-defmac-to-use-defmacx-td18843.html

找到 autoconf-2.65/doc/autoconf.texi ,进行修改

@@ -15,7 +15,7 @@
@c The ARG is an optional argument.  To be used for macro arguments in
@c their documentation (@defmac).
@macro ovar{varname}
-@r{[}@var{\varname}@r{]}@c
+@r{[}@var{\varname}@r{]}
@end macro
@c @dvar(ARG, DEFAULT)
@@ -23,7 +23,7 @@
@c The ARG is an optional argument, defaulting to DEFAULT.  To be used
@c for macro arguments in their documentation (@defmac).
@macro dvar{varname, default}
-@r{[}@var{\varname} = @samp{\default}@r{]}@c
+@r{[}@var{\varname} = @samp{\default}@r{]}
@end macro
@c Handling the indexes with Texinfo yields several different problems.

修改后测试通过

3) host-makedevs

buildroot过程中

/lichee/out/linux/common/buildroot/build/host-makedevs/makedevs.c:374:6: error: variable ‘ret’ set but not used [-Werror=unused-but-set-variable]
int ret = EXIT_SUCCESS;
^
cc1: all warnings being treated as errors

找到 /makedevs/makedevs.c/host-makedevs/makedevs.c 进行修改

最后一行, return 0; 修改为: return ret;

这也算(╯‵□′)╯︵┻━┻)

3. lichee缺rootfs.cpio.gz

buildroot后

无法定位 "rootfs.cpio.gz"

泽畔大大说可以自己包一个 我从H3的SDK里扒了一个出来 目前为止没有出错 放到linux内核目录下

4. awk: line 2: function strtonum never defined

awk版本问题,需要安装gawk

sudo apt-get install gawk

5. pack finish但无镜像输出

出现 Dragon execute image.cfg failed

lichee默认的对象是编译到nor flash上,但是没找到nor flash的配置文件o(╯□╰)o

修改**/lichee/tools/pack/pack**

354: -update_mbr    sys_partition_nor.bin 1 > /dev/null
355: -dragon image.cfg      sys_partition_nor.fex
354: +update_mbr    sys_partition.bin 1 > /dev/null
355: +dragon image.cfg      sys_partition.fex

用默认的配置文件 似乎是针对nand的

sudo ./build.sh
sudo ./build.sh pack

终于成功〒▽〒

lichee_zero_外设GPIO接口

注意

暂待修复

TF WIFI 小白编

材料

荔枝派Zero开发板TF WiFi模块

2. 焊接

  1. 要把wifi模块与zero正确连接(我用的是焊接于底部)
  2. 堆叠焊接于底部
  3. 堆叠焊于底部,可使散热情况优于前者,但需要注意两点:
  4. 下图红圈处需要剪断,否则无法插屏幕
  5. 天线要弯过来,或者重新焊接到背面。
https://box.kancloud.cn/7eac090889135ebbfd7c749252f57e86_584x968.png

3. 下载驱动

从网盘里下载r8723bs.ko驱动

网盘资料:链接: https://pan.baidu.com/s/1nv7Fq2X

>>> (建议在下载完后改名字,因为,这个名字里面有空格,因为空格有转义符,很容易搞错,打TAB比较方便,
但是在写自启脚本时TAB却是空格,所以,,,你懂的(・∀・))
注:名字一定要以.ko结尾

4. WiFi使用方法

1) 加载驱动并启用WiFi

在命令行里执行

insmod r8723bs.ko
sudo ifconfig wlan0 up
3) 输入下面命令编辑无线配置文件
vi /etc/wpa_supplicant.conf

然后将里面的下面部分删除

network={

}

然后保存修改,接着输入如下命令生成新的无线网络配置文件

wpa_passphrase 无线网络名称 无线密码 >> /etc/wpa_supplicant.conf

然后就成了,接着输入

./connect_wx.sh

看看是否成功连网,和ip地址

5. 成功加载驱动却没有wlan0

在命令行里执行

mount /dev/mmcblk0p1 /mnt/
cat /mnt/boot.scr

然后就会看到这个

'V·z?Xցܣ]Mڳetenv bootargs console=ttyS0,115200 panic=5 console=tty0 rootwait root=/dev/mmcblk0p2 earlyprintk rw
load mmc 0:1 0x41000000 zImage
load mmc 0:1 0x41800000 sun8i-v3s-licheepi-zero-ctc.dtb <--这里使用了错误的dtb
bootz 0x41000000 - 0x4180000

从网盘中获取正确的dtb来覆盖,具有wifi功能的dtb是 sun8i-v3s-licheepi-zero-dock.dtb (在网盘的zero_imager里面)

或者不用改。。把系统卡在电脑上读,在电脑里面改也行

zero_imager 里面有正确的boot.scr直接在电脑里面换上去就行了再把 sun8i-v3s-licheepi-zero-dock.dtb 复制到root目录

https://box.kancloud.cn/a2460fdea1379086c88ce304bcf310e8_843x785.png

6. 开机自动连网

首先写一个shell脚本来加载驱动,启动wlan0,运行连网脚本,放在任意目录都可以,输入命令:

vi wifi.sh

然后按a进入插入模式,输入:

#!/bin/sh
insmod /root(你放WiFi驱动的路径)/wifi.ko(WiFi驱动的名字)
ifconfig wlan0 up
/root/connect_wx.sh

写好后按下esc,再输入: wq

保存数据后输入: chmod 777 wifi.sh

然后添加开机启动脚本 vi /etc/rc.local

会显示这个

https://box.kancloud.cn/e19885964ed55aed33d7a4ae9e6aea67_664x549.png

然后把你的wifi自启脚本以绝对路径形式输入 exit 0 上面的一行中,如果路径太长,一行写不完,就在这一行的最后面输入 然后按下enter键

文档构建

前言

本文档选择使用Sphinx来进行构建,支持以 reStructuredTextMarkdown 格式编写

本地构建

1. 拉取文档

打开终端,运行以下命令:

git clone https://github.com/Lichee-Pi/lichee-pi-zero.git

2. 安装依赖

  • 首先我们需要通过pip安装Sphinx

    pip install Sphinx

    若您没有安装pip,请参照 pip 安装

  • 接着安装本文档的模块依赖

    pip install sphinx_rtd_theme 用于支持文档主题

    pip install recommonmark 用于支持Markdown文本格式

3. 构建

cd Lichee-Zero-Doc-zh-CN

# windows
.\make.bat html

# linux
make html

构建完成,进入 build ‣ html ‣ index.html

打开以浏览器浏览即可。

技术文档贡献

若您有意向贡献您的实践经验,请参照 todolist

以reStructuredText编写文档

.rst 文件是轻量级标记语言的一种,被设计为容易阅读和编写的纯文本,并且可以借助Docutils这样的程序进行文档处理,也可以转换为HTML或PDF等多种格式,或由Sphinx-Doc这样的程序转换为LaTex、man等更多格式。

在本文档中, .rst 文件配合sphinx工具以及readthedoc主题,具有较为丰富的文本表现。

此外,与Markdown对比来看:

  1. RST更适合于构建完整较大的文档,Markdown更适用于构建单页应用
  2. RST格式更为丰富,Markdown更为简洁
  3. RST格式要求稍高于Markdown

reStructuredText语法请参考 quick reStructuredText

个人建议您也可以通过查看 Read the Docs主题示例 ,配合其 github 的编辑源码模式,可更直观地进行对照、借鉴。

  • 另: RST的表格对于中文支持不好,个人推荐借助 pytablewriter 来生成中文表格
以下是使用示例:
# coding: utf-8
import pytablewriter

writer = pytablewriter.RstGridTableWriter()

writer.table_name = "example_table"
writer.header_list = ['水果', '价格', '数量']
writer.value_matrix = [
   ['香蕉', '1', '5'],
       ['苹果', '1', '6'],
       ['草莓', '1', '7'],
]

writer.write_table()
转换结果:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.. table::


  +----+----+----+
  |水果|价格|数量|
  +====+====+====+
  |香蕉|   1|   5|
  +----+----+----+
  |苹果|   1|   6|
  +----+----+----+
  |草莓|   1|   7|
  +----+----+----+

以Markdown编写文档

Markdown语句较为简明,互联网上也有大量的辅助工具与教程;

个人推荐您使用 vscode配合插件Markdown All in One,或使用 typora ,笔者使用体验较为舒适

一点小提醒

若您单纯使用Markdown书写,无需注意以下所有内容;

若您 想用Markdown而不涉及rst及其语法 构建您的 个人文档 时,建议您使用 Mkdocs 替代sphinx,参阅 readthedocs build process

若您将Markdown文件加入sphinx的构建行列,请注意以下两条:

  • 要使用sphinx所提供的特性时,如:

    小技巧

    15% if the service is good.

    错误

    Does not compute.

    请将其标为代码片段,代码类型为: eval_rst,sphinx将会将此片段作为rst文本进行解析:

    ```eval_rst
    
        .. Tip:: 15% if the service is good.
    
        .. Error:: Does not compute.
    
    ```
    
  • sphinx对Markdown的表格支持不够完全,请使用上一条所用方法,以rst语法来绘制表格