一个嵌入式软件工程师的五年

学生三年,职场两年。五年之内,虽无大成,然心中有感,不禁自发。

一、学生三年

1.1、初次接触嵌入式

1.1.1、一名浑噩的大学生

我不清楚具体有多少人初入大学确定专业时就感觉上错了船,但我觉得不在少数。转专业似乎是条出路,但是很奇怪的逻辑是——一般只有专业成绩前10~20%才有机会。不过这只是一个托辞,自身浑浑噩噩才是问题的根本。

记得很清楚,大一课堂上,清醒的时候不多,从来也没坐过前几排。被辅导员批评过,也在期末等成绩的时候胆战心惊,但从没想过自己要做什么。就这样,一年。

1.1.2、谢谢通识教育

有幸学校开设了较多的通识教育课,作为水利水电工程专业的学生,虽然不少同学对《C/C++语言程序设计》和《电工与电子技术》这两门课不感兴趣,但是帮我打开了一扇窗。

初次接触 C 语言,让我看到了程序世界的精彩。慢慢地,我沉迷其中,如痴如醉。那本教材,是我读过最多的课本,细节之处都写下笔记,或是心得,亦有不解。作业和考试不再是应付,最终也取得不错的成绩。后来报考计算机二级,也毫不犹豫地选择 C 语言,并顺利通过。

而《电工与电子技术》这门课,真正让我接触硬件知识。我很喜欢开设的电子实验课,理论用于实践,动手完成各种硬件电路的搭建。

1.1.3、弱水三千,只取一瓢饮

信息技术领域有众多行业和职业方向,当初我也是面临诸多选择。往上,可以选择纯软件;往下,可以选择软硬件结合。在探究和调研职业方向的路上,我接触了较多的东西。

那段时间,我泡在图书馆,在计算机科学那么多排的书架前,如饥似渴翻阅着。这期间,我学习了网站建设、计算机辅助技术、嵌入式开发、自动化与工业控制以及数字电路等等。我没有特别仔细的研读,也刻意不去了解技术细节,目的是抽丝剥茧找到自己的最终方向。

选择嵌入式软件,看重的是物联网,也因为自己喜欢底层开发,与硬件打交道乐此不疲。在单片机和嵌入式 Linux 这两块,我优先选择了单片机开发,将 Linux 往后放一放。

1.2、坚定转行

鱼与熊掌不可兼得。本专业的学习在自己看来也算不上鱼,给自己定下的目标就是专业不挂科,一身心扑到正业上。

课堂上,埋头看《微机原理》;宿舍中,学习单片机开发。只有在期中或期末的考试周,才将时间用在专业考试准备上。当然,自我学习并没有那么顺利,遇到诸多的问题,自我管理的能力也有一定的问题。

1.3、寻求出路

临近毕业之时,便不得不寻求自己的出路。首先,本专业的工作是不愁的,但并非心之所愿;而后有两条路:跨专业考研和跨专业找工作。

深思熟虑过后,决定两条路同时走。无论何时,学习总是不会错的。以备考之学,滋求职之事,齐头并进,较为稳妥。事后证明,这样的选择是正确的。

在备考的过程中,系统地学习了计算机科学几门基础课程,例如《计算机组成原理》、《计算机操作系统》、《计算机网络》、《数据库原理及应用》和《数据结构与算法》等等。这些知识内容对后面的应聘面试大有裨益,顺利获得心仪的职位。

至于考研之事,初试表现平平,后因求职定局,复试的心思有所浮动,最终并未被录取。倒也没有太多的后悔,当时家里房贷压力较大,也有意早日工作为父母分担。

二、工作两年

遥记得毕业不到几天,便入职新公司。公司深耕工业互联网领域,致力于工业生产设备安全与可靠性管理的研究与开发,我在终端开发部门担任嵌入式软件助理工程师一职。

2.1、职场新人

公司为客户提供端到端——硬件产品端到系统平台端——的解决方案。作为新人,工作之初并没有直接参与终端产品软件开发,而是负责各个项目硬件终端到系统平台的配置和数据接入,以及终端设备的测试工作。

这样的岗位安排有助于自己了解整个公司的业务架构,若只是参与某项终端产品的开发,则犹如管中窥豹。而测试的工作也让自己熟悉了终端设备,并且深刻体会到测试工作的意义和重要性。

至此半年,完成了职场新人之蜕变。

2.2、独当一面

2020年不易,不仅是生活上,还有工作。在这一年里,我完成了一款量产产品和三款试产产品的软件开发。说是四款产品,其实大部分的软硬件设计是一样的,所以以下从其中之一详述,并解释其他三款产品设计的不同点。

2.2.1、第一款产品

前文说到,公司致力于工业生产设备安全与可靠性管理的研究与开发,其重点就是设备振动分析,因此终端开发部门的产品大多是振动检测器。无线振动温度检测器是我经手的第一个项目,也并不是从零开始的,是从初代产品软硬件迭代而来。

无线振动温度检测器采集振动温度传感器数据,进行初步的边缘计算,然后将原始数据和分析数据通过无线通信方式传输到网关设备。具体的硬件设计方案不便阐述,这里作简单介绍。

振动数据采集电路中,微控制器提供给模数转换芯片的时钟信号控制后者的采样频率,提供给滤波器的时钟信号控制其截止频率;加速度传感器的模拟信号通过滤波器滤波后作为模数转换芯片的输入信号,模数转换芯片自动将模拟信号转换为数字信号;微控制器和模数转换芯片通过串行外设接口(SPI)总线通信,读取振动数字信号。温度数据采集电路中,微控制器和温度芯片通过集成电路总线(I2C)通信,获取温度数据。微控制器和 ZigBee 通信模组之间通过通用异步收发传输器(UART)进行通信,将传感器原始数据或者处理过的数据上传至网关设备;同时 ZigBee 通信模组也可以接收网关设备下发的各种指令,通过UART接口发送给微控制器。

软件上采用多线程设计方案,基于国产实时操作系统 RT-Thread OS。主线程负责硬件初始化、数据采集和上传应用以及休眠管理,通信线程由主线程在进行数据上传业务时创建,看门狗线程在软件异常时进行系统复位。以黑盒的角度观察,设备周期唤醒采集和上传数据,然后进入休眠。参数配置也通过无线通信进行修改。

2.2.2、其余三款产品

第二款产品设计时,在第一款产品硬件基础上更换了新的加速度传感器,可以直接输出三轴加速度数字信号,因此也搭配了更大内存容量的微控制器。软件设计上和之前没有太多差别,通信协议上作了兼容修改,以满足多轴振动数据。

第三款产品设计时,在第一款产品硬件基础上采用 NB-IoT 通信模组进行数据传输。软件重新进行设计,主线程负责硬件初始化和休眠管理,主线程创建指令线程和通信线程;指令线程负责从指令队列取指执行,指令类型分为内部指令和远程指令,同时也可能产生通信任务;通信线程负责从通信任务队列中获取任务执行,同时接收服务器远程指令;看门狗线程在软件异常时进行系统复位。

因为指令执行时可能产生通信任务,通信时也可能收到新的指令,所以在指令线程和通信线程中,用到了优先级翻转,确保这两个线程始终是第一和第二优先级。当无指令且无通信任务时,才由主线程接管控制权进行休眠和唤醒管理。由于更换了通信模组,因此驱动层和网络层重新编写代码,好在 RT-Thread OS 有现成的 AT 组件,方便了驱动层的设计和编码。协议方面,参照选用的物联网平台文档和实际应用重新设计,充分考虑未来的扩展性。

第四款产品也是基于第一款产品的设计,从低成本的角度出发,在其他硬件原理不变的情况下,使用复用器,将振动增加到四通道。软件上大部分内容可以从之前代码移植甚至不需要修改,但是仍然带来一些变化。

首先,之前的时间管理和参数管理是一维的,即数据上传周期和各类参数都只需要一个。例如原先有电池电量、温度、振动时域数据和特征数据四个独立的上传周期,那么对于现在四通道的产品,需不需要十六个独立的上传周期?同样的,对于参数管理,某些与传感器个体特性相关的参数(如灵敏度)则必须扩展成四个,其他的参数则需要单独评估。

其次,四通道的使能和失能是否应该是可配置的。例如通过修改通道选择参数的二进制编码,可以灵活控制使用哪些通道,但这同时又涉及到传输协议。因为有些技术细节没有介绍,因此难免会引起读者的困惑抑或是质疑,这里不过多阐述,总之这是一款需要短时间交付、只有简单的需求文档、限于技术要求的产品。

2.3、二三心得

这一年半的工作当中,确实学到了很多,也踩过不少的坑。有一些摸索出来的浅显经验,也有一些一直都懂但没做好的道理,在此将这二三心得付诸文字,与君共勉。

2.3.1、软件需要设计,且需要好好设计

也许是带我的良师觉得我开发经验尚浅,害怕我写出来的软件 Bug 频出,又或许他有意识培养我先设计后实现的良好习惯,总之在这过程中我明白了一个直白的道理——良好的软件是设计出来的,当软件被设计好过后,程序员只是在用具体的程序设计语言翻译它罢了。

如果设计和构思阶段不能理清自己的思路,那么实现时也不可能清晰明了,这不仅仅是软件开发领域的原则。

2.3.2、尽早测试和自动化测试

在工作的一年半内,我做过相当多的测试工作。除了自己的软件需要充分的测试,其他的产品也需要,但是这过程不是很轻松和顺意。资料缺失,人工投入较多,没有良好的测试规范和指导,这些都是弊病。

测试驱动开发不是所有人能玩得转的,但开发中持续测试应当重视。尽早测试可以在代码体量不庞大的时候抓住害虫,试想在几百行代码和几万行代码中找出问题,哪个更加简单?自动化测试则是把人从繁杂的工作中解放出来,单元测试、集成测试、系统测试和回归测试都是必需且重要的,据我了解,这些在嵌入式软件开发领域还不是很流行。

2.3.3、文档

不知道你有没有遇到以下这些问题。产品使用者总是询问开发人员某些细节,是不是让你很苦恼?联调时,和同事的交流是不是占用你太多的时间?工作交接的过程中,有没有足够的资料让接手人员快速进入工作?诸如此类的问题数不胜数,解决问题的核心要素就是文档,详尽有用的文档。

缺乏文档已经是程序员日常工作中排名前几的难题。代码文档化也是近年来风靡的概念,并且有较多工具可以帮助我们快速实现这一目标。在写代码时,依据特定的规则添加注释,便可以借助类似 Doxygen 这样的工具生成开发文档,非常有助于提高工作效率和进行信息交换。

文档代码化也同样值得注意,将文档以类代码的领域特定语言的方式编写,并借鉴软件开发的方式(如源码管理、部署)进行管理。二进制文档的优势和劣势都相当明显,而使用 Markdown 这类标记语言写出来的文档,虽然缺乏丰富的表现力(因为设计目标就是轻量),但对于软件开发领域绝对够用,并且可以解决二进制文档版本管理难的问题。

2.3.4、最快的成长是学习优秀的人,而不是自己领悟

没人一开始就能领悟各种设计模式和开发原则,养成良好的代码风格和习惯。在没有学习和别人斧正的情况下,也许工作一辈子还只是个代码民工。为了较好地阐明这两句话其中的含义,在这分享一次我阅读别人代码的真实经历。

有一次老板让我和一位硬件同事重新生产一款早期的终端产品,用于公司创立初期承接的项目中损坏产品的替换。需要强调的是,小公司创业初期没有完善的产品设计开发和生产的流程,资料也是混乱不堪。本以为不需要做什么事情,但很明显我低估了这项任务,因为生产出来的产品工作异常,深入调试过后才解决问题。

我打开从同事那拷贝过来的工程文件,没错,资料都是拷贝的,没有使用版本管理工具。这是一份 MDK Keil 工程文件,主控是 STM32 系列,一个几百行的主函数包含了所有应用逻辑。单从主函数就能看出相当多的问题。

  • Hal 库和寄存器混用

    整个工程应该是使用 STM32CubeMX 生成的,基于 Hal 库,但掺杂着大量的寄存器操作,并绝不是因为效率。

  • 标识符命名停留在初学者水平

    uint32_t iiuint16_t i 让人无语;大小驼峰命名和小写字母下划线命名混用;

  • 随处可见的幻数,可读性极差,容易出错

  • 毫无章法的喂看门狗,感觉像是调试时随性加上

  • 条件语句滥用,根本没考虑扩展性

  • 没有架构设计,直接处理通信协议的组包和拆包

我一个初出茅庐的职场新人都看出这么多问题,简直可以当作 Code Review 的反面教材,据说当时的开发人员也是有好几年工作经验的。

站在巨人的肩膀上才能看得更远。因此,多逛逛 Github,深入学习优秀的案例,多看书。

2.3.5、适当参加面试

面试可以为了跳槽,也可以为了增长见识,摸摸自己的底,这里我主要想谈谈后者。

在公司,可能没有人会考察你的基础知识是否扎实,但面试时一定会。工作一年多,慢慢忘了大小端和字节对齐的一些细节,也没有再写基础数据结构和算法代码。而面试就是考场,能够让自己清楚自己的不足,同时也能知道企业需要什么样的人才,有助于找到自己的职业方向甚至是事业。

三、写在最后

五年,很庆幸自己仍然热爱选定的方向。五年,自认为没有虚度如金的光阴。

新的一年,我们的故事还将继续。

你可能感兴趣的:(一个嵌入式软件工程师的五年)