高通Camera 软件工程师的成长之路

本文以个人的工作学习经历来描述一 Camera 软件工程师的披荆斩棘之路,时间从大学开始,以至任职于高通 Camera 软件工程师结束,杂以本人学习过程当中的一系列学习笔记和博客文章,绝对是干货满满,甚至于稍有溢出,堪称手把手教你如何成为一名稍显合格的 Camera 软件工程师。另外由于是以个人经历为参考点出发的,所以有些看起来中间会有很多是无关紧要的,但是依然作为自己的经历一部分写进去了。

倘若是按照我这个学习路径来的话可能会稍显吃力,因为要接触的东西实在是太多了,并且有些可能会跟不上时代的发展,但是其中的原理还是有必要去了解的。与其说本篇文章是有关于 Camera 的成长之路,我觉得更合适的看法是把它当成一个主攻软件方向的嵌入式小白成长指南,因为里面有很多东西都是通用的,只不过因为本人工作原因,与 Camera 软件相关的部分稍微深入了一点,但是整体上还是根植于整个嵌入式的软件方向。

知识图谱

d4cc79b3a83dcf40478036e1a9e56203.png

从基础电路开始(时间 - 一年)

我在大一的时候刚入学的第一个国庆节后就开始参加社团的招新活动了,那个时候也没有人带着,属于一脸呆萌的状态,稀里糊涂投了好几个社团,只有一个叫「电器开发部」的社团录用了我,基于我现在的状态,我真的非常非常庆幸自己能够在刚刚大一的时候就加入到这样一个优秀的社团(或许,是我自己优秀的气质吸引了招新的学长学姐吧),这个社团给予了我非常多的成长经验,教会了我极其丰富的知识,也给我带来了若干个国家级和省级的奖项。

这个阶段基本上是了解一些基础硬件电路的知识点,也包括一些数字电路设计理论,后者我学的比较差劲,惭愧惭愧。基础电路能够带给我什么知识点呢?我觉得到现在为止就算做着纯软件开发但依然受用的有下面几个点:

  1. 硬件电路设计理念的抽象,用于软件设计,比如说 V4L2 的架构设计

    元件、管脚、铜连接线的概念。

    具体内容可以看 V4L2 这里。

  2. 基础的概念,短路、接地、复位、重启、刷新等等。

    这有助于帮助我们判断一些简单的工程机故障以及对工程机做一些必要的防护措施,比如知道如何防静电等等。

    硬件的复位也会对应到软件上面的流开启、流关闭、设备的打开、关闭等等,有助于了解软件上面的一些时序问题。

  3. 线路干扰,尤其是频率上的串扰等杂乱信号。

    这个在工作中确实有遇到过因为信号功率较大导致两个没有做好良好设计、屏蔽的模块互相干扰,进而导致模块启动失败。

    常见的问题表现为开启了 B 模块,A 模块就难以正常工作。

  4. 逻辑电路设计思维带来的优势。

    众所周知,数电里面的逻辑电路设计,尤其是逻辑门电路的理论设计推导是极其复杂的,但是它能够对应到一些 ISP 算法设计以及其它一些软件设计理念上面去,这部分也是对思维训练有很重要的帮助,尤其是逻辑推导和设计这块。

我从大一加入社团之后就开始参加一些社团提供的基础理论讲解课,我到现在为止记得很清楚的事就是,第一个手工作品是做一个三色流水灯,不用任何数字逻辑元器件,只用最基础的电阻、电容、三极管,是一个非常基础并且经典的电路示例。后面逐渐加深了对电阻、三极管、二极管、电容等等基础元器件的理解,大型集成电路以及数字逻辑元器件都是由这些基础逻辑元件组成的。

值得怀念的是,从三色流水灯开始,到完成一个拾音、功放设备,到后面的 51 单片机的加入,再到 K60 32bit 的“高级”单片机,再到 ARM 的 A 系,逐级不断升级,这中间我做了很多很多当时觉得很有趣的东西,也多次完整的主导并设计实现一个软硬件综合系统,带来的知识量积累是十分可观的,我还记得起来的做过的东西有下面几个:

  1. 三色流水灯:

    主要元器件为三极管、电容、电阻、发光二极管。

  2. 功放、拾音器:

    主要元器件又多了 mic、喇叭、增强版三极管增益电路元器件 - LM386(想必电子专业的对这个元器件非常熟悉),可以做一个丐版卡拉 OK,虽然东西早被我扔了。

  3. 液晶显示系统:

    加入了单片机、 液晶屏(LCD1602,LCD12864)等器件。

    软件编码量开始逐级上升。

  4. 无线收发系统:

    除了单片机,液晶屏,又加入了 NRF2401 这一无线收发芯片。

    这部分极大的增强了我的代码能力和调试能力,无线收发设备的收发联调在当时对我来说是个极为蛋疼的一件事,它不是跟液晶屏一样可以逐步调试并确认寄存器什么的对不对,我记得联调的时候经常出现明明什么都对但是就是收不到的情况,不过经过艰苦奋斗终于搞定了这个事情,也为以后的飞思卡尔车车无线调试打下了基础。

  5. 飞思卡尔(那个时候还是叫飞思卡尔)智能车电磁组软硬件系统:

    这个是我全权负责的第一个大的项目,软件我自己包办,硬件除了信号放大板之外我设计并实现了所有的 PCB 电路,车子的硬件调试就交给我另外一个好基友了。

    这个项目是对我的软硬件综合能力、组织协调能力、项目进度控制的综合考验,虽然最后只拿到了华北赛区的三等,但是这个过程让我的思维能力、软件设计实现能力有了一个级的跨越。

    现在依然时不时想起那段艰苦奋斗的岁月,连续快半年断续通宵、无规律作息、肝电路、肝软件,现在回想起来还是一段极其美好的时光,我可太怀念了。

  6. 杂七杂八的还有蓝桥杯、数码管日历显示系统。

    还有电赛的风力摆系统,包办软件和硬件电路设计(复用了飞思卡尔的成果),硬件结构设计还是我那个好基友和另外一个大佬,这个肝了四天三夜,睡觉不超过 12 个小时,全部都是间断的休息,最后封箱之后是个凌晨,回去还特么冲了个凉水澡,一觉睡到晚上,瞎吃了点东西之后又睡到第二天,现在想想这也太能肝了,放现在绝对猝死的节奏。

    这个最后是国赛二等奖。

大学的时候还是要多参加些比赛,能够作为队长参加就更好,能够全方位的锻炼自己的能力,非常有价值的经历。因为 Camera 底层的开发和硬件离得很近,软件上还没有抽象到连硬件都看不到的地步,所以这些电路的基础理论为我提供了很多底层知识,比如寄存器概念、时钟的复位、时钟频率配置、串行并行等等,这些也不是说要实打实用上去到软件开发上面,而是很多时候知道这些基础概念会对软件编码、调试带来很多的便利,有很多抽象概念你可以一眼就知道它是做什么的而不至于两眼一抹灰。

C 语言初级阶段(时间 - 一年)

上面提到的项目如飞思卡尔和电赛的软件系统已经很大的锻炼了我的编码能力,但是那些仍旧只是从小白进阶到小灰而已,过程中学到的知识点我大概能够总结为下面的几个方面:

  1. 变量的定义由无意义的符号到有意义的字符串。

  2. 程序的排版、换行、缩进习惯养成。

  3. 常用操作符的熟练使用。

  4. 基本 C 库的使用。

变量的定义从最开始的时候我一般都是用 a, b, c 之类的字母去定义。当时我们接触到的程序都非常小,无非是那种课后练习作业,让写一个冒泡排序之类的东西,亦或是写一个小的设备驱动程序,之所以说设备驱动小,是因为都是些比较简单的设备,比如 1602、数码管、温度传感器等等。最开始的时候我看到那种驼峰命名、Linux kernel 的命名规范的时候还想着这些字符也太长了吧,看起来贼麻烦,原因就是因为没有接触过大规模的代码,所以才觉得说单纯的一个字母反而好理解些。关于 C 的编码规范有一个网页参考:http://users.ece.cmu.edu/~eno/coding/CCodingStandard.html

程序的排版也是很重要的一项技能点,也可以说是习惯,不良好的排版习惯会让后期维护、调试变得极为头疼,其中最显而易见的影响就是看起来杂乱无章,有强迫症的分分钟想锤墙自尽。其次就是会影响到对程序逻辑的阅读理解,差的排版会增加对程序的理解困难程度。换行和缩进的争议一向很严重,有的喜欢左边花括号另起一行,有的喜欢放在本行结尾,有的喜欢用 tab 键缩进,有的喜欢用空格缩进。我自己目前的编码习惯是:

  • 花括号另起一行,程序的分块看起来会更加清晰,虽然占用了更多的行数。

  • 缩进使用空格,4 个为单位,因为 tab 键在不同的编辑器上面会显示不同的长度,一旦混用 tab 和空格就可能会导致程序不能看了,即使只用 tab 也无法保证所有人看到的缩进都是一样的,空格就可以保证任何编辑器任何系统任何人看到的格式都是一样的,保持统一。

C 里面的常用操作符比如加减乘除、括号啥的在没接触到程序的时候已经刻在我们脑子里面了,但是有一些看起来比较新颖的操作还需要练习记忆,比如与或、异或、按位取反、逻辑与或、左移右移、还有一些三目运算符等。这些稍微进阶一点的运算符需要在稍大型程序的开发设计当中引入并不断实践练习,下面的图是我在网络上面找的一个不是特别全的运算符列表:https://www.tutorialspoint.com/cprogramming/c_operators.htm
924582a17498c1382c8ba0bdd9d0f56e.png
基本 C 库的话在单片机的小型程序上面根本用不到,因为那些只需要我们直接寄存器操作就好,甚至很多时候标准 C 库的函数方法例如:printf、strcmp、memcpy 等等都需要我们自己去封装实现。所以说另外一方面来讲,单片机上面的编程以及编译很多时候为我带来了相对于成熟 linux 系统没有那么完善的本质知识体验,比如程序的链接、运行过程在单片机里面可以通过比较简单的汇编来实现人工实现而非编译器自动化的集成。并且由于单片机的地址比较简单,51 常用的是 8bit 的寻址空间,这么小的寻址空间会使得内部的寄存器分配十分紧凑,并且数据地址宽度没那么大也可以很方便的实现地址分区的入门概念学习,虽然简单,但是基础理论概念确实极为有价值的,由渐入深再去看 32bit 的 Linux 系统常用硬件架构就会轻松不少,至少有些概念不至于说是完全没有一点点印象。

后期稍微进阶一些的单片机程序,有的是从编译器层面集成了 C 库,有的是通过自己封装提供了类 C 库的标准接口给到开发者去使用,对于 C 库的一些概念如下所示(部分摘自网络):

  • C 标准函数库(C standard library,缩写:

    libc)。

    标准函数库通常会随附在编译器上,是最基本的C函数库,也叫ANSI C。

    ANSI C共包括15个头文件。

    1995年,Normative Addendum 1 (NA1)批准了3个头文件(iso646.h、wchar.h和wctype.h)增加到C标准函数库中。

    C99 (就是 1999)标准增加了6个头文件(complex.h、fenv.h、inttypes.h、stdbool.h、stdint.h和tgmath.h)。

    C11 (就是 2011)标准中又新增了5个头文件(stdalign.h、stdatomic.h、stdnoreturn.h、threads.h和uchar.h)。

    至此,C标准函数库共有29个头文件。

    总而言之,几乎在任何平台上的 C 语言 (包括非 UNIX 平台) 都支持此标准。

  • POSIX 是 Portable Operating System Interface(可移植操作系统接口) 的缩写,X表示UNIX,它是 ISO C 的延伸,明定了一个可移植的操作系统所应具备的种种条件,其范围不只有系统函数库而已。

    POSIX库 就是C POSIX library。

    C POSIX library 是 C 语言的 POSIX 系统下的标准库。

    包含了一些在 C 语言标准库之外的函数。

    为了 OS 之间的可移植性,POSIX 标准规定了一些标准的接口。

    而这些接口标准的集合就是 POSIX 库。


    GNU C库(英语:

    GNU C Library,常简称为 glibc)是一种按照 LGPL 许可协议发布的,自由的,公开源代码的函数库。

    既包含 C 标准库,也包含 POSIX 库。

    glibc 是 linux 下面 C 标准库的实现,即 GNU C Library。

    glibc 本身是 GNU 旗下的 C 标准库,后来逐渐成为了 Linux 的标准 C 库,而 Linux 下原来的标准 C 库 Linux libc 逐渐不再被维护。

    Linux 下面的标准 C 库不仅有这一个,如 uclibc、klibc,以及上面被提到的 Linux libc,但是 glibc 无疑是用得最多的。

    Glibc 的手册可以在 GNU 的网站上面找到:

    https://www.gnu.org/software/libc/manual/pdf/libc.pdf

特别是在后面工作当中遇到的一些需要优化的问题也会直接要求我们去在 C 库上面去动手脚,可以参考后面的 #第一次接触商业代码 章节。后期的时候我用 Keil 和 STM32 单片机对自己的单片机知识做了一个复习,写了一系列比较完整的基于 STM32 的总结文章,整个的文章列表在这里:https://blog.csdn.net/u013904227/category_9265745.html 。其中我觉得比较有价值的是下面两篇:

  1. https://yellowmax.blog.csdn.net/article/details/51168546 这个是 STM32 的一个启动文件描述,会由编译器自动烧写到指定的片上内存当中引导启动,这部分把整个的单片机启动过程剖开了给我看,对整个的系统启动、程序加载理解有很大的帮助。

  2. https://yellowmax.blog.csdn.net/article/details/51285511 这个是我把 Keil 整个的 IDE 剥掉之后,看看整个单片机程序是如何被编译、链接的,我自己做成了一个 Makefile 的形式实现对 STM32 的程序编译与运行,这部分完全刨除了外在的 IDE,让我对单片机的编译、链接过程有了更多的本质性理解。

进阶的程序设计(时间 - 两年半)

这一阶段主要的大的学习项目就是下面几个:

  1. 51 单片机做的一个超级迷你的操作系统,准确来说应该是时间片式的任务切换调度系统。

  2. 基于 S3C2440 硬件平台的 Linux 系统学习,包括 bootloader、驱动、应用编程,这部分全部都是按照韦东山的视频教程来的,强推。

  3. 基于 Linux 平台的一个 CGI 网站服务程序,仅演示了最基本的静态 html 的部分服务,但是仍然带来了很多的网络接口编程知识。

特别说明的是,这几个小玩意儿都是在我大学即将毕业的一年里面做出来的,其中第二个是我基于韦东山大佬的视频教程一点点做出来的,有一部分当时实在是理解不了于是就直接一点点抄了过来,但是总算是写出来了,有一个带进度条显示的 mp3 播放系统的子功能是我自己加进去的,当时还挺自豪哈哈,剩下的都是在毕业前的三个月内时间搞定的,可以说是对整个大学阶段编程能力的一次检验。对于找到一份还不错的工作来说,我可以确定只要上面三个能够自己看完架构自己从 0 不再参考我的代码做出来,那即使是一个一般的二流本科、或者再差一点点的学校吧,一定是能够通过至少一个 IC 原厂的校招面试的,前提得是融会贯通。

以下项目的完成需要三个月左右时间

51 单片机的进阶程序设计 - 一个基于 51 单片机的迷你操作系统的实现

这个当时校招的时候被我写进去简历里面了,由于简历里面写的比较唬人,所以遭到面试官的王之蔑视哈哈,不过很感谢他没有把我刷掉,话说校招在武汉找的,当时是全志的校招在武汉那里有一个比较大的招聘分部,那个时候全志还是巅峰未下降的时候,我真的从头到尾稀里糊涂过了技术面、甚至一个团面,团面我真的不知道我是咋过的,想来竟然是有特别的缘分!?不过现在想来可能也是有这几个小玩意儿的加成,虽然短时间内无法跟面试官展现太多,但是这个看起来蛮唬人的项目相信还是为自己加了不少分的。

扯回来,这个程序的链接在我的 Github 仓库:https://github.com/YellowMax2001/51_OS_MINI/tree/master/51os_mini-0.16
其提供的基本功能包括但不限于以下几个方面,在 Github 的 readme 文档里面有一个比较详细的描述:

  1. 一个基于均分时间片的任务切换模块,抢占式内核、支持优先级256级(暂时不提供串口界面新建自定义优先级的任务)、支持任务延时/挂起、任务数量理论最多32 个。

  2. 一个自定义的简单文件系统,支持文本文件与文件夹的创建与删除(文本文件不超过508个字节)、文件详细类型的查看、文件系统格式化。

  3. 基于串口命令行的指令,支持的命令包括 ps, vi, cd, cat, ls 等等,具体的描述如下:


    308866101ae907e8a7ea25ceb91545b3.png
    这个项目还存在有不少的 bug,不过作为学习使用和编码的练习来说是足够了,这个项目我到现在看来也是丝毫不过时的,包括我自己的编码风格、文档说明,以及项目本身的功能性都很惊艳,我敢说电子信息工程的在大学毕业前半个月的时间能够写出来这个的人绝对不会很多的。

这里是 CSDN 上面的文章链接:https://yellowmax.blog.csdn.net/article/details/51044714 ,比较详细介绍了这个项目的内容,包括基本流程,开发环境是什么,如何添加一个自己的程序到这个系统里面。这个项目我觉得完全可以看做是对我大学四年接触到的单片机软件知识的一个汇总性项目,里面涉及到了链表类型的数据结构,虽然 51 单片机对指针的操作支持并不好,我那时就没用用到过 Linux 下的那种指针类型,链表的数据结构都是我用内存手动寻址操作实现的,包括内存块的分配也是在一个专门的文件里面实现的。

总结该项目涉及到的知识点:

  1. C 基础,操作符、宏、函数、指针等等,这些就不多说了。

  2. 链表类型的数据结构在 51 单片机上面的实现。

  3. 函数调用栈的保存与恢复,用于时间片任务调度。

  4. 中断的概念以及中断的配置和使用。

  5. 内存块的分配管理,我这个是把每一个内存块大小定为 512 字节,我记得这款 51 单片机的整个内存大小有 1M 貌似,这个是内部分 EEPROM 内存区,当然也可以接外界 ROM,但是我为了方便移植就只用了内部 ROM。

  6. 良好的编程习惯和文件夹、文件分布管理。

  7. 任务的定义与执行、基础 Linux 指令的操作。

  8. 单片机里面初级的互斥操作,比如在切换任务的时候要把中断关掉以避免其它中断打断当前任务造成错误。

  9. 栈区、堆区的区别以及在 51 单片机上面的实现方式。

基于 S3C2440 的 Linux 系统学习

这部分就是基于韦东山的一期、二期、三期 Linux 视频教程来的,对于想学习基础 Linux 系统的铁子们可以买来跟着学习下。一期的视频主要就是从单片机思维讲起,没有一开始就来高大上的 Linux 系统,然后到 bootloader 的移植使用,这部分最有价值的就是让我从单片机那种零散的驱动开发模式慢慢转到通用形式的驱动开发模式。其中比较有参考价值的就是 bootloader 的移植过程,其它的内容对我来说基本上都已经接触过了,并且后面工作也大多用不上,思想上已经有那块的知识面就行了。

bootloader 就是一个大点儿的比较通用的驱动程序集合,充当引导片上系统启动的功能,很多时候 bootloader 已经初始化过的一些东西到了 Linux 内核启动的时候还是要再次重复初始化一遍的。这块工作之后的芯片原厂也会有专门的岗位去做的,但是我个人觉得价值不大,包括成长价值和金钱价值,但是学习这部分对于复杂点的 ARM core SOC 的启动过程了解还是有帮助的,对于我自己来讲这个部分的学习也对于我工作内容有所帮助,具体就是一个启动优化的内部项目。bootloader 的文章在这里:https://blog.csdn.net/u013904227/category_6238924.html

二期就是对于 Linux 驱动的开发讲解,其中最主要的东西,对我自己来讲,就是要理解并习惯 Linux 内核下的驱动程序开发套路,以及理解 Linux 下主要的几个子系统驱动程序框架。我比较关注的就是字符设备驱动、总线型设备驱动程序框架这两个,其余的比较高级的驱动程序框架比如:I2C、Display、V4L2 等等很多都是从这两个衍生出来的。其中字符设备驱动程序就是最最最基础的一种驱动程序结构了,当然还有网络设备驱动、块设备驱动啥的,但是这些跟我的工作相关性并不大就没有特别去关注过它们。

其中有两篇文是我当时学习的时候比较关注的点,也是我觉得在这个阶段收获最大的两个知识点:

  • https://yellowmax.blog.csdn.net/article/details/50804833 :

    这个是字符设备驱动的一个简单介绍,现在看着是有点简陋了写的,不过抛砖引玉倒还合适。

  • https://yellowmax.blog.csdn.net/article/details/51167886 :

    这个是总线型设备驱动程序的架构介绍和使用实例,在 Linux driver 里面有很多驱动总线类型,在 ‘/sys/bus’ 里面可以看到,比如说 i2c 这个就是一种通用集合还有 gpio 等,本文介绍了如何去创建一个新的 bus 类型并去使用它。

三期就是项目实践了,就是我说的那个我加了一个带进度条的 mp3 的那个项目,在教程里面是一个数码相框项目,里面涵盖了极其丰富的内容,这部分完全可以帮我跨过实习生的阶段直接到一个基本合格的正式工,项目的代码在我的 Github 上:https://github.com/YellowMax2001/digital-picture 。本项目包含的内容点包括但不限于以下几个方面:

  1. C 的面向对象玩法,包括泛型参数、函数指针、结构体继承、简陋的重载等等。

  2. Freetype 字符库的使用和裁剪,对字符编码会有一定的了解,ASCII、UTF 等:

    https://yellowmax.blog.csdn.net/article/details/52104668

  3. Linux Framebuffer 显示驱动架构以及对应的用户空间编程。

  4. Alsa 声音驱动架构了解,以及 mp3 解码库、mp3 格式的使用和了解:

    https://yellowmax.blog.csdn.net/article/details/52184038

  5. Jpeg 编解码库的使用了解、jpeg 编码原理(深入下去有离散余弦变换、ZigZag 编码、哈夫曼编码、块效应消除、量化等等细节内容)的学习了解:

    https://yellowmax.blog.csdn.net/article/details/52105336

  6. 图像的线性插值方法,这里只用到了近邻插值,用以实现图像的缩放,进阶的有后来接触到的双线性插值等等。

  7. Linux 下的文件操作,读写、映射等等。

  8. 输入设备驱动架构,例如按键、触摸屏、串口等等,包括输入设备的操作去抖动原理。

  9. 图形显示,比如画点操作、划线操作、规则形状填充、透明度/颜色设置等等,这个也会加深对颜色编码的了解,工作用会接触到更多的颜色表示方法,基础原理都能够互通。

  10. 文本文件格式、图像格式、音频文件格式等等。

这个阶段更多的是对稍稍大一点的程序架构的了解和掌握,其中包含了初级的分层、分模块的设计思想,这部分虽然还属于是初入门级别的,但是基本原理已经和商业的代码不相上下了,学之受用无穷。还有就是对 Linux 环境下编程的逐步接受,后面在工作之后又陆续看过 「Unix 环境高级编程」、「Linux 设备驱动程序」等书籍,进一步夯实了 Linux 下面用户控件和 Kernel driver 的编程知识。

基于 Linux 平台的一个 CGI 网站服务程序

这个小的项目看起来实际上算是个跑题的项目,和主线学习看着没啥关联的样子,但是对我来说还是有些许帮助的,主要体现在后面工作之后的 Camera 编码部分的工作,比如 RTSP 传输协议的使用以及一些编码接口使用等,所以这里就把它加进来了。这个项目主要是用 Linux 系统提供的接口实现了一个 CGI 后台服务器程序,功能比较简单,只支持静态页面的响应,但是已经有助于了解一些比较简单的网络传输协议和报文封装了。

具体的项目代码在这里:https://github.com/YellowMax2001/big-http 。仿 boa 写的,可以使用嵌入式开发板运行(打通了网络接口),或者是运行在 Linux 主机上面,然后直接在局域网开一个浏览器去访问对应的地址就可以,里面有一个默认的静态主页,提供下面几个元素:

  1. 基础 Html 文本以及基础语法组件。

  2. 支持图片,音乐,视频等多媒体文件的下载、播放、上传等。

说起来做这个玩意儿的契机是我上大学的时候总是觉得 C 语言就一个黑框框,写起来代码实在是太过无聊了,于是偶然间在网络上发现杨中科的「C 语言也能干大事」系列教程,于是我就跟着学了几个月,里面主要是用 C 构建了一个 WIN 平台下面的图形界面程序,我用他教的作为基础做了不限于以下的几个小玩意儿:

  1. 一个 WIN 上面的音乐播放器,用 VC6 搞的,老古董了已经。

    基本的操作如添加、删除播放列表,声音调节,进度条,歌名显示等等好像都是有的。

    https://github.com/YellowMax2001/Music-Player

  2. 一个基于网页的人员管理工具,就只提供添加、删除、查找列表形式组织的个人信息,没什么用,就只学会了点儿数据库皮毛和 CSS 皮毛。

    https://github.com/YellowMax2001/cgi_web

杨中科的教程是免费的,这部分其实和主线没什么关系,不过感兴趣的话可以折腾下,我自己反正当时也是没少折腾:https://blog.csdn.net/tsingke/article/details/6395371 。这部分我觉得对于编程的能力提升不大,可以说是基本没有啥提升,不过就是增加了一点点数据库操作、CSS 原理、HTML 网页格式等与网页前端或后端相关的一些杂乱知识点,作为兴趣拓展可以,深入就 Duck 不必了,要想精进编程、数据结构与算法能力还是参考上面其他两个项目以及老老实实用黑命令框调试的好,这个阶段如果不是特定专业的话写 UI 真的浪费生命。

未接触的遗憾

这部分搞完我就去参加校招了,随后随着三个月的公司实习结束,我的大学生活正式结束,这里回顾下感觉大学搞得东西真的蛮不少的。不过中间走了不少的弯路,比如那个带 UI 的程序设计项目,中间还搞了个人网站玩儿,没深入进去也没搞出啥外快收入的名堂,浪费,还有特别把「鸟哥的 Linux 私房菜」第一部好像是,对着敲了快三分之二,结果大部分后面都用不到,浪费。还有就是回顾过来发现根本就没有静下心来去学习过数据结构与算法的知识,大学阶段其实蛮适合去学习这些知识的,只不过我当时总是觉得这些整天和黑框框打交道,实在是太无趣了,于是就没学,现在想来简直是极其不划算,我应该把上面几个浪费时间的学习替换成数据结构与算法的练习上面去。

工作之后的时候倒是恶补了一段时间的常规数据结构与算法原理,也写了不少的代码,其中一部分整理出来写成了专栏文章,这部分是我工作之后才写的,对于在校阶段来说还是蛮适合阅读学习的:https://blog.csdn.net/u013904227/category_8608198.html 。里面包含了排序、查找、链表等内容,再往后的我学了但是没有总结出来形成文章。但是作为嵌入式的工程师来讲,掌握下面这几个子项目已经可以应付大多数编码工作了:

至于对于搞嵌入式的来说,这些玩意儿到底有没有用呢,肯定的告诉你,有用,绝对有用,要想精进升级到中高级去大厂搞搞更高级的玩意儿,那这些就是蛮有用的,如果一直在小厂或者是完全可靠人脉那就没有太大的意义,不过学了总是没坏处的,这个并不会像网页前后端一样浪费时间,学了会受用好多年的。

这里贴下当时本科毕业校招的攻略

第一次接触商业代码

从进公司工作开始,其实从实习的时候已经开始接触到了工程上的代码了,用于 IPC、运动 DV、行车记录仪产品的用户空间代码和内核空间驱动。在最开始接触到这些代码的时候就是一脸萌,这些庞大的代码看起来好像是一座摩天大楼一般,不同功能的模块巨多,好似一整栋互相连通的房间一样组成了一个大迷宫,进去之后发现一个房间外还有另外一个房间,永无尽头,然后跑了一圈回来曾经到过的房间又会发现怎么已经被装修到妈都不认识了,又得重新探索一遍了。刚开始的时候就会是这个样子,还没办法很好的把自己的思维收缩到特定的模块,因为那个时候每一个模块看起来都是牵一发而动全身的,任何一个问题从问题点去追查总会必须经过好多个模块,这就导致我初期的半年内极为头痛。

对于校招生来说我自己是比较建议去公司实习的,不用写太多代码,就仅仅帮开发做一些单元测试的工作即可,这样可以很好的帮助我了解产品的软件架构分层、分模块状况,并且测试工作并不需要特别多的积累,只要智商正常都可以很快上手工作的,实习这段时间也完全不必要急着上手要去写一些正式的代码,就通过测试来对整个产品的软件代码进行抽丝剥茧、层层探索即可,做到能够掌握代码都有哪些模块,都起着什么样的作用,模块之间是怎么联系起来的,一个完整的业务逻辑流程是怎样的就可以了,这对后面正式入职之后的 过渡期极其有帮助,不必尽知其意,囫囵吞枣,有个概念即可。校招还是有时间有机会去做这一步的,社招就没有时间机会给你这些过度时间了,所以前期的业务逻辑、代码框架基础是十分重要的,对于后面新业务逻辑的切换有着非常大的帮助作用。

我自己在正式入职之后也是做了大概半年时间打杂的事情,其中做过的一个比较有意义的东西就是在产品里面加入一个长曝光的功能,我的技能真正开始进入状态是在第一次接到任务的时候,我记得还很清楚,难度比较高的是让我在 SDV 软件功能上面加一个长曝光的功能,其他还有一些杂七杂八的小任务,不过都很简单,没有什么难的。这个任务的难度在于它需要统筹很多的软件资源,包括从摄像头 sensor 驱动到最上层的 APP 这一整条链路都得你自己去打通,并且去设计它的软件实现以及如何编程使用。我作为一个菜鸟,公司初来乍到,人比较生地不太熟的情况下,这是需要协调驱动、算法中间件、逻辑中间件、APP 四个方向的人员去完成的,虽然它的功能逻辑并不复杂,就一个很单一的长曝光功能,不用去设计复杂的交互逻辑,还得考虑复杂业务逻辑的各种通路,但是重点在于协调资源以及软件通路设计,这个过程我就不说了,涉及到沟通与功能设计实现,总之它是我花费了比较大功夫做出来的一个功能。期间找了很多人帮忙,最后迭代了一次才把功能做的标准化、模块化的。这个任务让我从宏观上了解了一点我当时那个系统的运作方式,为以后的工作打了一个基础。

这个阶段最大的收获我觉得包括但不限于以下的几个方面:

  1. OpenMax、V4L2 架构的掌握以及其设计思想、设计方法的思考与理解。

    这块的学习过程中带给了我非常多的视频软件架构的常识性知识,就是为自己建立了一些自己已经知道并作为基础知识掌握的一个体系,然后在后续的工作当中经常会无意识的把这些基础常识代入进去,会有很多方便的。

  2. 软件模块的编写与集成。

    这部分是把我自己的编码模式和编码习惯扳到了一个还算正确的道路上,很多时候我们总是说,不必要求上来就绝对王者走位,但是至少得有一个王者意识,只要走在正确的方向,那就非常有帮助了。

  3. 基本的软件开发流程的熟悉掌握。

    包括但不限于 SOC 从设计到 FPGA 验证,到 PCB layout、实机验证、驱动适配、软件适配、功能开发、回归测试、发布这一整套流程。

    还有不同部门之间的计划拉通、日程同步等等。

    其它的技能如 Git、IDE 等的使用,编译环境的搭建、简单脚本的编写等等都是一个看起来不明显的隐形技能,但是对于工作还是很有效率上面的帮助的。

  4. 杂七杂八的知识积累,不求甚解的那种,比如 sensor 曝光原理极其部分寄存器配置、些许硬件知识、编解码基础原理、网络传输基础原理、部分算法、性能优化等等等等,有些到现在还会偶尔用到,大部分虽没什么用但是挺有用的(haha。

其中有一个没人可以帮我忙的项目,叫做运动 DV 启动优化,目标是从上电到第一帧画面显示时间间隔要在 3S 以内。如果说其它的项目或多或少都是有人可以问可以帮忙的,那这个项目就是 90+% 得靠自己单打独斗,期间没少受到怀疑,不过最终还是搞定了它,这个能够给自己有很多威望加成,后面就会有很多的优质项目第一时间考虑自己。这个过程中我除了这些还有些软性技能收获,那就是很多时候干活要多尽量为自己争取贡献大的活,有 70% 把握搞定就可以去争取过来了,并且贡献大的活不一定难,但是对自己的职业发展绝对很有帮助。

从技术角度来讲,这个启动优化的项目主要包括下面几个方面的知识点:

  1. U-boot 可以完全剪掉不要,这样会牺牲掉一些功能,比如 u-boot 的扇区擦写、网络下载、启动设置等功能,没有 U-boot 可以省去加载 U-boot 到内存以及运行 U-boot 的时间。

  2. ramfs、ramdisk、rootfs 等的区别,快速启动可以构建一个最小化文件系统用 ramdisk 方式启动,rootfs 里面放置可执行程序提前运行,然后使用 overlayfs 加载其它库并合并切换 rootfs 到正式的 fs。

    这个部分是快速启动项目的核心,因为它可以利用多核的并行化加快整个的 fs 加载速度。

  3. C 库的裁剪选型、其它库的裁剪编译。

    这个是不同 C 库的运行速度、体积、函数数量等等的对比:

    http://www.etalabs.net/compare_libcs.html 。

    有些简单项目里面我们会使用 musl-libc 来减少 C 库的体积。

最最重要的两个框架,OpenMax 和 V4L2 这两个,我也都分别写了专栏来去总结记录:

  1. OpenMax

    https://blog.csdn.net/u013904227/category_9272882.html

  2. V4L2

    https://blog.csdn.net/u013904227/category_9277668.html

这两个专栏基本上是我在上一家公司所有工作内容的集大成之作,基本上包括了软件方面最有价值的收获,前者是嵌入式流媒体的通用框架参考,海思等的 IPC、SDV 平台都会用到类似的结构,这个架构会带来很多音视频开发组件的套路性知识点,掌握之后再去看一些更加复杂的结构就大概可以做到心中有数,不至于慌。而 V4L2 则是 Linux 内核下的视频输入驱动框架,里面的设计思想非常有趣,这个我反倒觉得代码本身没有那么重要,其设计思想、组织方式非常具有含金量,基本上这两种架构掌握之后再去看其它的大多数架构代码都会有一种:这个 code 我好似在哪儿见过的感觉。当然还有更加高端一点的架构设计,不过有了前面的基础,后面的掌握起来也会很方便的。

其实到这里已经可以实现 Camera 的入门级开发要求了,掌握上面的编码模式、框架已经可以在软件上面具有一定的议价能力了,不过当然还是缺乏更多的实践锻炼,毕竟这些架构在我掌握的时候也只是用于百万级别的产品线上面,复杂度还远比不过后面接触到的亿级的产品代码,但是满足中低端消费产品的 Camera 软件开发绝对够格。如果再加上数据结构与算法的部分基础知识,那对自己的竞争力提升会更加有帮助:https://blog.csdn.net/u013904227/category_9285894.html

算法的领路人

这里指的算法是特定行业特定模块的算法,比如我这里说的就是视频防抖、畸变校正这两类通用算法,并不包括计算机行业的通用算法(如排序、查找、匹配、寻路等等)。这些个算法的初次接触都是在我的第一家公司,这里不得不说第一家公司给了我太多的职业发展帮助(虽然薪资不够有竞争力哈哈),这个没的说。

我在上家公司的时候参与过两轮的算法集成和研发,其中第一轮是外购的三方算法,基本上都是纯软件的算法了,得益于四核的处理器,那个终端产品勉强能够运行起来到 1080P60fps 的算法,以及近似 4K30fps 的算法,这个过程当中我参与了大概是 30% 左右,因为当时我还算是新人,技术啊各方面并不是很成熟,所以就作为小跟班跟着大佬们慢慢接触了一部分的研发任务。后面把软件的算法硬件化了,当然只是一部分硬件化,这个过程当中的软件开发部分我算是直接主导开发的,过程当中学到了非常多的图像处理相关知识点,也接触到了一个算法从理论到最后落地的整个研发过程,中间遇到的问题、踩过的坑都是极其有价值的经历,我个人觉得这部分是最值钱的一个系列知识点了。

这部分的文章我并没有去写,其实已经开了一个头了,总结篇已经写完了,但是还没有确定要不要发,因为就本篇文章的目的来说,以上的知识点已经足够作为一个合格的 Camera 软件工程师了,虽不是那么高级,但是初级绝对是足够了。所以这里只是提及下后面进阶的其中一个方向都需要有哪些知识点,但是目前并不打算把所有的点详细展开描述,况且现在还没有形成系统的文章。

ISP 的处理过程中有着非常多非常多的小模块,比如说黑电平矫正、镜头阴影矫正、色彩矫正、坏点修复、降噪、超分、自动曝光、自动对焦、去马赛克、色彩转换、边缘增强、Gamma 等等等等,详细列举出来大概得有几十个以上的子模块,所以图像效果的 Tuning 是个极为系统、复杂的东西,并且由于主观感觉不一样、模块的先后顺序也不一样,整个的最终效果出来也是很不一样的,正是因为越来越复杂的 ISP pipeline,所以有关 ISP 的技术才能够如此经久不衰吧。

总之如果要对 Camera 进行进一步的技术力提升的话,就得对 ISP 处理有更多的了解才可以,但是 ISP 的内容有那么多,一个人不可能对所有的方面都能够研究的很深入,这就需要去进行方向细分。方向细分的话会考虑到有具体的某一个技术点,或者是架构师、系统集成,对于从整个系统层面去把握日常项目开发的这种来说是不需要特别详细的了解每一个细分技术点的,只需要对每个部分的大概功能有所了解即可,而如果是希望往某个技术点深耕的话就需要选择一个自己喜欢的点去挖掘了。

对于我自己的话,其实还是蛮喜欢系统架构的,但是目前为止的机会并不多,因为自己的技术能力和协调能力还不够,想往这个方向发展的话还需要多年的不断努力才可以,不过对于我自己来讲可能更需要的是机会而不是单纯的个人努力,虽说个人努力也可以一定程度上为自己获取更多的机会,相辅相成吧,不过从我的角度来讲,机会和平台还是特别特别重要的,这是我在上家公司体会到的点,技术能力一定要配合平台和机会去展示才有价值,才能够获得持续成长的动力。

到手机 Camera

之前做的产品都是运动 DV、行车记录仪什么的(还是非高端的),所以相对来讲应用场景并没有那么复杂,有很多的东西并不需要,比如说:自动对焦(镜头是固定超广角的),多摄系统等等。比起现在的手机 Camera 来讲,我之前做的东西可以说是丐版 Camera 了,基本上就是最简洁朴素的功能,而且由于成本制约,很多硬件并不会给你特别大的优化空间,连带着软件也无法做到更好的效果,并且由于成本限制、公司产品路线规划,导致软件层面实际上并没有那么多时间和经费给你重构、优化设计,很多都是吃老本的,所以导致整个的产品看着就是很不爽,感觉好多东西无法发挥出来,只能舍弃掉匆匆上场。

手机 Camera 一般都是跑在安卓上面的,所以必须要先对 Android Camera HAL3 架构

手机 Camera 由于其越来越多的功能需求以及各大厂商的极度内卷化,产品特性越来越复杂,软件架构越来越庞大复杂,摄像头也越来越多,所以整个的技术栈就极其深也极其广。之前有专门写过一篇文章来描述手机 Camera

目前手机 Camera 最基础的就是拍照功能,拍照功能也已经投入了非常多的研发,现在几个热门的发展方向是 HDR、超分、夜景等,这几个方向还有比较大的挖掘潜力。近些年来我从终端用户角度来看,感知最强的拍照进化有几个点(有的可能暂时没想到):

  1. 拍照不糊了,之前小米 6 一代的拍照还是无法拍移动场景,不管是拍摄者的移动还是被拍摄者的移动,都是一不小心就糊了,但是现在基本上已经可以做的很好了。

  2. 画面纯净度提升了很多,噪点少了很多,最明显的就是晚上拍照,会发现比以前的照片干净很多,白天就更不用说了。

  3. 分辨率的提升,这个感知算是最大的了,毕竟一亿像素不是闹着玩的。

  4. 对焦更快更准确了。

后面手机 Camera 会更侧重于视频的研发与进化,毕竟视频恐怕会成为未来的主流拍摄场景,5G 的普及使得短视频应用越来越火,算力的提升也使得视频拍摄的玩法越来越多了,现在比较重要的几个点(我知道的):防抖;HDR;视频人像;物体跟踪等。视频的优化比起照片难度会更高,会受到算力、存储、算法复杂度等等的制约,所以视频这块还有非常大的优化和发展空间,我个人感觉视频的最终形态就是能够赶得上电影级的拍摄质感和效果,当然由于硬件制约,我估计达到个百分之七八十就已经非常非常不错了,总之视频这块还大有可为。

未来的方向

其实相对于 Camera 这个方向来讲,已经是一个比较通用的技术栈了,比如说 SOC 厂如牙膏厂高通、海思等等;上游 IP 授权厂如 ARM 等;终端设备厂商如小米、苹果、华为等;软件厂商如字节、腾讯等;还有一大票厂商都会用到这一类的技术,也会有不少的相关职位,所以这个方向上来讲是没有太大问题的。近来各大厂商对于影像这块的投入越来越大,据我了解到的,小米和 vivo 都专门成立了全球范围内的影像专业团队,加大了这方面的投入。

未来影像这块肯定就奔着 AI 这个方向去了,雷总不是说以后的发展方向就是计算摄影了么,以后会有越来越多的场景会涉及到 AI 计算,而 SOC 厂商也会不断跟进专门的 AI 处理 IP,传统的影像算法不知道到时候还能不能够继续适用了,不过软件的集成是永远不会变的,一定一直都会有相当多的需求。而图像基础知识我觉得还是有必要不断地去学习拓展,因为即使是后面有更多的 AI 计算加入,一些最基础的理论肯定还是不会变的,就是内功永远不会变。

对于我自己来讲,可能会更加倾向于往图像的视觉处理方向发展,传统的 ISP pipeline 我打算是理解其基本原理即可,但是即使是自己想往视觉方向发展,有没有机会还两说,这块我自己也处于一种迷茫的状态,所以也需要尽可能早的找到正确的方向以及方法,然后才能够慢慢的往那个方向去发展。后面即使是要做管理更多的也会偏向于技术方面,毕竟自己也确实对这块有着不小的兴趣,兴趣促进学习探索的动力嘛。

End

下面把文中涉及到的一些链接按照文中出现的先后顺序整理下全部放在这里以供参考(很多无法外链,可以在阅读原文里面找到):

  1. V4L2:

    https://blog.csdn.net/u013904227/category_9277668.html

  2. C 编码规范:

    https://blog.csdn.net/u013904227/category_9277668.html

  3. C 运算符:

    https://www.tutorialspoint.com/cprogramming/c_operators.htm

  4. Glibc 手册:

    https://www.gnu.org/software/libc/manual/pdf/libc.pdf

  5. STM32 文章:

    https://blog.csdn.net/u013904227/category_9265745.html

  6. 51 OS Github:

    https://github.com/YellowMax2001/51_OS_MINI/tree/master/51os_mini-0.16

  7. 51 OS CSDN:

    https://yellowmax.blog.csdn.net/article/details/51044714

  8. Bootloader 描述:

    https://blog.csdn.net/u013904227/category_6238924.html

  9. 数码相框:

    https://github.com/YellowMax2001/digital-picture

  10. Http 服务:

    https://github.com/YellowMax2001/big-http

  11. 本科校招攻略:

    https://blog.csdn.net/u013904227/article/details/77825138

  12. OpenMax:

    https://blog.csdn.net/u013904227/category_9272882.html

  13. V4L2:

    https://blog.csdn.net/u013904227/category_9272882.html

  14. 数据结构与算法:

    https://blog.csdn.net/u013904227/category_9285894.html

  15. 手机 Camera:

    https://yellowmax.blog.csdn.net/article/details/104713388

8e58c7382117e10a31bea404307d9731.png

推荐阅读:

一篇文章带你了解Android 最新Camera框架

这可能是介绍Android UvcCamera最详细的文章了

深圳上班,

生活简简单单,

14年开始从事Android Camera相关软件开发工作,

做过车载、手机、执法记录仪......

公众号记录生活和工作的点滴,

点击关注“小驰笔记”,期待和你相遇~

fa987da3e167393c1ddf8fd2e6396ade.png

8f330e80e3f5795e3a1f9d82a0658b62.png

点个赞,一起加油!

posted @ 2021-10-30 08:00  小驰行动派  阅读(37)  评论(0编辑  收藏  举报  来源