对重写代码说不。
作者 | Roman Luzgin
译者 | 苏本如
责编 | 屠敏
出品 | CSDN(ID:CSDNNews)
以下为译文:
重写代码消耗了12个月!
我们从头开始重写代码浪费的时间。
你能想象在软件行业,12个月的时间没有任何新产品推出,没有任何新版本更新吗?
真的,我不由自主地问自己这个问题:
在这个快速发展的世界里,12月的时间能让我们做多少事情?
“2015年1月20日,星期二,下午5:10,AntiMalware软件终于进入了第一次公测。”
经过几十个小时的不眠不休后,第一个版本的软件说明书终于发布到了网站上,这标志着我们的新旅程的开始。
我在一家为企业和终端用户提供安全软件的小型网络安全公司工作。我们开发的软件保护用户免受恶意软件的侵害。如果用户的电脑被恶意软件感染,我们的软件会帮助他们清理。AntiMalware就是其中一个软件。
第一个测试版收到的反馈令人鼓舞。我们有四个开发人员为这个产品工作,不断地修复Bug, 改进产品功能,推出新版本。
第一个稳定版本
经过两个月的纠错、功能改进和编码工作,我们发布了AntiMalware的第一个稳定版本。
看看用户怎么说?
大多数用户的反馈都很好,他们喜欢这个产品。这让我们的团队深受鼓舞,大家卯足了劲地干活,来改进这个产品的核心功能。
进入市场
2016—2017。
大风暴来临前的黄金岁月。
AntiMalware软件处于它的最佳期,它成为了我们的旗舰产品。用户纷纷把它推荐给他们的朋友们。所有与安全相关的博客和论坛也都在推荐这个软件。它成了拯救被恶意软件感染的用户的首选软件。
下载、安装、销售,一切都向好的方向发展,用户群在几个月内迅速增长。 创始人很高兴,团队也是如此。大家都在想:“我们做到了! 像其他大公司一样,我们认为我们创造了自己的成功故事。“
新机遇(至少我们这样认为):进入企业市场
后来,公司决定进入企业市场。一个新的企业产品团队成立了。原产品负责人离开了公司,我们的CTO接任成为新的产品负责人(这是灾难的开始,稍后我会解释)。
一些开发人员离开了公司,但没有什么影响。我们把每件事情处理得很好,AntiMalware软件仍然是市场上最好的选择。
好日子结束, 麻烦开始
正如我前面所说,我们的CTO成了AntiMalware的产品负责人,他需要处理AntiMalware的方方面面。而且他还是该软件的首席开发人员,负责不间断地发布更新和功能提升。同时,他的职位让他还需要处理公司的其他事务。
当然,一开始都很顺利,我们的情况就像所有软件开发一样,我们不间断地维护和改进我们的软件。
正如我们应该预料到的(显然我们没有),不知何故,软件开发过程开始慢下来。
新的版本更新开始延期了,这种情况持续了一阵子,很快就变成没有版本更新了。这让我很不安,有一天我问CTO:
“这个产品出了什么问题?为什么版本更新要花费那么多时间而且开发进展缓慢?”
他深吸一口气,开始回答:
“我们的代码太复杂,它的结构不好,耦合太紧。架构设计完全错误,用户界面和核心逻辑代码混杂在一起,每当修复一个Bug或作某些改变时,其他部分就会受影响。即使是小的改变也很难做好。每次更新,都会引起新的问题。
一些方法竟然有20个参数,方法体的代码有两页长!你能想象吗?有许多不应该实现的东西不知为何都实现了。
这就是为什么每次更新都要花费很长时间而我们无法推出新功能的原因。每次我们推出一个新版本,我都担心可能会引入新的Bug,而那些现在工作得很好的核心功能则有可能因此无法工作。在这种情况下,发布新版本太冒险了,我们可能会失去我们的用户,我们的软件无人再愿意使用。”
他的回答中提到的一系列问题其实我们都知道。只是,我们期望从他的口中说出来。
我还问了一个问题。负责这个软件的前任首席开发人员为这个软件开发了一年时间,而他都在CTO的管理下,那么CTO为什么允许这样混乱的代码出来呢?
“我不想打击他的积极性,我们必须尽快进入反恶意软件市场,他很擅长这个,所以我才没有制止他这样做。”
CTO这样回答。
也就是说,为了以最快的速度进入市场,我们牺牲了代码质量,这样做也等于破坏了这个产品的未来。
经验教训:
要在第一时间对不好的代码设计说“不”,不要让“面条式代码”毁了你的产品的未来。要确保做出的软件产品有可持续开发性。
那么,如何修复这个可怕的代码?
“我们都是程序员,而程序员的心中都驻着个建筑师,当他们到达一个地方的时候,他们想做的第一件事就是把这个地方夷为平地,然后在上面建造一些宏伟的建筑。我们对那些渐进式的更新不感兴趣:如小修小补、改进、种种花草等等。”
- Joel Spolsky,Stackoverflow公司CEO
开发人员总是倾向于抛弃旧代码然后从头开始,他们有这样做的理由。因为他们认为旧代码都是无用而且凌乱的。但是这只是想当然的理由。当我们试图找出背后的真正原因时,我们会发现:
我们可能错了!
旧代码对我们来说可能看起来很凌乱,必须从头重写的原因并不是因为代码本身,而是因为一个重要的,基本的编程法则:
读代码比写代码难。
这解释了代码重用困难的原因,也解释了为什么我们认为旧代码象头发一样凌乱。因为这个原因,当我们阅读另一个开发人员的代码时,我们的潜意识会不断对着我们耳语“扔掉它,重新开始”。
像所有开发人员一样,我们也落入了这个陷阱。只是读一遍我们的凌乱的代码就足够让我们下决心考虑从头重写了。
在一系列的会议之后,即使CTO对重写代码有抵触(他是对的),他最终还是被说服了,我们决定从头重写代码。
然而,重写代码的决定并没有持续太久…
那是一个周末,星期日,我边喝早茶边读一些推送文章。就像我的推送知道该向我展示什么一样,我读到了那篇最著名的关于重写代码的文章,就是Joel Spolsky写的Netscape 的代码重写故事(https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/)。
读完那篇文章后,我立马分享给了AntiMalware开发团队,包括CTO。
然后我们开始了新的讨论。
本来说服CTO作出代码重写的决定就已经很难了。他在读完那篇文章后马上改变了主意,他决定中止代码重写。这让其他团队成员生气了,他们冲我大喊大叫:
“你为什么给他看那篇文章?我们都已经说服他了。这个产品必须从头重写,这是唯一的解决方案。”
我们的第一次重写代码的尝试到此结束了。关于这个话题的讨论也终止了。我们的CTO相信我们可以管理好这个糟糕的代码,并有能力在它之上发布新版本,直到严酷的现实击倒我们为止。
一年没有任何更新…
真的,这不是玩笑。真的一年没有更新了!
“为什么没有更新?“
“自上次更新到现在已经有好几个月过去了。”
每天,我们都得面对这些来自用户的负面评论。作为一家小公司,我们需要管理的产品太多了,而且,我们又进入了企业市场,这些加在一起,使得我们陷入了这样的困境。
把所有这些结合起来,你就会得出这样的结论:我们忘记了我们的用户。
回想一下,我们不想发布新的版本,因为我们不想失去用户。
但事实应该是相反的:如果我们不发布新的更新,我们肯定会失去用户,而我们已经一年半没有给他们任何新版本了。
在被现实打了一巴掌之后,我们决定回头。对我们来说,除了重写代码别无它途。我们做到了。
当前
“2018年12月17日,星期一,21:40。测试的电子邮件准备好了,即将发送给我们的内部测试组。”
经过12个月筋疲力尽的工作,代码重写终于完工。我们准备了第一个测试版本说明,就像上次这个产品面市的第一天一样。
我们又回来了…
这个产品的重写版本仍处于测试阶段。测试已经快一个月了。我们正在修复错误,倾听用户的意见,审查用户反馈……一切就像4年前一样……
但是在这12个漫长的月中,我们错过了什么呢?如果不是重写,我们会做出什么新产品?!
许多问题可以在这里提出来。但我知道我们只有重写一条路,我们看不到任何其他的解决方案。
如果你也落入了这个陷阱,开始思考“我是否应该从头开始重写代码”,那么在开始代码重写的第一步之前,就考虑自己提问下面的问题,每个开发人员都应该问问自己:
你准备好抛弃关于旧代码的所有知识了吗?
这个问题很重要!请诚实地回答:你真的准备好抛弃所有的知识,所有收集到的错误和修复,年复一年的编码结果吗?抛弃旧代码并从头开始,真的是你所期望的吗?当你从这个角度来审视代码重写的决定,你会发觉很痛苦,不是吗?所有那些试图修补bug的不眠之夜都会在你眼前闪过。相信我,因为我有切身体会。
你必须和很多用户交谈才能找到导致你的软件不能正常工作的问题所在,然后你要在你的软件中定位这个错误,重现这个问题,然后找到解决方法,然后……等等。
你能保证你会做的比第一次更好吗?
这点很重要:当你从头开始的时候,没有人能保证你会比第一次做的更好。
因为你选择抛弃关于这个软件的所有知识和已经收集的错误和修复,所以同样的错误很可能再次出现在你的新代码里。
可能代码重写团队已经不是第一个版本的开发团队。所以你实际上没有“更多的经验”。你会犯下旧版本中的大部分的的错误,并带来一些新错误,而这些新错误在旧版本中并不存在。
如果你没有很好地计划重写工作,你可能面临新版本比原始版本更糟的风险。然而,既然作出了重写的决定,你就要承担这个风险,这个风险可能导致你失去你的客户。
你准备好将几个月/几年的时间优势拱手送给你的竞争对手吗?
你知道需要多少时间来重写你的软件吗?
代码重写牵扯到大量的精力、计划和准备工作。你必须把每项任务计划好,然而一个接一个地冲刺。你必须确切地知道完成这个痛苦的过程的最后期限。没人知道你会不会错过这个最后期限。有很大的可能你不能准时完成这个过程。
你不得不在数月或数年时间内只能交付旧版本给用户,这将置你于极其危险的境地。你完全无法进行任何战略改变或对市场所需的新功能作出反应,因为你没有任何新代码可以交付。
你的客户可能会抛弃你,因为你除了不断地提供一成不变地旧版本外,无法给他们任何新的东西。
这些你都考虑到了吗?
从代码重写中我们学到了什么?
从头开始重写一个系统,本质上就是承认作为一个设计师的失败。它其实是在声明,“我们未能设计一个可维护的系统,因此必须重新从头开始。”
——摘自 Max Kanat-Alexander的 Code Simplicity
像其他设计师一样,我们承认我们未能设计好我们的软件,我们从这个精疲力尽的过程中学到了很多东西。在这里,我分享一些我们从中获得的经验教训。
代码重写是开发人员的一种错觉,大多数情况下它不是解决方案。
当你的代码遇到问题时,准确地诊断问题很重要。像每个开发人员一样,你最初的想法不应该是代码重写。代码重写只是一种错觉。因为你在阅读别人的代码的时候,你会认为如果你从头重写代码,你能做得更好。在这种情况下,请始终牢记那个重要的,基本的编程法则。
在决定重写代码前,考虑代码重构
有针对性的重写对于处理代码库中最严重的错误很有用。如果可以限制范围并解决大部分问题,就不要进行整体重写。例如,软件的加载速度非常慢。但这只影响到项目的一小部分。通过小心地移动代码、重构和更改接口,这个问题可以一次性解决。你不必重写所有代码。
代码重写是一条比预期耗时更长、更困难、更容易失败的路。
告诉大家一个开发人员通常在错过最后期限后才意识到的事实:一切都比想象的要花更长的时间。代码重写成本的估计通常很悲观,然而实际的成本几乎总是比你想象的更高,花费的时间也更长。因为总是会有想不到的复杂问题要解决,这些都会使重写过程变得更加困难和痛苦。最后,你很可能不得不接受失败的结果。
确保重写后的产品能够更好地解决用户的问题,至少相同,不能接受更差。
重写对用户没有直接的影响/好处。因为用户不关心代码,他们只想解决自己的问题,仅此而已。在用户看来,能够解决他们问题的产品就是好产品。否则,他们不会用它。用户不关心你的代码重写决定,所以重写后版本必须至少和旧版本一样有效地解决他们的问题。
保持对现有产品的维护和支持。
在我们的案例中,我们有一年的时间没有向用户提供任何软件更新。这对于我们今天生活的世界来说是太长了。尽管我们的产品依然足够优秀,但是没有更新用户肯定会抱怨。当程序员重写代码时,永远不要停止维护当前正在使用的系统。在重写过程中,旧的代码仍然需要维护,小的更新和错误修复需要及时提供给用户。否则,你将面临失去用户的风险。
让用户尽快参与设计过程
确保定期向用户展示最新进展,以便他们能够帮助你捕获最严重的错误。尽快与用户见面是很重要的。他们的反馈将帮助您根据他们的需求设计新产品。不要实现任何不必要的功能,这将避免你的代码库过于复杂化。
保持产品团队同步步调一致
一个产品团队不仅仅包括编程队伍,营销、支持、编程、设计……所有团队需要协力工作。通过定期汇报重写进展情况来确保整个团队步调一致。
在我们的案例中,我们遇到了很多这样的问题。例如,营销团队准备产品测试活动时,他们必须准确了解产品方面的情况,以便让客户为即将到来的产品改变做好准备。但是,有时我们在没有通知他们的情况下做了一些更改。这害得他们必须从头开始准备他们的测试活动。记住:不要浪费任何人的时间。
不要对产品作重大更改。
了解你的产品的弱项和强项,这一点很重要。切记不要改变产品的强项,也即用户喜爱的方面。如果用户对用户界面满意,不要对用户界面作大改动。只做最小的更改和小的用户体验改进。当您用重写后的版本替换现有版本时,确保你的用户不会被新的巨大变化所困扰。有许多情况用户放弃了新版本,因为他们找不到以前版本提供的相同的功能。不要让同样的事情发生在你身上。
不要让你的产品只依赖于一个开发者。
在我们的案例中,CTO是负责开发我们软件的首席开发人员。由于他的立场,我们的产品开发进展缓慢。即使是很小的变化也需要几个星期,有时甚至几个月。我想表达的关键点是保持一直更新,永远不要停止。
版本迁移/更换要循序渐进。
当您确认新版本已经准备好,开始用新版本替换旧版本时。要一步一步,循序渐进。
首先,从一个小型的内部测试组开始,将您的产品发送到该组。收集他们的反馈和崩溃报告,修复错误,迭代新版本,然后重复这个过程,直到你确认你的产品已经准备好公开测试。
进入公开测试后,用户的反馈是你最期待的。你的第一个目标应该是确保您的产品能够解决用户的问题。当你确认新版本提供的功能与旧版本相同或者更好时,就可以进行更换了。这时候开始为新用户发布新版本,并将现有用户迁移到新版本。
以上这些都是我从代码重写过程中吸取的关键经验教训。代码重写几乎永远都不应该是解决方案,重构才是更好的选择。强烈建议采用代码重构循序渐进解决问题。这样做的风险更低,客户也更满意。
什么时候重写代码是合适的选择
然而,有时候重写代码也是合适的解决方案。下面我我列出了重写代码的几种情形:
切换到另一种语言或平台:
当一种语言变得如此古老,导致你很难找到开发人员,或者必须花大价钱才能找到时。
现有的代码库变得不可维护(像我们的情形):
如何确认你的代码变得不可维护呢?这个很难,但是如果你发现即使是很小的更改也很难实现,或者新的更新比正常需要花费的时间多得多,或者任何新的更改都会影响到软件的其他部分并导致新的错误,那么你可以确认你的代码变得不可维护了。
有足够的资源可以同时维护现有系统和设计新系统:
重写代码的时候,永远不要停止维护当前正在使用的系统。只要系统在使用中,必须始终对其提供维护。记住,你的个人注意力也是一种必须考虑的资源,如果你打算同时为新系统和旧系统做设计工作,你要考虑是否每天有足够的时间。
开发人员变成了软件开发的瓶颈(像我们的情形):
这不应该出现在重写代码的原因列表中。因为你可以随时在团队中调配开发人员,也可以雇佣新的开发人员来解决瓶颈问题。
然而,就像我们的情形一样,有时你可能需要将它作为代码重写的一个原因。因为我们的软件使用的是旧技术,而CTO是唯一负责开发它的人。我们很难找到一个新的开发人员,因为这个平台年代太久。即使我们能找到一个新人,对我们来说也太昂贵。因此。我还是把它作为代码重写的情形之一,列在这里。
软件的年龄太长(我说的是10-20年或更长时间):
随着时间的推移,一个软件的代码会变得越来越凌乱,维护也会变得越来越昂贵。这是因为为了快速推出修复补丁,初始架构有时会被牺牲掉。而且,懂得旧技术的开发人员越来越少,人员成本也越来越高。同时,很难找到适合旧的应用程序运行的硬件、操作系统和框架。此外,随着业务的发展,旧的系统很可能无法满足新的业务需求。
所以,你必须在旧系统高昂的维护成本,新系统的潜在好处,以及从头重写的成本之间作一个权衡。
如果你的情形符合上述一点或多点,代码重写可能是你能接受的选项。否则,正确的做法是通过一系列简单的步骤改进系统的设计,在不重写代码的情况下处理解决现有系统的复杂性。
从头重写代码可能是你犯的最大错误,但同样地,不重写代码也可能导致相同的结果。我的建议是优先考虑重构而不是重写。
有些开发人员坚信所有系统最终都必须重写。记住这并非总是对的。设计一个不需要抛弃的系统是可能的。总有软件设计师会告诉你,“无论如何,总有一天我们会丢掉所有的东西”。但是,如果软件是从一开始就设计得很好,而且一直有很好的维护,为什么它会被抛弃呢?
原文:https://medium.freecodecamp.org/lessons-learned-in-my-10-years-as-a-developer-3d33c8702828
本文为 CSDN 翻译,如需转载,请注明来源出处。作者独立观点,不代表 CSDN 立场。
热 文 推 荐
☞ Python 爬虫分析豆瓣 TOP250 告诉你程序员业余该看什么书?
☞ 云评测 | OpenStack智能运维解决方案 @文末有福利!
print_r('点个好看吧!');
var_dump('点个好看吧!');
NSLog(@"点个好看吧!");
System.out.println("点个好看吧!");
console.log("点个好看吧!");
print("点个好看吧!");
printf("点个好看吧!\n");
cout << "点个好看吧!" << endl;
Console.WriteLine("点个好看吧!");
fmt.Println("点个好看吧!");
Response.Write("点个好看吧!");
alert("点个好看吧!")
echo "点个好看吧!"
点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。



第十章:FreeRTOS内存管理深度解析:从原理到heap_4的最佳实践
FreeRTOS项目工程完善指南:STM32系列
本文是FreeRTOS + STM32开发系列教程的一部分。我们将完善之前移植的FreeRTOS工程,添加串口功能并优化配置文件。
更多优质资源,请访问我的GitHub仓库:https://github.com/Despacito0o/FreeRTOS
准备工作
首先,我们需要准备以下文件:
- FreeRTOS配置文件(FreeRTOSConfig.h)
- 串口初始化文件(usart.h和usart.c)
📝 准备工作说明:这些文件是构建完整FreeRTOS工程的基础。FreeRTOSConfig.h用于配置RTOS的核心参数,而串口文件则提供了与PC通信的接口,这对于调试和监控系统运行状态至关重要。
关键文件解析
usart.h文件核心部分
这个文件定义了STM32串口通信所需的宏和函数声明。以下是文件的核心部分:
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h"
#include
/**
* 串口宏定义,不同的串口挂载的总线和IO不一样,移植时需要修改这几个宏
* 1-修改总线时钟的宏,uart1挂载到apb2总线,其他uart挂载到apb1总线
* 2-修改GPIO的宏
*/
// 串口1-USART1
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
// 其他串口配置...
void USART_Config(void);
#endif /* __USART_H */
- 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
🔍 注意:上述代码只展示了部分内容,完整的串口配置支持USART1~USART5,可以根据自己的需求选择使用。完整代码请访问我的GitHub仓库获取。
📝 串口配置详解:
- 时钟配置:USART1使用APB2总线,其他USART使用APB1总线,这决定了时钟频率和性能
- GPIO配置:定义了TX和RX引脚的具体位置,便于硬件连接
- 波特率设置:115200是常用的调试波特率,可以根据需要调整
- 中断配置:通过NVIC配置实现串口中断处理,提高通信效率
usart.c文件核心部分
此文件实现了串口初始化和重定向printf函数的功能:
#include "usart.h"
/**
* @brief 配置嵌套向量中断控制器NVIC
* @param 无
* @retval 无
*/
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
// 更多串口初始化和重定向函数...
- 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
🚀 提示:完整代码包含了USART_Config函数以及C标准库printf函数的重定向实现。这使得你可以在FreeRTOS项目中轻松使用printf进行调试输出。
📝 中断配置详解:
- 优先级分组:使用NVIC_PriorityGroup_2,将4位优先级分为2位抢占优先级和2位子优先级
- 中断优先级:抢占优先级1和子优先级1的设置确保了串口中断的及时响应
- 中断使能:通过NVIC_Init使能中断,使系统能够响应串口事件
FreeRTOSConfig.h文件核心部分
FreeRTOSConfig.h是FreeRTOS的核心配置文件,通过修改这些宏定义,可以灵活调整RTOS的行为:
/**
* @file FreeRTOSConfig.h
* @author Despacito (https://github.com/Despacito0o/FreeRTOS)
* @brief FreeRTOS配置文件
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include "stm32f10x.h" // 设备头文件
// 调度器配置
#define configUSE_PREEMPTION 1 // 启用抢占式调度器
#define configUSE_TICKLESS_IDLE 1 // 启用低功耗无滴答模式
// 时钟配置
#define configCPU_CLOCK_HZ (SystemCoreClock) // CPU 时钟频率
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) // 系统节拍频率
#define configUSE_16_BIT_TICKS 0 // 使用 32 位节拍计数器
// 更多配置项...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
⚙️ 配置说明:这个FreeRTOSConfig.h文件比之前移植时使用的配置文件更加完整和详细,添加了许多有用的配置项和注释。完整的配置文件可在GitHub仓库中获取,里面包含了60+的配置选项,覆盖了从内存管理到中断优先级的各个方面。
📝 配置详解:
- 抢占式调度:启用抢占式调度器,允许高优先级任务抢占低优先级任务
- 低功耗模式:启用Tickless模式,在系统空闲时降低功耗
- 时钟配置:使用系统时钟作为基准,确保时间精度
- 节拍配置:1000Hz的节拍频率提供了良好的任务调度粒度
工程完善步骤
1. 准备新工程
复制003动态创建工程模板并重命名为005,这将作为我们新的工程模板:
📝 步骤说明:创建新工程模板的目的是为了保持原有工程结构的同时,添加新的功能。这样可以避免破坏原有功能,同时方便进行功能扩展。
2. 替换FreeRTOSConfig.h文件
打开005工程,导航到...\Despacito\005\FreeRTOS
目录,更换我们准备好的FreeRTOSConfig.h文件:
📝 步骤说明:替换配置文件是为了使用更完整的FreeRTOS配置选项。新的配置文件提供了更多的可配置项,使系统更加灵活和可定制。
3. 创建驱动文件夹
导航到...\Desktop\Despacito\005
,新建一个Driver文件夹:
📝 步骤说明:创建Driver文件夹是为了更好地组织代码结构,将硬件驱动相关的代码集中管理。这种组织方式使代码结构更清晰,便于维护和扩展。
4. 添加串口文件
在Driver文件夹中新建一个usart文件夹,用来存放串口初始化文件,并将usart.c和usart.h这两个文件放进该目录下:
📝 步骤说明:添加串口文件是为了实现与PC的通信功能。这些文件包含了串口初始化和配置的代码,是实现调试和监控功能的基础。
5. 配置工程包含路径
打开工程,点击魔法棒->C/C++(AC6)->Include Paths->…添加如下路径,点击OK->OK:
📝 步骤说明:配置包含路径是为了让编译器能够找到新添加的头文件。这是确保代码能够正确编译的重要步骤。
6. 添加Driver组和串口文件
添加Driver组以及串口初始化文件,并整理一下点击OK:
📝 步骤说明:在工程中添加Driver组是为了在keil中更好地组织和管理驱动相关的文件。这使项目结构更清晰,便于开发和维护。
7. 修改main.c
在main.c中添加#include "usart.h"
头文件,并调用串口初始化函数USART_Config();
📝 步骤说明:修改main.c是为了初始化串口功能。通过调用USART_Config()函数,系统可以正确配置串口参数,为后续的调试输出做好准备。
8. 处理静态内存分配问题
编译后发现有错误,这是因为我们开启了静态内存分配但没有实现相关函数。有两种解决方法:
- 将
configSUPPORT_STATIC_ALLOCATION
改为0 - 自己实现相关函数
我们选择方法2,从004项目中复制静态任务创建函数:
StaticTask_t IdleTaskTCB;
StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
* ppxIdleTaskTCBBuffer = &IdleTaskTCB;
* ppxIdleTaskStackBuffer = IdleTaskStack;
* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
📝 步骤说明:处理静态内存分配是为了解决编译错误。通过实现vApplicationGetIdleTaskMemory函数,我们为FreeRTOS的空闲任务提供了静态内存分配的支持。这种方法比动态分配更可靠,适合在资源受限的嵌入式系统中使用。
9. 优化SysTick中断处理
前面我们在FreeRTOSConfig.h中注释了xPortSysTickHandler
的定义,现在我们需要规范化SysTick中断处理:
- 打开port.c,找到xPortSysTickHandler函数
- 在stm32f10x_it.c中修改SysTick_Handler函数,加入FreeRTOS调度相关代码:
#include "stm32f10x_it.h"
#include "FreeRTOS.h"
#include "task.h"
// ...
void SysTick_Handler(void)
{
#if(INCLUDE_xTaskGetSchedulerState==1)
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
#endif
{
xPortSysTickHandler();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
📝 步骤说明:优化SysTick中断处理是为了确保FreeRTOS的调度器能够正常工作。通过修改SysTick_Handler函数,我们实现了与FreeRTOS调度器的正确集成,确保任务调度的时间精度和可靠性。
10. 最终编译
完成以上步骤后,我们的工程就可以方便地使用宏配置和printf打印信息了:
📝 步骤说明:最终编译成功标志着我们的工程已经完成了所有必要的配置和修改。现在系统具备了完整的调试输出功能,可以通过串口实时监控系统运行状态。
总结
通过本文的步骤,我们成功完善了FreeRTOS工程,添加了:
- 完整的FreeRTOSConfig.h配置文件
- 串口通信功能
- printf调试输出功能
- 更规范的中断处理方式
这些改进使得我们的FreeRTOS工程更加健壮和实用,为后续开发复杂应用奠定了良好基础。
📚 获取完整代码和更多示例
完整的工程代码、配置文件以及更多示例请访问我的GitHub仓库:https://github.com/Despacito0o/FreeRTOS如果这篇文章对你有帮助,请给我的GitHub仓库点个Star!你的支持是我持续更新的动力。
评论记录:
回复评论: