一、前言
相信如果有80/90/00的同学有可能看过《魔幻手机》里面的傻妞是很多人小时候梦寐以求的,那今天我们就来圆梦自己制作一款桌面AI小手机,我们做的这个手机长宽高比例是82x50x17,看起有点微胖所以我们叫它"胖妞手机"。 学习完本套项目对个人综合能力的提高非常有帮助。项目综合了前面学习的内容,并结合初学者对知识的接受程度由浅入深的带着大家学习一个硬件设计、焊接调试、驱动编写、系统定制、结构设计等一整个完整的项目。
二、硬件设计
0. 视频教程
【Linux手机硬件设计】立创·泰山派RK3566 Linux开发板训练营第七课
1. 电路原理分析
胖妞手机硬件主要由泰山派以及一个屏幕扩展板构成,我们这里主要是来分析这个扩展板的设计原理以及思路,扩展板主要由接口转换电路、背光电路、音频电路构成。
1.1 接口转换电路
为什么需要接口电路呢?主要是因为我们选的3.1寸屏幕它的mipi接口和触摸接口和我们泰山派的mipi和触摸接口都不一样,所以我们需要通过设计扩展板使信号线能够对应。
注意:
📌 接口原理图设计时的线序正反需要根据FPC的封装与实际接插情况来定。切勿画反。
3.1寸屏数据手册:
【D310T9362V1 SPEC->D310T9362V1 SPEC.pdf】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->数据手册
->3.1寸屏幕资料
->D310T9362V1 SPEC->D310T9362V1 SPEC.pdf
。
1.1.1 3.1寸屏MIPI接口
3.1寸屏幕的分辨率为480x800,使用的是MIPI DSI接口,屏幕排线为24个引脚,其中4、5、9脚为空不需要接。
1.1.2 泰山派MIPI屏接口
泰山派的MIPI接口是标准31PIN的,但因为我们我们扩展板上有些引脚并没有用上所以做了删减,这里泰山派上同网络标签和3.1寸屏幕的FPC接口直接相连,需要注意的是MIPI_DSI_VCC_LED+和MIPI_DSI_VCC_LED-背光引脚,此背光是由泰山派上的板载背光电路输出,他的输出电流是110mA我们3.1寸屏幕最大能承受的驱动电流是25mA所以不适合直接接到3.1寸屏幕的FPC上。
1.1.3 3.1寸屏触摸接口
3.1寸触摸屏接口使用的是i2C协议与泰山派进行通讯,除了i2C外还有两个比较重要的引脚分别是触摸复位引脚和触摸中断触发引脚
1.1.4 泰山派触摸接口
泰山派触摸接口以及功能如下所示,同名的标签和上面3.1寸触摸FPC链接。
1.2 背光电路
背光电路主要由、背光选择电路、背光驱动电路以及背光调节电路构成。
1.2.1 背光选择电路
背光电路分为两路:第一路是由泰山派输出,第二路:是板载的背光驱动输出,通过4个0欧姆电阻进行选择,如果贴(R103和R104)不贴(R105和R106)则由泰山派背光电路供电,不贴(R103和R104)贴(R105和R106)则由板载背光驱动电路供电。
前面章节我们已经计算过了泰山派上的板子背光驱动电路IOUT=0.2V/R(R=(R95xR96)/(R95+R96)),最终得出IOUT = 0.2V/1.8≈110mA,但我们这款3.1寸屏幕背光电流最大只能支持25mA,如果直接接到屏幕上有烧屏幕或者屏幕发烫的风险,实际测试中使用泰山派背光供电会出现屏幕非常发烫情况,所以我们默认不贴(R103和R104)贴(R105和R106)使用板载背光驱动供电。
1.2.2 板载背光电路
背光驱动电路主要由SY7201ABC实现,SY7201ABC是一款高效率的LED驱动器,主要用于控制和调节LED灯的亮度。SY7201ABC通过提供恒定电流来确保LED发光的一致性和稳定性,从而提高了LED使用的寿命和效能。
SY7201ABC数据手册:https://atta.szlcsc.com/upload/public/pdf/source/20160903/1472896628181.pdf
封装与引脚功能说明:
L15 D7 C255 与SY7201ABC组成 BOOST升压电路(主要是利用电感电容的储能特性 进行升压 (电感电流不能突变,与电容电压不能突变))
R99 R100 为采样电阻与参考电压进行对比恒定输出电流,输出电流的计算公式IOUT=0.2V/R,这里问了使R能够匹配更加精准的值我们并联了两个电阻R99和R100,其中R99为NC不贴,最终的IOUT=0.2V/10Ω=20mA
R102 R101为上下拉电阻默认下拉贴R101,这里两个电阻的作用Linux系统启动到背光驱动加载需要一些时间,也就意味着驱动加载之前这个IO口是不确定的,我们根据需要在没有驱动控制的时候通过上拉或者下拉电阻来决定屏幕背光关闭还是打开。
1.2.3 背光调节电路
因为我们泰山派没有PWM引脚引到3.1寸扩展板,但触摸接口有I2C1引到3.1寸扩展屏幕上,I2C是可以挂在多个设备的,所以为了能够实现背光调节功能,我们通过GP7101一颗I2C转PWM的芯片来实现PWM的调节,GP7101和触摸一起挂到I2C1下。
1.3 音频接口
1.3.1 喇叭
通过两个弹簧顶针(POGO PIN)与泰山派SPKP和SPKN连接,音频驱动电路由泰山派上的RK809-5实现。
1.3.2 麦克风
通过一个弹簧顶针(POGO PIN)与泰山派MIC连接,MIC相关的驱动电路集成在了泰山派上。
1. 原理图与PCB设计
在了解了3.1寸胖妞手机扩展板硬件电路后接下来进行原理图与PCB设计环节,原理图设计部分包含了元器件选型、元器件搜索以及原理图整理的内容;PCB设计部分包含边框结构设计、叠层设计、阻抗匹配、规则设置、模块化布局、PCB走线与设计检查、PCB生产与打样等内容。
2.1 原理图设计
2.1.1 工程创建
打开嘉立创EDA专业版软件:https://pro.lceda.cn/editor
登录账号后选择创建工程,输入工程名称:【泰山派RK3566】胖妞手机扩展板设计,系统会自动创建一个工程项目,接下来就在该项目中完成胖妞手机扩展板原理图与PCB设计等内容。
创建好工程后打开左侧工程列表中的Schematic1图页,在右侧画布区域放置元器件进行连接,底下的PCB1是用来绘制PCB图的页面,设计流程是先完成原理图的设计后再到PCB设计。
2.1.2 元器件搜索
元器件搜索的方式有三种途径,第一种可以在左侧的常用库中找到官方提供的参考库进行放置,优点是比较方便,缺点是库种类较少;第二种方式是通过顶部菜单栏中的放置按钮选项,可以看到器件实物图与参考价格、数据手册等信息;第三种方式是在软件底部面板中搜索放置器件,这种方式的好处是可以看到所选器件的符号、封装与3D模型效果图。
使用技巧:熟悉使用快捷键可以大大提升设计效率哦~
2.1.3 元器件放置
为了方便初学者学习,该项目提供了完成电路图中所需的元器件清单,可以直接根据器件清单中提供的器件编号及备注信息进行元器件的搜索和放置。以底部面板搜索器件为例,在原理图工作区打开底部库面板,选择器件类目,在搜索栏中输入器件编号,进行搜索找到对应元器件点击放置在画布中即可。
泰山派RK3566胖妞手机扩展板物料BOM下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->物流BOM
->泰山派RK3566胖妞手机扩展板物料BOM.xlsx
。
2.1.4 原理图整理
完成元器件放置后接下来进行电路图的连接与整理工作,参照以下电路图,完成元器件间的连接,使用网络标签可以替代导线连接,两个相同网络的位置放置相同网络标签即可。接下来按功能模块划分各个电路,使用矩形边框包围住各个电路模块,并用文本加上电路模块标识说明。最好使用设计菜单栏下的检查DRC功能查看电路连接是否有误。
2.2 PCB设计
2.2.1 原理图生成PCB
原理图设计好后需要把原理图更新转换到PCB中,右边框框中就是我们原图中对于元器件封装以及各网络之间的飞线。我们PCB Layout要做的就是按照规则将这些飞线用导线进行连接。
2.2.2 结构设计
在公司实际的项目中结构工程师会先输出结构图给我们,我们在基于结构工程师的结构要求来设计PCB的板框形状以及器件布局等,最后在和结构工程师共同敲定最终的布局,当然我们平时DIY没有结构要求基本上大家都是随心所欲画板框和布局的但因为泰山派胖妞手机3.1扩展板后面需要与泰山派、屏幕、喇叭、外壳其它配件等配合,所以我们对结构有严格的要求。所以在我们开始PCB设计时候先要把结构定下来。
2.2.2.1 板框结构文件
结构图我们已经根据外壳和装配情况提前设计好了大家直接下载导入就行,如果你不想使用我做好的结构你也可以自己手动去调整,比如装配3.1寸屏幕的时候FPC放在什么位置合适,定位孔放在什么位置刚好可以和泰山对上,这些需要你自己去测量。
【胖妞手机3.1寸扩展板结构图.dxf】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->结构文件
->胖妞手机3.1寸扩展板结构图.dxf
。
2.2.2.2 导入板框结构
在PCB界面中选择文件->导入->DXF,选择我们上面下载的胖妞手机3.1寸扩展板结构图.dxf文件,一定要在PCB界面如果在原理图界面导入DXF是不一样的。
预览一下看是否有问题,没有的话参数选择默认直接导入
把导入的结构文件放置到原点,这里导入以后默认是放在顶层走线层的。我们需要把其他器件调整到文档层板框设计成板框层。
通过属性面板把DXF文件中的板框改成板框层,把其他元器件改成文档层,修改完成以后全选并锁定结构文件。
2.2.3 叠层阻抗设计
做叠层阻抗匹配主要的目的是让信号完整性更好,一般当我们设计的板子上有高速信号存在的时候我们就需要去考虑信号的完整性。
我们3.1寸扩展屏幕上面有MIPI信号,MIPI属于高速信号但又因为我们这个屏幕尺寸很小速率也不高其实不做也没有关系,这里出于教学目的所以设计中会去考虑信号完整性,但实际打板测试的时候我会忽略并用2层板免费工艺进行打样测试。
2.2.3.1 阻抗要求
单端走线我们一般是50ohm ±10%加上我们3.1寸扩展板上面没有什么重要的单端信号所以调节可以放宽,这里比较重要的是MIPI DSI信号
2.2.3.2 阻抗计算
为了方便大家计算叠层阻抗,我们嘉立创为大家提供了一个阻抗计算器,大家在计算中可以快速的计算出你所需要参数对应的板材等。
嘉立创阻抗计算:https://tools.jlc.com/jlcTools/#/impedanceCalculateNew
我们的板子比较简单,走线在顶层和底层就够了,内部两层直接设为GND,给到顶层走线层和底层走线层参考,所以我们计算阻抗的时候各层参考。最终得到单端6.16mil,mipi差分4.88mil,线间距8mil。
2.2.4 规则设置
简单的板子一般不需要设置规则直接默认规则下设计即可,但是我们前面已经计算出来单端和差分相关的规则,如果我们不去设置规则通过手动调节来满足上面的线宽线距要求会非常费劲,比如我们定义了mipi差分对规则,我们在走mipi差分对的时候软件会自动帮我们设计好线宽以及线距线长度等。规则的作用很多也很实用,设计复杂的板子必须要有完事的规则,更多的规则相关内容需要大家在后续的学习中去进阶,你也可参考我们泰山派的设计里面我们有定义了大量的规则去辅助我们设计。
生产的时候会根据板子工艺复杂度来来收费,比如你使用了特殊线宽和过孔等所以你在设计规则的时候尽量选择满足自己并且性价比高的工艺规则。
2.2.4.1 距离规则
上面我们阻抗匹配计算的时候线距离选择的是8mil所以我们在规则中也把线到线距离设置为8mil
2.2.4.2 创建导线规则
通常我们会根据网络来设置走线规则,假如我们板子上信号线以及电源有N种我们需要评估这些线并给出合适的线宽规则,我们这只有两种情况,所以我们就创建两种走线规则,第一种是单端走线,第二种是电源线因为电流都不大我们就统一宽度。
单端走线就是普通的线比如GPIO这种,我们前面已经计算出了单端走线线宽为6.16mil,所有直接基于默认的修改就行。
电源走线规则,扩展板上的器件都是几十毫安非常小,所有我们统一创建一个名为pwr的规则,线宽设置为15mil给到所有的电源网络使用。
2.2.4.3 创建差分规则
前面计算出来的差分对线宽4.88mil所有我们这里设为4.9mil线宽,间距设置为8mil,需要注意的是差分对长度误差理论上是一样长最好,但不可能做到所以我们给设计的误差为10mil。
2.2.4.4 创建过孔规则
但大家在设计的时候尽量减少过孔的种类,理论上我们PCB上就一种尺寸的过孔规格是最好的,因为这样生产的时候机器不用换转头,一个转头就把所有的过孔搞定了。所有我们这里就直接使用默认的过孔规则。
2.2.5 规则生效
前面我们只是设置了规则,但是有些规则并不会生效,要让规则生效我们还需要在网络规则中去应用到指定的规则。
2.2.5.1 导线规则应用
把电源相关的网络都应用成PWR规则,其他的使用默认的单独网络规则。
2.2.5.2 差分对规则应用
网络类中选择差分对,按住ctrl+鼠标左键选择差分对,然后右键新建差分对。
弹出差分对管理器,创建差分对,总共有三对差分线都使用这种方法完成创建。
创建完成,可以看到规则这里就是应用了我们的差分对规则,点击应用生效。
2.2.6 PCB布局
布局在PCB整个设计中非常重要、布局会影响到整个板子的美观、是否方便走线、产品性能等,通常情况下我们布局的步骤是先抓模块、在处理结构器件、模块化布局、最后在优化调整。
2.2.6.1 抓模块
将原理图生成PCB后接下来进行元器件的布局与走线,刚转到PCB画布时元器件摆布是比较杂乱的,首先要做的是将元器件按电路功能进行分类,分类的方式是先在原理图页面对各个电路模块进行单独框选,然后选择“设计”菜单栏下的“布局传递”功能,传送到PCB将对应的元器件提取出来重新摆放,这一步是分类的关键。
这里以板载背光背光电路为例,原理图中全选板载背光电路,右键选择布局传递,这个时候会跳到PCB中并把所有板载背光电路相关的器件进行选中,最终我们把背光相关的模块放在一起,其他的模块依次操作,分完类以后我们大致可以看到所有模块之间飞线连接关系,这有利于我们对每个模块摆放位置的分析。
2.2.6.2 结构器件
在没有结构要求的情况下这一步可以跳过,但如果是产品基本上对结构都是有要求的,碰到对结构有要求的情况需先处理结构器件在开始模块化布局。完成结构器件布局以后建议进行锁定,防止后序误操作
2.2.6.3 模块化布局
布局时元器件相互连接处有一根淡蓝色的线条,这根线叫做飞线,它起的作用是告诉我们那两个焊盘是相同网络,需要使用导线连接,所以飞线也叫做指引线。在布局走线时可以将GND网络的飞线隐藏,使页面更简洁。隐藏方式是:在左侧“工程设计”列表中选择网络,在飞线列表中将和GND前的眼睛关闭即可。GND通常最后会通过铺铜方式进行链接,所以走完线后在打开GND飞线进行处理。
元器件属性是用来显示元器件位号的,但在布局中位号的存在会干扰我们布局,我们可以在右侧“过滤”列表中选择元件属性把眼睛关掉,板子最后调整丝印的时候我们在把位号显示出来处理。还有另外一种方法是把所有位号缩小然后居中到芯片中心。这里大家掌握第一种即可。
接下来布局时把相关模块电路放到一起,按照飞线的指引摆放,尽可能使飞线水平,走线时减少拐弯。最终布局效果如下所示:
2.2.7 PCB走线
2.2.7.1 基本规范
以前大家可能听过PCB走线不就是连连看么,但我更想告诉大家好的PCB Layout其实是复杂技术,它包含了信号完整性、电磁兼容性、生产制造工艺等,合理的走线不仅能够确保电路的稳定性和可靠性,还能提高生产效率和降低成本。最重要的是可以避免玄学。所以我们非常有必要了解一些基础的规范,下面我列举了一些比较常用的规范希望大家在设计中去应用巩固和拓展。
- 线宽与线间距(规则定义):
- 确定线宽以满足电流需求和热耗散,避免过热。
- 保持线与线之间、线与焊盘之间的适当间距,以防止潜在的短路问题。
- 对于高速信号,线间距应考虑3W原则,以减少串扰。
- 阻抗匹配(规则定义):
- 确保高速信号线的特性阻抗与驱动器和接收器的阻抗相匹配,以减少信号反射和传输损耗。
- 使用适当的终端电阻来补偿不匹配的阻抗。
- 走线方向:
- 避免急剧的转弯和90度角走线,以减少信号的反射和干扰。
- 尽可能使用平滑的曲线或斜角走线。
- 走线设计:
- 在顶层和底层之间使用过孔进行连接时,应尽量减少过孔的数量和尺寸,以降低信号完整性问题。
- 保持走线的直线性,避免不必要的弯曲和折叠。
- 对于两层板设计,优先考虑表层走线,以便于布线和检查。
- 防护设计:
- 对于敏感或重要的信号线,采用包地或防护带来减少外部干扰。
- 考虑在关键信号线旁边放置地线或地平面,以提供额外的屏蔽。
- 分区与隔离:
- 将数字信号和模拟信号、高速信号和低速信号分开布置,以减少它们之间的相互干扰。
- 利用内层走线(带状线)来提供更好的隔离效果,尤其是在信号完整性要求高的场合。
- 3W原则:
- 在设计高速信号线时,应用3W原则来设置线间距,即线间距至少为线宽的三倍,以减少串扰。
- 差分信号线设计:
- 保持差分对走线的间距一致,通常间距应为走线宽度的两倍。
- 确保差分对的走线等长,以减少时序误差和信号失真。
- 避免在差分对中引入任何不平衡,例如,避免在一条走线上使用过孔而在另一条上不使用。
- 等长要求:
- 对于高速信号和时钟信号,确保所有相关走线等长,以减少信号路径上的延迟差异。
- 使用绕线或蛇形走线来调整走线长度,以达到等长的要求。
- 靠近板边的走线规则:
- 避免在板边附近布置高速或敏感的信号线,因为板边容易受到外部干扰。
- 保持板边至少20mil(或走线宽度的两倍)的距离内没有走线,以防止在制造过程中由于机械应力导致的走线断裂。
- 如果必须在板边附近布置走线,应使用地线或地平面作为保护,以减少干扰。
- 其他考虑:
- 更具实际情况考虑。
2.2.7.2 走线
2.2.7.3 差分等长
MIPI的差分对属于高速信号需要特殊处理(实际屏幕小速率不高),在布线中选择差分对布线来出MIPI的差分线,并且没对走线中进行各地处理,差分对的误差需要在合理范围之内所以需要在布线中选择差分对长度调节来走蛇形线使差分在合理范围之内。
2.2.7.4 铺铜区域
打开GND网络飞线,在放置工作栏目选择放置铺通区域,铺通网络选择GND,铺铜完成后如果还存在飞线,可通过在存在飞线的位置放置对应网络的过孔或者是调整走线位置使网络能够连接,也可以采用手动接线的方式消除飞线。
2.2.8 整理
2.2.8.1 泪滴
走线完成后可在“工具”菜单栏选择泪滴添加,加强焊盘与走线的连接,最后再进行覆铜操作,如果对走线有移动调整也应使用快捷键Shift+B进行重建覆铜
2.2.8.2 丝印标注
为了方便调试和直观的了解开发板信息,我们把前面隐藏的位号打开并摆放规则整齐,还可以添加一些文字和图片来描述开发板相关信息。
2.2.8.3 检查DRC
完成走线后点击底部面板中的检查DRC按钮,若显示无报错警告,则代表该PCB设计完成。实际设计过程中往往可能出现各种错误,也可以通过检查DRC找到错误点进行修复,最后我们还是有13个错误这是封装导致的,原因是DRC检查到封装里面的焊盘距离挖槽小于6mil,我们可以通过修改封装或者规则来解决,但我们这里选择人为忽略。
3. 免费PCB打样
嘉立创自2019年起给全国电子工程师及爱好者普及PCB免费打样服务至今,伴随着电子工程师的成长,解决学习过程中打样贵、打样慢的问题。在完成PCB设计后我们可以大胆地将文件提交到嘉立创平台进行PCB的打样,每个月可以支持免费打样两次,每次可打一款板子,实际生产5片,重点是还全国包邮!接下来介绍如何在嘉立创平台进行免费的PCB制板服务。
3.1 优惠券领取
在进行PCB下单前需要先领取一张免费打样券,打开嘉立创下单平台的优惠券领券专区页面,嘉立创下单的账号与嘉立创EDA的账号通用,登录账号后即可领取优惠券:https://www.jlc.com/newOrder/#/collectCoupons
进入领券专区后,选择PCB+SMT喷锡免费券进行领取,每个月可领取两张,领取后30天内有效。这里的PCB+SMT的意思是可用于PCB和SMT而不是一定要用SMT才能用券,系统会随机抽取幸运儿,有极小概率抽中的话还可以免费体验工厂帮忙焊接元器件(SMT)的服务,如果能抽中那也是极好的!
3.2 生成制造文件
领取优惠券后回到PCB设计页面,点击顶部菜单栏中的选择“导出”选项,在里面可以选择导出物料清单(BOM)、PCB制板文件(Gerber)以及坐标文件,这三个文件是实际PCB设计生产中常用到的文件。其中物料清单是用于元器件采购以及SMT贴片时物料选择、坐标文件是用于SMT贴片时器件位置定位,而里面的PCB制板文件(Gerber)文件就是PCB生产用的文件。
点击导出PCB制板文件(gerber),在选项中选择“导出Gerber”,这时系统会提醒先检查DRC再进行导出,这里需要点击“是,检查DRC”,当检查无误后才会导出Gerber文件下载到本地。然后再将Gerber文件上传到嘉立创下单系统即可完成下单。
也可以在菜单栏直接点击PCB下单的话可以不需要下载文件直接跳转到嘉立创平台上进行PCB的打样操作,这里给PCB下单提供了更多的便利。点击自动下单后 同样需要对DRC进行检查,检查无误后系统会讲下单数据生产,点击“确认”按钮即可跳转到嘉立创下单平台页面进行下单。
3.3 下单页面介绍
进入下单平台后,需要填写板子生产工艺参数,具体说明如下:
3.3.1 基本信息
- 板材类别:选择FR-4,另外FPC板材为柔性PCB、铝基板常用于做灯板、铜基板散热性较好、高频板用于设计制作多阻抗和信号要求较高的板子;
- 板子尺寸:默认会自动识别出来的,没有识别的话也可手动填写;
- 板子数量:免费打样数量为5片,如果多打需要自费;
- 板子层数:嘉立创现在支持1-6层的免费打样,板子设计是四层板,这里选择两层(因为我们这个板速率不高你直接用2层来做性价比更高)。
- 产品类型:选择工业/消费/其他类电子产品,航空和医疗板精度设计要求较高;
- 确认生产稿:如果是批量生产那必须要确认生产稿,避免生产文件有误影响板子功能,免费打样则选择不需要生产稿即可。
3.3.2 PCB工艺
PCB工艺选项里面内容较多,仅需关注下图中框选出来的几个选项:
- 拼板款数:在进行批量打样时常将多个PCB拼在一个板子上生产,这样成本更低。由于目前仅做免费打样,拼板数量应为1,出货方式为单片;
- 板子厚度:默认板子厚度为1.6mm,无特殊要求建议是默认1.6mm ,选择其他厚度和颜色匹配时容易选到冷门工艺会额外添加工艺费;
- 需要阻抗:选择我们前面计算出来的层叠结构(因为我们这个板速率不高你直接用2层来做性价比更高)
- 阻焊颜色:即板子的颜色,嘉立创支持七种不同的阻焊颜色,其中绿色的生产周期最快,最快48小时内发货,其他颜色最快是72小时发货,可结合自身喜好和板子的着急程度选择合适的阻焊颜色。
3.3.3 个性化服务
个性化服务没有特殊需求选默认即可。
3.3.4 交期
交期与所选颜色有关,嘉立创最快支持12小时加急,但需额外支付加急费。若无特殊需求,选用默认交期即可。
3.3.5 SMT贴片与激光钢网
如需选择工厂代焊接元器件则在此选择需要SMT贴片,工厂生产PCB后会将元器件一同焊接好寄出,SMT属于收费服务,若不需要则选择不需要即可。钢网是用于SMT贴片刷锡膏用的,如果自己有贴片机,可在生产PCB时选择开钢网回来自己进行贴片焊接。
SMT与钢网选择页面
3.3.6 开票与支付
嘉立创是支持开票的,下单前需填写开票税号,免费打样无需开票,开票信息填写个人即可。在确认订单方式推荐选择“系统自动扣款并确认”选项,避免个人疏忽忘记确认订单影响生产。
开票/支付页面
3.3.7 发货与快递
在发货页面建议选择电子收据,发货方式可选不同交期是否一起发后,填写收货地址,下单联系人和技术联系人都可以填自己的联系方式,快递根据地区不同会由不同的快递显示,选择里面一个免费的快递即可。
快递选择页面
3.3.8 使用优惠券下单
填写完下单数据后,在右侧结算页面选择使用优惠券下单即可获得减免,这里注意下优惠前面额应该是20元,如果超过20那可能是选了某个特殊工艺,应修改回默认工艺后再使用优惠券下单。
4. PCB焊接练习
焊接教学视频:【Linux手机焊接调试】立创·泰山派RK3566 Linux开发板训练营第九课
4.1 焊接工具
需要用到的焊接工具有:电烙铁、焊锡丝、锡膏、镊子、吸锡带、助焊剂、高温海绵、吸锡器、松香、洗板水、焊台、热风枪等,其中电烙铁、焊锡丝、这四项为必备工具,其余几个有最好,没有也可以。
电烙铁选择常规烙铁就行,有条件的可以选择焊台,升温快且稳定。烙铁加热时请勿触摸金属位置,避免烫伤,头发绑好避免烫到,桌面保持整洁。
电烙铁及操作示意图 焊锡建议是选用无铅焊锡,焊锡时较好比较好上锡。一般左手拿焊锡,右手拿烙铁,如果惯用左手的话反之。
焊锡丝
高温海绵在使用前先用水浸湿,然后拧干后使用,海绵上不能沾太多水分,避免烙铁头高温沾水后急速降温损坏烙铁头。
4.2 辅助焊接工具
在嘉立创EDA专业版中提供了一个焊接辅助工具,焊接时可在线打开该工具对照元器件的参数及位置,实时进行查看焊接情况。打开方式是该项目的PCB设计页面,点击“工具”菜单栏,选择“焊接辅助工具”;也可以直接点击下载以下离线文件,并在浏览器中打开。
进入焊接辅助工具后,在显示模式中选择仅显示已焊接选项,左侧选择焊接时勾选对应器件,右侧的3D预览效果图显示当前器件焊接位置及情况,避免焊接错误。焊接的原则是从矮到高以此焊接。
4.3 焊接注意事项(必读)
4.3.1 NC不要贴
原理图中为NC的器件不要贴,比如电源背光选择电路如果贴上了以后会扩展板上的背光电源会和泰山派上的电源串联起来轻者损坏背光电路重者烧坏开发板。
原理图中为NC的器件不要贴,PWM背光上下拉电路默认上拉或者下来就行,FB反馈只要贴一个,两个都贴10Ω最终得到的电流就是40mA了超过了屏幕背光要求所以会发烫。
4.3.2 肉眼检查
焊接完成后不要立刻上电,先肉眼观察是否存在连锡、虚焊、锡珠、短路等情况。一般在工厂有飞针测试每个网络都测过去,但是你测量短路不肯能每个都去测量,一般只会选择测量电源等重要网络,所以看的时候仔细一点假如I2C_DSI_VCC_LED+网络短到其他GPIO上这种你是没办法测试出来的。不然烧板子就麻烦了。
如果有耐心最好还去检查一下自己的原理图和PCB,因为FPC线序画反、网络错位、网络名字不对等问题太常见了,基本新手必犯。
4.3.3 测量短路
焊接完成后不要立刻上电,必须测量短路,使用万用表蜂鸣档测量VCC_3V3和GND,I2C_DSI_VCC_LED+和I2C_DSI_VCC_LED-,MIPI_DSI_VCC_LED+和MIPI_DSI_VCC_LED- 这几组网路是否有短路情况,没有才可以上电。
4.3.4 测量断路
断路会导致你的板子没办法正常点亮,因为断路带来的影响比前面小很多,个人经验是上电后没有达到预期效果才会去判断是否有断路情况,判断短路可以通过万用表蜂鸣档位测量整条线路是否导通,GPIO引脚也可以通过测量内阻的方式排查是否导通。
4.3.5 测试验证
排查完所有问题后给泰山派烧入固件,然后断电在接入扩展板,因为第一次测试你不确定问题是出在你自己编译的固件还是硬件所以推荐首次测试使用后面附件中提供的固件进行测试,排除硬件问题以后再开始调试自己的代码。
正常情况屏幕会被点亮并进入系统、进入系统后可以正常触摸,如果用我们提供的固件依然没有办法正常工作就需要再返回检查上面步骤了。
三、软件开发
视频教程:【Linux手机驱动调试】立创·泰山派RK3566 Linux开发板训练营第八课
1. 驱动开发
1.1 打开设备树驱动
mipi相关的设备树在tspi-rk3566-dsi-v10.dtsi中,这里面包含mipi相关的所有设备树。我们通过tspi-rk3566-user-v10.dts中使用头文件去包含tspi-rk3566-dsi-v10.dtsi来决定是否使用mipi屏幕,所以我们使用前需打开。
1.2 GP7101背光驱动
调试屏幕我们一般会先把背光点亮如果使用的是泰山派的背光电路那直接使用代码里面默认的背光PWM驱动就行,但为了保护屏幕背光我们选择的是扩展板上的板载背光电路给3.1寸屏幕背光供电,扩展板板载背光电路PWM脚是通过GP7101 i2C转PWM芯片实现。所以我们需要编写一个GP7101驱动。
1.2.1 配置i2c1设备树
从原理图中可知GP7101和触摸共同挂在道I2C下,从数据手册中我们可以得知GP7101的I2C地址是0XB0,0xB0是包含了读写位的所以我们实际填写中还需要右移一位最终地址为0X58。
在tspi-rk3566-dsi-v10.dtsi
中添加GP7101相关设备树驱动,首先引用I2C1并往设备树I2C1节点中添加GP7101子节点并指定I2C地址、最大背光,默认背光等。
&i2c1 { // 引用名为i2c1的节点
status = "okay"; // 状态为"okay",表示此节点是可用和配置正确的
GP7101@58 { // 定义一个子节点,名字为GP7101,地址为58
compatible = "gp7101-backlight"; // 该节点与"gp7101-backlight"兼容,
reg = <0x58>; // GP7101地址0x58
max-brightness-levels = <255>; // 背光亮度的最大级别是255
default-brightness-level = <100>; // 默认的背光亮度级别是100
};
};
2
3
4
5
6
7
8
9
1.2.2 创建驱动
一般背光驱动都放在/kernel/drivers/video/backlight
目录下,所以我们在此路径下创建一个my_gp7101_bl
目录用来存放Makefile和gp7101_bl.c
文件。
1.2.3 编写Makefile
my_gp7101_bl/Makefile
中把gp7101_bl.c
编译到内核中,当然也可以选择obj-m编译成模块。
obj-y += gp7101_bl.o
要想my_gp7101_bl
下的Makefile
生效还需要在上一层目录的Makefile中添加my_gp7101_bl目录,所以我们需要在backlight
目录下Makefile中加入:
obj-y += my_gp7101_bl/
1.2.4 gp7101_bl.c驱动
1.2.4.1 I2C驱动框架
前面第六章节触摸驱动的时候我们就学过了I2C驱动框架,所以直接把触摸I2C驱动框架复制过来修改一下,如果你没有看前面章节也没有关系,这个框架结构会用就行。
#include "linux/stddef.h"
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/input/mt.h>
#include <linux/random.h>
#if 1
#define MY_DEBUG(fmt,arg...) printk("gp7101_bl:%s %d "fmt"",__FUNCTION__,__LINE__,##arg);
#else
#define MY_DEBUG(fmt,arg...)
#endif
#define BACKLIGHT_NAME "gp7101-backlight"
static int gp7101_bl_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
MY_DEBUG("locat");
return 0;
}
static int gp7101_bl_remove(struct i2c_client *client)
{
MY_DEBUG("locat");
return 0;
}
static const struct of_device_id gp7101_bl_of_match[] = {
{ .compatible = BACKLIGHT_NAME, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, gp7101_bl_of_match);
static struct i2c_driver gp7101_bl_driver = {
.probe = gp7101_bl_probe,
.remove = gp7101_bl_remove,
.driver = {
.name = BACKLIGHT_NAME,
.of_match_table = of_match_ptr(gp7101_bl_of_match),
},
};
static int __init my_init(void)
{
MY_DEBUG("locat");
return i2c_add_driver(&gp7101_bl_driver);
}
static void __exit my_exit(void)
{
MY_DEBUG("locat");
i2c_del_driver(&gp7101_bl_driver);
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My touch driver");
MODULE_AUTHOR("wucaicheng@qq.com");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
1.2.4.2 驱动中的结构体
因为驱动过程中会有很多参数,我们不可能创建全局变量去保存他们,在linux驱动中一般都是通过创建一个结构体来保存驱动相关的参数,所以这里我创建一个gp7101_backlight_data
结构体。
/* 背光控制器设备数据结构 */
struct gp7101_backlight_data {
/* 指向一个i2c_client结构体的指针*/
struct i2c_client *client;
/*......其他成员后面有用到再添加........*/
};
2
3
4
5
6
1.2.4.3 probe函数
当驱动中of_match_table = of_match_ptr(gp7101_bl_of_match)
和设备树匹配成功以后会执行探针函数,探针函数中我们会去初始化驱动。
// gp7101_bl_probe - 探测函数,当I2C总线上的设备与驱动匹配时会被调用
static int gp7101_bl_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct backlight_device *bl; // backlight_device结构用于表示背光设备
struct gp7101_backlight_data *data; // 自定义的背光数据结构
struct backlight_properties props; // 背光设备的属性
struct device_node *np = client->dev.of_node; // 设备树中的节点
MY_DEBUG("locat"); // 打印调试信息
// 为背光数据结构动态分配内存
data = devm_kzalloc(&client->dev, sizeof(struct gp7101_backlight_data), GFP_KERNEL);
if (data == NULL){
dev_err(&client->dev, "Alloc GFP_KERNEL memory failed."); // 内存分配失败,打印错误信息
return -ENOMEM; // 返回内存分配错误码
}
// 初始化背光属性结构
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_RAW; // 设置背光类型为原始类型
props.max_brightness = 255; // 设置最大亮度为255
// 从设备树中读取最大亮度级别
of_property_read_u32(np, "max-brightness-levels", &props.max_brightness);
// 从设备树中读取默认亮度级别
of_property_read_u32(np, "default-brightness-level", &props.brightness);
// 确保亮度值在有效范围内
if(props.max_brightness>255 || props.max_brightness<0){
props.max_brightness = 255;
}
if(props.brightness>props.max_brightness || props.brightness<0){
props.brightness = props.max_brightness;
}
// 注册背光设备
bl = devm_backlight_device_register(&client->dev, "backlight", &client->dev, data, &gp7101_backlight_ops,&props);
if (IS_ERR(bl)) {
dev_err(&client->dev, "failed to register backlight device\n"); // 注册失败,打印错误信息
return PTR_ERR(bl); // 返回错误码
}
data->client = client; // 保存i2c_client指针
i2c_set_clientdata(client, data); // 设置i2c_client的客户端数据
MY_DEBUG("max_brightness:%d brightness:%d",props.max_brightness, props.brightness); // 打印最大亮度和当前亮度
backlight_update_status(bl); // 更新背光设备的状态
return 0; // 返回成功
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
1.2.4.4 devm_backlight_device_register函数
devm_backlight_device_register
这个函数非常重要,他是 Linux 内核中用于动态注册背光设备的一个函数。前缀带devm
的一般都会在设备被销毁时自动释放相关资源,无需手动调用 backlight_device_unregister。 这个函数的主要作用是创建并注册一个 backlight_device
实例,这个实例代表了系统中的一个背光设备。背光设备通常用于控制显示屏的亮度。函数原型如下:
struct backlight_device *devm_backlight_device_register(
struct device *dev, const char *name, struct device *parent,
void *devdata, const struct backlight_ops *ops,
const struct backlight_properties *props);
2
3
4
参数说明:
dev
:指向父设备的指针,通常是一个struct i2c_client
或struct platform_device
。name
:背光设备的名称。parent
:背光设备的父设备,通常与dev
参数相同。devdata
:私有数据,会被传递给背光操作函数。ops
:指向backlight_ops
结构的指针,这个结构定义了背光设备的行为,包括设置亮度、获取亮度等操作。props
:指向backlight_properties
结构的指针,这个结构包含了背光设备的属性,如最大亮度、当前亮度等。
1.2.4.5 gp7101_backlight_ops结构体
ops参数非常重要,因为我们就是通过这个参数指向的结构成员中的函数去实现获取背光更新背光的。函数的原型如下:
struct backlight_ops {
unsigned int options;
#define BL_CORE_SUSPENDRESUME (1 << 0)
/* Notify the backlight driver some property has changed */
int (*update_status)(struct backlight_device *);
/* Return the current backlight brightness (accounting for power,
fb_blank etc.) */
int (*get_brightness)(struct backlight_device *);
/* Check if given framebuffer device is the one bound to this backlight;
return 0 if not, !=0 if it is. If NULL, backlight always matches the fb. */
int (*check_fb)(struct backlight_device *, struct fb_info *);
};
2
3
4
5
6
7
8
9
10
11
12
13
14
通过backlight_ops
定义了一个名为gp7101_backlight_ops
的backlight_ops
结构体实例,并且只初始化了.update_status
成员,它指向了一个名为gp7101_backlight_set
的函数,这个函数负责更新背光设备的亮度状态。
static struct backlight_ops gp7101_backlight_ops = {
.update_status = gp7101_backlight_set,
};
2
3
1.2.4.6 gp7101_backlight_set函数
这就是我们更新背光的核心函数了,每次背光被改动的时候系统都会回调这个函数,在函数中我们通过I2C1去写GP7101实现修改背光。 GP7101两种操作方法第一种是8位PWM,第二种是16位数PWM,刚好我们背光是从0~255所以,我们就选择8位PWM,八位PWM模式需要写寄存器0x03。
/* I2C 背光控制器寄存器定义 */
#define BACKLIGHT_REG_CTRL_8 0x03
#define BACKLIGHT_REG_CTRL_16 0x02
/* 设置背光亮度 */
static int gp7101_backlight_set(struct backlight_device *bl)
{
struct gp7101_backlight_data *data = bl_get_data(bl); // 获取背光数据结构指针
struct i2c_client *client = data->client; // 获取I2C设备指针
u8 addr[1] = {BACKLIGHT_REG_CTRL_8}; // 定义I2C地址数组
u8 buf[1] = {bl->props.brightness}; // 定义数据缓冲区,用于存储背光亮度值
MY_DEBUG("pwm:%d", bl->props.brightness); // 输出背光亮度值
// 将背光亮度值写入设备
i2c_write(client, addr, sizeof(addr), buf, sizeof(buf));
return 0; // 返回成功
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1.2.4.7 i2c_write
i2c_write函数直接把我们前面写的触摸驱动I2C写函数拷贝过来。
s32 i2c_write(struct i2c_client *client, u8 *addr, u8 addr_len, u8 *buf, s32 len)
{
struct i2c_msg msg; // 定义i2c消息结构,用于传输数据
s32 ret = -1; // 初始化返回值为-1,表示失败
u8 *temp_buf; // 定义临时缓冲区指针
msg.flags = !I2C_M_RD; // 标志位,表示写操作
msg.addr = client->addr; // 设备地址
msg.len = len + addr_len; // 写入数据的总长度(地址长度+数据长度)
// 分配临时缓冲区
temp_buf = kzalloc(msg.len, GFP_KERNEL);
if (!temp_buf) {
goto error; // 如果分配失败,跳转到错误处理
}
// 装填地址到临时缓冲区
memcpy(temp_buf, addr, addr_len);
// 装填数据到临时缓冲区(紧随地址之后)
memcpy(temp_buf + addr_len, buf, len);
msg.buf = temp_buf; // 设置消息的缓冲区为临时缓冲区
// 发送消息并写入数据
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret == 1) {
kfree(temp_buf); // 释放临时缓冲区
return 0; // 如果消息成功传输,返回0表示成功
}
error:
// 如果写入失败,打印错误信息
if (addr_len == 2) {
MY_DEBUG("I2C Write: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((u16)(addr[0] << 8)) | addr[1]), len, ret);
} else {
MY_DEBUG("I2C Write: 0x%02X, %d bytes failed, errcode: %d! Process reset.", addr[0], len, ret);
}
if (temp_buf) {
kfree(temp_buf); // 释放临时缓冲区
}
return -1; // 返回-1表示失败
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
1.2.4.8 注释backlight
因为我们之前的背光驱动也是用的"backlight"
节点,为了不去修改上层我们自己写的驱动也是用的"backlight"
节点所以两个节点会冲突,所以我们在tspi-rk3566-dsi-v10.dtsi中把之前的屏蔽掉留下我们自己写的驱动。 屏蔽原有背光设备树节点。
/ {
/*backlight: backlight {
compatible = "pwm-backlight";
pwms = <&pwm5 0 25000 0>;
brightness-levels = <
0 20 20 21 21 22 22 23
23 24 24 25 25 26 26 27
27 28 28 29 29 30 30 31
31 32 32 33 33 34 34 35
35 36 36 37 37 38 38 39
40 41 42 43 44 45 46 47
48 49 50 51 52 53 54 55
56 57 58 59 60 61 62 63
64 65 66 67 68 69 70 71
72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87
88 89 90 91 92 93 94 95
96 97 98 99 100 101 102 103
104 105 106 107 108 109 110 111
112 113 114 115 116 117 118 119
120 121 122 123 124 125 126 127
128 129 130 131 132 133 134 135
136 137 138 139 140 141 142 143
144 145 146 147 148 149 150 151
152 153 154 155 156 157 158 159
160 161 162 163 164 165 166 167
168 169 170 171 172 173 174 175
176 177 178 179 180 181 182 183
184 185 186 187 188 189 190 191
192 193 194 195 196 197 198 199
200 201 202 203 204 205 206 207
208 209 210 211 212 213 214 215
216 217 218 219 220 221 222 223
224 225 226 227 228 229 230 231
232 233 234 235 236 237 238 239
240 241 242 243 244 245 246 247
248 249 250 251 252 253 254 255
>;
default-brightness-level = <255>;
};*/
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
在dsi1中也需要屏蔽掉否则找不到引用节点编译时候会报错。
&dsi1 {
status = "okay";
rockchip,lane-rate = <1000>;
dsi1_panel: panel@0 {
/*省略*/
// backlight = <&backlight>;
/*省略*/
};
};
2
3
4
5
6
7
8
9
1.2.5 完整代码补丁
【Android补丁】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->代码补丁
->3.1寸屏幕背光驱动补丁.zip
。
【Linux补丁】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->代码补丁
->3.1寸屏幕背光驱动Linux补丁.zip
。
1.3 屏参数调试
1.3.1 tspi-rk3566-dsi-v10.dtsi配置
tspi-rk3566-dsi-v10.dtsi
大多数设备树都是已经定义好的(要想了解详细参数以及使用方法可以看前面章节)我们这次适配3.1寸触摸屏只需修改以下几个参数,其他保持默认即可。
- 修改lanes数
- 配置初始化序列
- 配置屏幕时序
1.3.1.1 修改lanes数
3.1寸屏幕硬件上只用了2lanes的差分对,设备树中默认配置的是4lanes所以我们需要把lanes修改为2
dsi,lanes = <4>;
改为
dsi,lanes = <2>;
2
3
1.3.1.2 配置初始化序列
初始化序列是参考3.1寸屏幕厂商给的修改过来的,每款屏幕所使用的屏幕参都有些许不同,如果还不了解屏参调试的同学可以参考第五章节MIPI屏幕调试。
panel-init-sequence = [
// init code
05 78 01 01
05 78 01 11
39 00 06 FF 77 01 00 00 11
15 00 02 D1 11
15 00 02 55 B0 // 80 90 b0
39 00 06 FF 77 01 00 00 10
39 00 03 C0 63 00
39 00 03 C1 09 02
39 00 03 C2 37 08
15 00 02 C7 00 // x-dir rotate 0:0x00,rotate 180:0x04
15 00 02 CC 38
39 00 11 B0 00 11 19 0C 10 06 07 0A 09 22 04 10 0E 28 30 1C
39 00 11 B1 00 12 19 0D 10 04 06 07 08 23 04 12 11 28 30 1C
39 00 06 FF 77 01 00 00 11 // enable bk fun of command 2 BK1
15 00 02 B0 4D
15 00 02 B1 60 // 0x56 0x4a 0x5b
15 00 02 B2 07
15 00 02 B3 80
15 00 02 B5 47
15 00 02 B7 8A
15 00 02 B8 21
15 00 02 C1 78
15 00 02 C2 78
15 64 02 D0 88
39 00 04 E0 00 00 02
39 00 0C E1 01 A0 03 A0 02 A0 04 A0 00 44 44
39 00 0D E2 00 00 00 00 00 00 00 00 00 00 00 00
39 00 05 E3 00 00 33 33
39 00 03 E4 44 44
39 00 11 E5 01 26 A0 A0 03 28 A0 A0 05 2A A0 A0 07 2C A0 A0
39 00 05 E6 00 00 33 33
39 00 03 E7 44 44
39 00 11 E8 02 26 A0 A0 04 28 A0 A0 06 2A A0 A0 08 2C A0 A0
39 00 08 EB 00 01 E4 E4 44 00 40
39 00 11 ED FF F7 65 4F 0B A1 CF FF FF FC 1A B0 F4 56 7F FF
39 00 06 FF 77 01 00 00 00
15 00 02 36 00 //U&D Y-DIR rotate 0:0x00,rotate 180:0x10
15 00 02 3A 55
05 78 01 11
05 14 01 29
];
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1.3.1.3 配置屏幕时序
屏幕时序一般根据数据手册和厂家给的参考得来,以下是针对3.1寸屏幕修改好的参数。如果还不了解屏参调试的同学可以参考第五章节MIPI屏幕调试。
disp_timings1: display-timings {
native-mode = <&dsi1_timing0>;
dsi1_timing0: timing0 {
clock-frequency = <27000000>;
hactive = <480>; //与 LCDTiming.LCDH 对应
vactive = <800>; //与 LCDTiming.LCDV 对应
hfront-porch = <32>; //与 LCDTiming.HFPD 对应
hsync-len = <4>; //与 LCDTiming.HSPW 对应
hback-porch = <32>; //与 LCDTiming.HBPD 对应
vfront-porch = <9>; //与 LCDTiming.VEPD 对应
vsync-len = <4>; //与 LCDTiming.VsPW 对应
vback-porch = <3>; //与 LCDTiming.VBPD 对应
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <0>;
};
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1.3.2 完整代码补丁
【3.1寸大显MIPI补丁.zip】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->代码补丁
->3.1寸大显MIPI补丁.zip
。
1.4 触摸屏驱动
1.4.1 配置i2c1设备树
从原理图中可知GP7101和触摸共同挂在道I2C下,所以引用&i2c1并添加一个我们自己定义的myts@38触摸节点。
&i2c1 {
status = "okay"; // 表示这个i2c1设备是可用的
clock-frequency = <400000>; // 设置i2c1的时钟频率为400kHz
myts@38 { // 定义一个i2c设备,设备地址为0x38,设备名称为myts
compatible = "my,touch"; // 表示这个设备是触摸屏设备,驱动名称为my,touch
reg = <0x38>; // i2c设备地址
tp-size = <89>; // 触摸屏的大小
max-x = <480>; // 触摸屏支持的最大X坐标值
max-y = <800>; // 触摸屏支持的最大Y坐标值
touch-gpio = <&gpio1 RK_PA0 IRQ_TYPE_LEVEL_LOW>; // 触摸屏的触摸中断引脚,连接到gpio1的第0个引脚,触发方式为低电平触发
reset-gpio = <&gpio1 RK_PA1 GPIO_ACTIVE_HIGH>; // 触摸屏的复位引脚,连接到gpio1的第1个引脚,有效电平为高电平
};
/****省略****/
};
2
3
4
5
6
7
8
9
10
11
12
13
14
1.4.2 创建驱动
一般触摸都放在/kernel/drivers/input/touchscreen
目录下,所以我们在此路径下创建一个my_touch
目录用来存放Makefile
和my_touch.c
文件。
1.4.3 编写Makefile
touchscreen/Makefile
中把my_touch.c
编译到内核中,当然也可以选择obj-m编译成模块。
obj-y += my_touch.o
1.4.4 my_touch.c驱动
1.4.4.1 I2C驱动框架
和前面一样,这就是一个框架结构大家会用就行,这里就不过多赘述了。
static int my_touch_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return 0;
}
static int my_touch_ts_remove(struct i2c_client *client)
{
MY_DEBUG("locat");
return 0;
}
static const struct of_device_id my_touch_of_match[] = {
{ .compatible = "my,touch", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_touch_of_match);
static struct i2c_driver my_touch_ts_driver = {
.probe = my_touch_ts_probe,
.remove = my_touch_ts_remove,
.driver = {
.name = "my-touch",
.of_match_table = of_match_ptr(my_touch_of_match),
},
};
static int __init my_ts_init(void)
{
MY_DEBUG("locat");
return i2c_add_driver(&my_touch_ts_driver);
}
static void __exit my_ts_exit(void)
{
MY_DEBUG("locat");
i2c_del_driver(&my_touch_ts_driver);
}
module_init(my_ts_init);
module_exit(my_ts_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My touch driver");
MODULE_AUTHOR("wucaicheng@qq.com");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
1.4.4.2 驱动中的结构体
因为驱动过程中会有很多参数,我们不可能创建全局变量去保存他们,在linux驱动中一般都是通过创建一个结构体来保存驱动相关的参数,所以这里我创建一个my_touch_dev
结构体。
// 定义一个表示触摸设备的结构体
struct my_touch_dev {
struct i2c_client *client; // 指向与触摸设备通信的 I2C 客户端结构体的指针
struct input_dev *input_dev; // 指向与输入设备关联的 input_dev 结构体的指针,用于处理输入事件
int rst_pin; // 触摸设备的复位引脚编号
int irq_pin; // 触摸设备的中断引脚编号
u32 abs_x_max; // 触摸设备在 X 轴上的最大绝对值
u32 abs_y_max; // 触摸设备在 Y 轴上的最大绝对值
int irq; // 触摸设备的中断号
};
2
3
4
5
6
7
8
9
10
1.4.4.3 probe
当驱动中of_match_table = of_match_ptr(my_touch_of_match)
和设备树匹配成功以后会执行探针函数,探针函数中我们会去初始化驱动。第六章已经对触摸相关函数已经参数进行了分析这里就不在去分析了。
static int my_touch_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret; // 定义一个返回值变量
struct my_touch_dev *ts; // 定义一个结构体指针,用来指向my_touch_dev结构体
struct device_node *np = client->dev.of_node; // 获取设备节点
// 打印调试信息
MY_DEBUG("locat"); // 调用MY_DEBUG函数打印调试信息,此处打印"locat"
ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); // 使用devm_kzalloc分配内存,减少内存申请操作
if (ts == NULL){ // 检查内存分配是否成功
dev_err(&client->dev, "Alloc GFP_KERNEL memory failed."); // 内存分配失败,打印错误信息
return -ENOMEM; // 返回内存申请错误的码
}
ts->client = client; // 触摸屏设备的客户端指针指向i2c_client结构体
i2c_set_clientdata(client, ts); // 将my_touch_dev结构体的指针设置为i2c客户端的数据
// 从设备树中读取触摸屏的最大X和Y值
if (of_property_read_u32(np, "max-x", &ts->abs_x_max)) {
dev_err(&client->dev, "no max-x defined\n"); // 如果读取最大X值失败,打印错误信息
return -EINVAL; // 返回参数无效的错误码
}
MY_DEBUG("abs_x_max:%d",ts->abs_x_max); // 打印X值
if (of_property_read_u32(np, "max-y", &ts->abs_y_max)) {
dev_err(&client->dev, "no max-y defined\n"); // 如果读取最大Y值失败,打印错误信息
return -EINVAL; // 返回参数无效的错误码
}
MY_DEBUG("abs_x_max:%d",ts->abs_y_max); // 打印Y值
// 获取并请求复位GPIO管脚
ts->rst_pin = of_get_named_gpio(np, "reset-gpio", 0); // 从设备树中获取复位管脚
ret = devm_gpio_request(&client->dev,ts->rst_pin,"my touch touch gpio"); // 请求使用复位管脚
if (ret < 0){ // 如果请求失败
dev_err(&client->dev, "gpio request failed."); // 打印错误信息
return -ENOMEM; // 返回内存申请错误的码
}
ts->irq_pin = of_get_named_gpio(np, "touch-gpio", 0); // 从设备树中获取中断管脚
ret = devm_gpio_request_one(&client->dev, ts->irq_pin, // 请求使用中断管脚
GPIOF_IN, "my touch touch gpio");
if (ret < 0)
return ret; // 如果请求失败,直接返回错误码
// 复位触摸屏设备
gpio_direction_output(ts->rst_pin,0); // 设置复位管脚输出低电平
msleep(20); // 等待20毫秒
gpio_direction_output(ts->irq_pin,0); // 设置中断管脚输出低电平
msleep(2); // 等待2毫秒
gpio_direction_output(ts->rst_pin,1); // 设置复位管脚输出高电平
msleep(6); // 等待6毫秒
gpio_direction_output(ts->irq_pin, 0); // 设置中断管脚输出低电平
msleep(50); // 等待50毫秒
// 申请中断服务
ts->irq = gpio_to_irq(ts->irq_pin); // 将GPIO管脚转换为中断号
if(ts->irq){ // 检查中断号是否有效
ret = devm_request_threaded_irq(&(client->dev), ts->irq, // 请求线程化中断
NULL, my_touch_irq_handler, // 中断服务函数
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, // 中断触发方式为下降沿触发,且只触发一次
client->name, ts);
if (ret != 0) {
MY_DEBUG("Cannot allocate ts INT!ERRNO:%d\n", ret); // 如果中断请求失败,打印错误信息
return ret; // 返回错误码
}
}
// 使用devm_input_allocate_device分配输入设备对象
ts->input_dev = devm_input_allocate_device(&client->dev);
if (!ts->input_dev) { // 检查输入设备对象是否分配成功
dev_err(&client->dev, "Failed to allocate input device.\n"); // 打印错误信息
return -ENOMEM; // 返回内存申请错误的码
}
// 设置输入设备的名称
ts->input_dev->name = "my touch screen";
// 设置输入设备的总线类型为I2C
ts->input_dev->id.bustype = BUS_I2C;
// 设置X轴的最大值为480
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, 480, 0, 0);
// 设置Y轴的最大值为800
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, 800, 0, 0);
// 初始化5个多点触摸槽位,直接模式
ret = input_mt_init_slots(ts->input_dev, 5, INPUT_MT_DIRECT);
if (ret) {
dev_err(&client->dev, "Input mt init error\n"); // 打印错误信息
return ret; // 返回错误码
}
// 注册输入设备
ret = input_register_device(ts->input_dev); // 注册输入设备
if (ret)
return ret; // 返回错误码
// 读取版本号
gt9271_read_version(client);
return 0; // 如果一切顺利,返回0
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
1.4.4.4 中断
读取触摸数据有很多方法比如轮询但是这样效率太低了,所以我们这里通过中断方式实现触摸数据读取,当屏幕被触控时,屏幕会通过irq引脚输出中断信号。
devm_request_threaded_irq(&(client->dev), ts->irq, // 请求线程化中断
NULL, my_touch_irq_handler, // 中断服务函数
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, // 中断触发方式为下降沿触发,且只触发一次
client->name, ts);
2
3
4
1.4.4.5 中断线程服务函数
上面中断以后当屏幕被触摸会触发中断线程服务函数my_touch_irq_handler,在这个函数里面我们通过i2c去读取屏幕相关的参数。
static irqreturn_t my_touch_irq_handler(int irq, void *dev_id)
{
s32 ret = -1; // 定义一个返回值,初始化为-1
struct my_touch_dev *ts = dev_id; // 获取指向设备数据的指针
u8 addr[1] = {0x02}; // 定义一个寄存器地址数组对应数据手册TD_STATUS
u8 point_data[1+6*5]={0}; // 定义一个点数据数组,预留足够空间 for 5 touch points
u8 touch_num = 0; // 定义一个变量来存储触摸点的数量
u8 *touch_data; // 定义一个指针用于指向点数据
int i = 0; // 定义一个循环变量
int event_flag, touch_id, input_x, input_y; // 定义一些用于存储事件信息的变量
MY_DEBUG("irq"); // 打印中断信息
ret = my_touch_i2c_read(ts->client, addr, sizeof(addr), point_data, sizeof(point_data)); // 尝试读取触摸屏设备的数据
if (ret < 0){ // 如果读取失败
MY_DEBUG("I2C write end_cmd error!"); // 打印错误信息
}
touch_num = point_data[0]&0x0f; // 获取触摸点的数量
MY_DEBUG("touch_num:%d",touch_num); // 打印触摸点数量
// 遍历触摸点数据
for(i=0; i<5; i++){
// 获取触摸点数据
touch_data = &point_data[1+6*i];
/*
解析触摸点的事件标志位
00b: 按下
01b: 抬起
10b: 接触
11b: 保留
*/
event_flag = touch_data[0] >> 6;
if(event_flag == 0x03)continue; // 如果事件标志位不是按下或抬起,则跳过此循环
touch_id = touch_data[2] >> 4; // 获取触摸点ID
MY_DEBUG("i:%d touch_id:%d event_flag:%d",i,touch_id,event_flag); // 打印调试信息
input_x = ((touch_data[0]&0x0f)<<8) | touch_data[1]; // 计算X坐标
input_y = ((touch_data[2]&0x0f)<<8) | touch_data[3]; // 计算Y坐标
// MY_SWAP(input_x,input_y); // 如果需要交换X和Y坐标,可以取消注释此行
MY_DEBUG("i:%d,x:%d,y:%d",i,input_x,input_y); // 打印调试信息
// 设置输入设备的触摸槽位
input_mt_slot(ts->input_dev, touch_id);
if(event_flag == 0){ // 如果是按下
// 上报按下事件和坐标
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); // 设置为按下状态
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x); // 报告X坐标
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); // 报告Y坐标
}else if (event_flag == 2){ // 如果是长按
// 直接上报数据
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x); // 报告X坐标
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); // 报告Y坐标
else if(event_flag == 1){ // 如果是触摸抬起
// 上报事件
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); // 设置为抬起状态
}
}
// 报告输入设备的指针仿真信息
input_mt_report_pointer_emulation(ts->input_dev, true);
// 同步输入事件
input_sync(ts->input_dev);
// 返回IRQ_HANDLED,表示中断已经被处理
return IRQ_HANDLED;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
1.4.4.6 TD_STATUS
读取数据从TD_STATUS开始,一个触摸点包含6个数据分别是TOUCH1_XH、TOUCH1_XL、TOUCH1_YH、TOUCH1_YL、TOUCH1_WEIGHT,共计支持5点触控,所以连续读取的长度为1(TD_STATUS)+6(数据)*5。
ret = my_touch_i2c_read(ts->client, addr, sizeof(addr), point_data, sizeof(point_data));
TD_STATUS寄存器中的[]位存储的是触摸点数据,所以我们通过下面语句读取到当前有多少点被按下。
touch_num = point_data[0]&0x0f; // 获取触摸点的数量
1.4.4.7 TOUCHn_X寄存器
TOUCHn_XH寄存器的[7:6]位是事件标志位数,所以我们通过touch_data右移6位来获取[7:6]位值。通过判定这个标志位我们可以知道当前触摸状态。
值类型:
- 触摸:00b
- 抬起:01b
- 长按:10b
- 保留:11b
event_flag = touch_data0 >> 6;
TOUCHn_XH寄存器的[3:0]位是x坐标的高[11:8]位数,要想获取完整的x坐标值需要把TOUCHn_XH的[3:0]位和TOUCHn_XL的[7:0]位进行组合。
input_x = ((touch_data[0]&0x0f)<<8) | touch_data[1];
1.4.4.8 TOUCHn_Y寄存器
TOUCHn_YH寄存器的[7:4]位是触摸ID,通过右移4位获取到触摸id。
touch_id = touch_data[2] >> 4; // 获取触摸点ID
TOUCHn_YH寄存器的[3:0]位是y坐标的高[11:8]位数,要想获取完整的x坐标值需要把TOUCHn_YH的[3:0]位和TOUCHn_YL的[7:0]位进行组合。
input_y = ((touch_data[2]&0x0f)<<8) | touch_data[3]; // 计算Y坐标
1.4.4.9 上报数据
数据到以后调用input接口函数进行数据上报,详细的函数使用方法大家看代码里面的注释。
1.4.5 完整代码补丁
【Android补丁】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->代码补丁
->3.1寸屏幕触摸驱动补丁.zip
。
【Linux补丁】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->代码补丁
->3.1寸屏幕触摸Linxu驱动补丁.zip
。
1.5 开机logo更换
1.5.1 素材
找一张你想使用的开机logo图片,最好图片大小尺寸和屏幕的尺寸一样,比如我们这里随便找一张图片用来做测试。选择保存图片到本地
1.5.2 BMP
一般图片保存到本地以后大多都是png或者jpeg的格式开机logo使用的是bmp格式,所以我们需要进一次图片格式转换,如果你有会用PS等工具的话可以使用他们调整一下图片大小已经旋转方向,最后输出bmp,如果你懒那也没有关系,转bmp的在线工具有很多,这里推进一个在线转换:https://onlineconvertfree.com/zh/convert-format/png-to-bmp/
1.5.3 替换
把转换完的logo代替kernel目录下的logo.bmp和logo_kernel.bmp。
1.5.4 生效
logo代替完成以后要生效还需要重新编译内核并更新内核,如果编译过程中出现logo相关报错,一般都是logo格式不对,重新生成bmp图片后在尝试编译。
1.5.5 图片格式报错
图片格式不对会导致错误
2. 系统开发
2.1 Android系统
2.1.1 修改系统信息
使用我们提供的sdk编译出来后的Android11系统固件进入关于系统中,默认设备名称和型号都是rk3566_tspi,大家开发自己的产品时候一般希望客户看到的是自己的商品名称,我们可以通用修改rk3566_tspi.mk实现。路径:SDK/device/rockchip/rk356x/rk3566_tspi/rk3566_tspi.mk
PRODUCT_MODEL := rk3566_tspi
修改为:
PRODUCT_MODEL := my_fat_girl_phone
2
3
2.1.2 修改时区
Android11系统在联网的情况下会自动去同步时间,但是如果时区不是中国时区自动同步以后会有时差,所以我们需要把系统默认的时区修改成中国时区。路径:SDK/device/rockchip/rk356x/rk3566_tspi/rk3566_tspi.mk
#添加时区为亚洲上海
PRODUCT_PROPERTY_OVERRIDES += persist.sys.timezone=Asia/Shanghai
2
2.1.3 修改屏幕密度
屏幕密度决定了Android11系统UI的缩放布局以及选择的素材控件等,举个例子如果你屏幕是2K的分辨率,那么你密度就可以调大一点这个图标才协调,如果你用的是这次的3.1寸屏密度大了一个桌面就只能放下几个图标路径:SDK/device/rockchip/rk356x/rk3566_tspi/rk3566_tspi.mk。
#修改密度这里是240
PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=240
2
2.1.4 预制apk
泰山派开发板下载系统以后如果有需要用到的apk还需要自己手动安装,加入有100W个胖妞手机都需要用到这个apk应用,如果我们每个都去手动安装那基本上是不现实的,我们可以把需要安装的应用内置到系统sdk中,我们装系统的时候自动安装。
SDK/device/rockchip/rk356x/rk3566_tspi
preinstall //不可卸载应用
preinstall_del //可卸载卸载,恢复出厂设置复原应用
preinstall_del_forever //可卸载应用,恢复出厂设置不会复原
2
3
4
创建了一个preinstall目录并放入了一个yingyongbao.apk(应用宝)
编译以后会自动生成Android.mk和preinstall.mk
out目录下生成我们预制的应用
2.1.5 旋转屏幕
3.1寸屏幕默认是竖屏显示,想要修改成默认横屏需要调整3个地方,修改logo方向、修改触摸方向、修改android系统方向。
2.1.5.1 系统添加屏幕旋转
diff --git a/rk3566_tspi/BoardConfig.mk b/rk3566_tspi/BoardConfig.mk
index 4f702c3..bbad87a 100755
--- a/rk3566_tspi/BoardConfig.mk
+++ b/rk3566_tspi/BoardConfig.mk
@@ -37,3 +37,6 @@ ifeq ($(strip $(BOARD_USES_AB_IMAGE)), true)
include device/rockchip/common/BoardConfig_AB.mk
TARGET_RECOVERY_FSTAB := device/rockchip/rk356x/rk3566_tspi/recovery.fstab_AB
endif
+TARGET_RECOVERY_DEFAULT_ROTATION := ROTATION_RIGHT
+SF_PRIMARY_DISPLAY_ORIENTATION := 90
2
3
4
5
6
7
8
9
10
11
2.1.5.2 修改logo方向
logo方向不是必须的,如果感觉logo是竖屏,系统是横屏不接受可以参考上面logo制作方法,使用图片编辑工具把logo图片旋转90度。
2.1.5.3 修改触摸方向
修改/kernel/drivers/input/touchscreen/my_touch/mytouch.c
diff --git a/my_touch.c b/my_touch.c
index 9acab2d..608914a 100755
--- a/my_touch.c
+++ b/my_touch.c
@@ -143,7 +143,7 @@ static irqreturn_t my_touch_irq_handler(int irq, void *dev_id)
input_x = ((touch_data[0]&0x0f)<<8) | touch_data[1];
input_y = ((touch_data[2]&0x0f)<<8) | touch_data[3];
- // MY_SWAP(input_x,input_y);
+ MY_SWAP(input_x,input_y);
MY_DEBUG("i:%d,x:%d,y:%d",i,input_x,input_y);
// 设定输入设备的触摸槽位
input_mt_slot(ts->input_dev, touch_id);
@@ -151,11 +151,11 @@ static irqreturn_t my_touch_irq_handler(int irq, void *dev_id)
if(event_flag == 0){
// 如果是按下上报按下和坐标
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
}else if (event_flag == 2){
// 如果是长按直接上报数据
- input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
}else if(event_flag == 1){
// 触摸抬起,上报事件
@@ -277,8 +277,8 @@ static int my_touch_ts_probe(struct i2c_client *client,
/*设置触摸 x 和 y 的最大值*/
// 设置输入设备的绝对位置参数
- input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, 480, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, 800, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, 800, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, 480, 0, 0);
// 初始化多点触摸设备的槽位
ret = input_mt_init_slots(ts->input_dev, 5, INPUT_MT_DIRECT);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
重新编译内核并烧录
2.1.6 修改开机动画
开机显示完logo以后会进入系统之前会显示一段开机动画,默认的android系统开机动画是一个android的logo在闪光,我们可以自定自己的开机动画来代替默认开机动画。
2.1.6.1 创建文件夹
创建开机动画文件夹命名为bootanimation,并在文件夹中创建两个文件夹分别为part0,part1和一个文档desc.txt。
文件说明:
- partN:用于存放动画图片,里面存放着多张连续的图片,开机会去播放里面的图片。如果part0播放完了,接着就来播放part1,以此类推N代表多个part,我们这里就演示两个。
- desc.txt:描述图片信息以及显示的方式
800 480 10
p 1 0 part0
p 0 0 part1
2
3
第一行:800 480 10分别表示800*480分辨率和一秒钟播放10张图片。
第二行:p 1 0 part0分别表示p播放、1播放循环次数为一次,播放完后间隔0秒重复播放或播放下一个part。part0指目录。
第三行:p 0 0 part1分别表示p播放、0播放一直循环播放,播放完后间隔0秒重复播放或播放下一个part。part1指目录。
2.1.6.2 part目录
这里以part0目录为例,part1大家举一反三,part中对图片的命名格式有明确要求,如果不按照要求来系统无法识别。规则如下:
- 建议使用png格式图片
- 图片的序列是连续的
- 如果图片个数小于10张图片的命名规则是01.png到09.png
- 如果图片个数小于100张图片的命名规则是001.png到099.png
- 如果图片个数小于1000张图片的命名规则是0001.png到0999.png 下面素材中提供的图片有117张所以命名就是0001.png到0117.png
2.1.6.3 打包压缩资源
打包这里大家一定要非常注意,不是直接压缩bootanimation目录,这样压缩后会多一层bootanimation目录导致系统无法识别,需要进入bootanimation目录选择所有文件压缩。压缩等级改为仅存储。
2.1.6.4 修改SDK
把上面准备好的bootanimation.zip资源文件复制到SDK/device/rockchip/rk356x目录下。
打开SDK/device/rockchip/rk356x/device.mk文件在末尾追加下面代码。
PRODUCT_COPY_FILES += $(LOCAL_PATH)/bootanimation.zip:system/media/bootanimation.zip
2.1.6.5 快速验证
每次制作完成logo需要看效果都要重新编译然后烧录非常浪费时间,我们可以通用adb命令直接把制作好的bootanimation.zip动画资源push到泰山派开发板中,重启验证修改后的效果。
adb root && adb remount && adb push Z:\tspi\Android11_20231007\PublicVersion\device\rockchip\rk356x\bootanimation.zip /system/media/bootanimation.zip
2.1.6.6 制作动画图片(可跳过)
ps导入一个视频,并把视频转成一帧帧图片资源。演示中的所有资源文件在文末附件中提供,如果不制作自己的logo可以直接跳过用现成的。
上面导出以后命名是到图层的,为了满足我们前面说的规则,这里写了一个脚本自动转换。
import os
import shutil
# 源文件夹路径
src_folder = './hztest/'
# 目标文件夹路径
dest_folder = './out/'
# 重命名函数
def rename_files(src_folder, dest_folder):
# 确保目标文件夹存在
if not os.path.exists(dest_folder):
os.makedirs(dest_folder)
# 遍历源文件夹中的文件
for filename in sorted(os.listdir(src_folder)):
# 提取文件名和扩展名
name, ext = os.path.splitext(filename)
# 提取图层号
layer_num = int(name.split(' ')[-1])
# 根据图层号重命名文件
if layer_num < 10:
new_filename = f"000{layer_num}{ext}"
elif layer_num < 100:
new_filename = f"00{layer_num}{ext}"
else:
new_filename = f"0{layer_num}{ext}"
# 拷贝文件并重命名
shutil.copy(os.path.join(src_folder, filename), os.path.join(dest_folder, new_filename))
print(f"Renamed {filename} to {new_filename}")
# 调用重命名函数
rename_files(src_folder, dest_folder)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2.1.6.7 开机动画资源
2.1.7 编译生效
Android11直接使用make编译上面的修改都不会生效,要生效需要installclean以后在编译,千万不要clean,clean以后要编译大半天,installclean只需1-5分钟就好了。
source build/envsetup.sh && lunch rk3566_tspi-userdebug && make installclean -j88 && make -j88 && ./mkimage.sh
编译完成以后需要重新烧录固件
2.1.8 效果演示
2.1.9 完整补丁
竖屏:
【胖妞手机安卓版代码补丁(默认竖屏).zip】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->代码补丁
->胖妞手机安卓版代码补丁(默认竖屏).zip
。
横屏:
【胖妞手机安卓版代码补丁(默认横屏).zip】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->代码补丁
->胖妞手机安卓版代码补丁(默认横屏).zip
。
四、外壳
1. 模型文件
泰山派胖妞手机外壳由顶壳和底壳两个部分组成。3D文件在下方有提供,如果想预览顶壳和底壳模型你可以把模型下载以后拖入到嘉立创3D模型在线预览平台进行查看。
在线预览平台:https://viewer.forface3d.com/online
1.1 顶壳
【顶壳.STL】和【顶壳.STEP】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->外观结构
文件夹中。
1.2 底壳
【低壳.STL】和【低壳.STEP】下载
📌 在 下载中心
->百度网盘(立创·泰山派开发板资料)
->第09章.【立创·泰山派】项目案例
->【智能小手机】项目资料
->7.手机综合项目
->外观结构
文件夹中。
2. 3D外壳生产
胖妞手机外壳可以在我们嘉立创的三维猴进行3D打印生产,三维猴可打印材料包括树脂、工业塑料、尼龙、金属;主要服务行业有工业设计、医疗、电子、汽车、手办、道具、工艺品、五金配件、雕塑等。
在线下单网址:https://www.jlc-3dp.cn/swhorder/#/placeOrder
2.1 上传模型
下载上面的3D模型文件.STL
和.STEP
都可以,把模型拖到三维猴上传图纸区域,等待模型加载,加载完成后会有预览图显示。
2.2 材料选择
3D打印的材料很多种,不同材料颜色、光滑度、韧性、温度等都不同,材料详细大家可以阅读:三维猴材料介绍(点击跳转)
泰山派满负荷工作时温度可以达到60-70度,所以建议大家选择材料的时候选耐温高一些的如: “ 尼龙3201PA-F ” 防止温度导致外壳变形,我们这里不会满负荷运行所以为了经济实惠就选默认的 “ 树脂LEDO 6060 ” ,表面处理选择打磨粗磨。
2.3 提交订单
完成顶壳和底壳两个模型的参数设置后,右边查看并阅读打印须知后提交订单。
2.4 订单信息
跟据提示完成收货人信息、下单联系人、其它信息的填写和大家平时购物一样。如果你参加了泰山派训练营或上月支付满二十并领取到优惠券,完成所有填写以后会弹出优惠券使用选项。
3. 组装
3.1 物料清单
3.2 第一步
第一步把3.1寸屏幕和扩展板组装起来,3.1寸屏幕排线和触摸排线插入到FPC座中扣紧,接着翻转平板使用双面胶固定屏幕和扩展板,注意折叠的时候温柔一点别把屏幕排线扯断了。
3.3 第二步
把屏幕装入到底壳中,可以通用喇叭座子来判定屏幕和底壳的方向,屏幕完全装入以后是和外壳同高的且间隙非常小的。
3.4 第三步
通过0.3间距31PIN同方向MIPI屏排线和0.5间距6PIN同向触摸排线连接泰山派和3.1寸屏幕扩展板,这排线尽量是越短越好。
3.5 第四步
折叠排线把泰山派装入到底壳中,折叠的时候不要像折纸一样压死,尽量折个圆角,折死了信号不好而且容易断。
3.6 第五步
把喇叭装到顶壳的喇叭插槽中,喇叭默认是不带插座头的,这个需要自己买回来焊接,或者直接把喇叭焊接到扩展板板上也可以。
3.7 第六步
盖上顶壳使用镊子把喇叭线插入到座子中并确保外壳没有夹到线
3.8 第七步
盖上顶壳把落实拧紧,不用太用力感觉螺丝已经拧到底即可,因为是塑料件,太用力容易导致外壳变形损坏等,这里只需要装3课螺丝,另外一个孔被屏幕排线挡住所以不需要装螺丝,螺丝尺寸为M212(2mm12mm)