对于TI的问题

“现在我要给你看肖像...嗯...我警告你这是肖像...无论如何,请像对待肖像一样对待他...

在本文中,我们将讨论制造商推荐的在CCS开发环境中CC1350 MK程序的开发和调试。 以上产品的优缺点(以及它们的优点)和缺点(以及没有它们的缺点)将受到影响。 文本中将没有屏幕截图,以显示(带圆圈)编译图标在集成编程环境中的位置或在目录中选择文件的位置。 认识到类似样式文章的基本可能性,我将尝试着重于概念性问题,以希望读者能够弄清细节。

除了分享所获得的经验之外,这次活动的目的还在于试图引起国内MK制造商的健康嫉妒,这些制造商是TI(“在我们繁荣的国家”)的直接竞争对手-这项任务坦率地说是忘恩负义,但是他们说一块石头会掉下来。

我将立即强调,它仅适用于Windows 7(此外,仅适用于7版),尽管TI网站提供了适用于Mac和Linux的选项,但我还没有尝试过它们,我很愿意相信那里的一切都不那么酷,但是为什么要考虑关于坏事(反之亦然,那里一切都很好,但是为什么羡慕)。

因此,TI网站教给我们什么-要开始使用评估模块,您需要执行三个必要步骤:

  1. 购买评估模块-完成。

    关于边距(PNP)的注意事项:您还必须这样做,因为在有问题的编程环境中,我个人(不幸地)至少在我所寻找的地方,并没有设法仿真硬件以进行调试。
  2. 安装开发环境-下载,运行安装程序,一切正常。 我们将评估模块连接到USB-木柴本身升起,一切又变好了-完成了。 当您尝试对设备进行编程时,我们会收到一条有关需要更新固件的消息,我们同意并再次证实一切。 总的来说,无处不在,没有什么可写的。
  3. 去学习适用于SimpleLink CC13x0 SDK 3.10的TI SimpleLink学院3.10.01课程-一个奇怪的建议,似乎是在教我-只是宠坏了,但是就这样,我打开了相应的链接并被惊呆了​​-这里塞满了多少东西。

在这里,我们看到了有关使用SYS / BIOS硬件驱动程序和TI-RTOS操作系统以及使用NDK网络堆栈(包括USB)以及使用无线协议的使用以及与公司生产的各种MK系列代表合作的更多方面的培训材料。 所有这些财富都伴随有现成的示例,如果我们考虑到用户手册和模块说明的存在,那么也许别无所求。 但是,还有一些实用程序可以方便地以各种方式准备和配置程序代码,刷新和调试工作,而且这些财富也得到了充分证明。

Pnp:如果有人倾向于将此材料视为与公司,其产品和编程系统相关的广告,那么它很可能是正确的,我对被检测到的软件数量印象深刻。 它的质量将被进一步讨论,我希望,我对偏见的怀疑将被消除,我并没有完全被这种感觉所蒙蔽,并且我继续完美地看到了描述对象的缺陷,所以这不是对年轻人的热爱,而是对成年专家的一种认真的感觉。 我不敢想像要为其创建和维护如此大量的软件和文档需要大量的材料成本,但这显然是在一个月内没有完成的,而且公司很可能知道它在做什么。

好吧,直到我们推迟对材料的研究以后,我们将“沿用掘金”理解所有内容,并大胆地开放CCS。 它实现了从父级Eclipse接收的工作区的概念。 就个人而言,项目概念离我更近,但是没有人困扰我们将一个项目保留在太空中,所以让我们继续前进。

但是事情变得更糟了-我们打开调试板的工作区(RP)并看到许多项目(通常有两个版本-在RTOS下和“裸铁”下)。 正如我之前所说,这不是犯罪,但是许多项目包含具有相同软件模块的相同文件的事实根本不是一件好事。 该代码被重复了很多次,支持更改成为一项非常艰巨的任务。 是的,通过这种解决方案,只需复制目录就可以更轻松地传输项目,但是对于这种情况,可以导出项目,并且可以很好地实现。 充分支持了项目树中文件的链接,因此,在提供的示例中包括文件本身的决定并不令人满意。

我们将继续进行研究-我们将开始完成的项目,但不会使LED闪烁,尽管调试板上有两个,但使用的是串行端口(现成的uartecho示例)。 我们创建了一个新的RP,其中包括我们感兴趣的项目,但是……一无所获,从消息中可以明显看出,有必要在RP中包括一个相关项目。 目前尚不清楚为什么要这样做,但是满足环境要求并不困难,然后开始组装项目。

Pnp:在家用计算机上,我使用了Import Project命令,并且所有必要的包含都是独立发生的。 我不知道在哪里指出了完全相关的项目,让我们留待以后对这方面的分析。

我们编译,刷新并开始调试。 我们发现一个有趣的现象-在考虑使用串行端口的库时,分步执行无法充分显示-优化成本。 我们关闭编译器设置中的优化(不存在那些设置,是否确实有人都知道它们,而且还要使用所有这些设置),然后再次组装项目-没有任何改变。 事实证明,至少包括链接形式的项目树中仅包含那些文件。 我们添加了到库源的链接,并且在重建所有内容后都正确调试了(只要我们启用了生成调试信息的选项)。

Pnp:但是我找到了启用MISRA-C符合性检查的选项。

Pnp:另一种方法是在后续程序集中使用“ Clean ...”命令,“ Build All”命令由于某种原因不会影响关联的项目。

然后,我们发现并不是所有的东西都总是可以正常调试的,有时我们发现自己处于机器代码领域,而编译器没有找到源代码。 由于编程环境为我们提供了工作所需的所有文件-预处理程序,汇编代码和链接卡的结果(您只需要记住启用相应的选项),因此我们转向后者。 我们发现程序代码的两个区域-从0x0000开始。 并从0x1000开始。 (32位体系结构对每个人都有好处,但是地址编写并不是他们的强项)。 我们转到微电路的文档,发现里面有一个专门映射到0x1000的ROM区域。它包含库的内置部分。 有人认为,与0x000地址空间相比,使用例程可以提高性能并减少消耗。 当我们掌握MK时,我们对最后一个参数并不感兴趣,但是调试的便利性至关重要。 您可以通过将NO_ROM选项设置为编译器来禁用ROM(但出于我们的目的),我们将这样做并重新组装项目。

Pnp:切换到ROM中的子例程看起来很有趣-命令系统中没有长时间的转换,因此转换首先通过返回低地址区域(0x0000)的中间点执行,并且已经存在PC引导命令,其参数无法被反汇编程序识别。 我无法相信的事情,好像有了这样的间接费用,您就可以赢得速度,尽管对于很长的例程来说-为什么不这样做。

顺便说一下,一个有趣的问题是,通常如何保证ROM的内容与公司提供的源代码相对应。 我可以立即提出一种在ROM中嵌入其他功能(当然是调试和服务)的机制,对于用户-MK程序员将是完全不可见的。 就我个人而言,我毫不怀疑该芯片的开发人员也知道许多其他实现这种功能的机制,但是我们将结束偏执狂的攻击。

另一方面,我只能欢迎这种类似BIOS的出现,因为从长远来看,这将使开发人员梦想在一个内核的不同MK系列之间实现代码的真正可移植性。 我们还注意到与“嵌入式”软件模块的交互实现的特殊性。 如果在早期尝试创建在TivaC模型中实现的类似机制,那么使用组号和子程序入口点的编号来访问呼叫管理器,这会导致大量开销,那么由于功能的重复命名,此处的通信分辨率处于链接器级别。插入了直接跳转到ROM中的子例程。 这执行起来快得多,但是在更改使用模型时需要重新编译项目。

现在我们已经为方便的调试做好了充分的准备,现在我们回到项目,开始通过访问模块的源代码(嗯,这就是我的想法...)来安静地调试程序,这将使我们对这些文本的质量形成看法。 正在研究的项目实现了串行通信通道的镜像,并且非常便于培训。 当然,我们使用RTOS进行了选择,我没有看到丝毫理由不在我们的配置中使用它(大量的内存和程序内存)。

马上,我们注意到源代码是用C语言表示的,通常不是很方便,许多语言结构与其优点类似,看起来很麻烦,但是创建者更关心代码兼容性而不是语法糖。 尽管可以创建C ++版本的库,但是条件编译早已为人所知,并且已在所有地方使用,但这需要额外的材料成本。 当然,公司管理层知道他们在做什么,我的评论是一种“狡猾的分析”,但在我看来,我也有权发表自己的看法。

我也知道相反的方法,当该库是使用最新的C ++工具设计的,并且当被问及那些使用不符合最新规范的编译器的开发人员怎么做时,完美的答案是是否升级到新版本该库(在这种情况下,我强烈建议您选择第二个选项)。 我个人的看法是,如果我们真的希望使用我们的产品(TI显然希望使用它,并且不基于“丢下我,这是给您的新鼓”的原则来制造该库),那么它的方法肯定是正确的。

该程序的源代码看起来很经典-初始化硬件和软件环境,创建任务并在主模块中启动sheduler,在单独的编译模块中启动任务文本。 在所考虑的示例中,任务恰好是一个-mainThread,名称中的目的也不是很清楚,而且,这也让我有些困惑-包含源文本的文件的名称与函数名称不一致(uartecho.c-尽管名称在此处使用)很好在编程环境中的搜索是以标准方式(上下文菜单或实体名称上的F3)实现的,这没有问题。

预计在启动之前设置任务参数的过程:

  1. 创建一个参数结构(当然是局部的),
  2. 给它默认值,
  3. 设置标准以外的参数,以及
  4. 创建任务时使用结构。

尽管这些操作很自然,但并不是所有的库作者都明白这一点,而且我看到了各种实现,例如其中没有阶段2,这导致了有趣的(对于外部观察者,对于程序员而言)程序行为。 在这种情况下,一切都很好,出现的唯一问题是为什么默认值不是恒定的,这可能是该死的过去的遗产。

PNP:在众所周知的FREE-RTOS中,采用了略有不同的方法,其中任务参数直接在任务创建函数的API调用的主体中指示。 这些方法的优缺点如下:

  1. +允许您不显式指定与默认值匹配的参数,+不需要记住参数的顺序,-更详细,-更大的内存开销,-您需要了解默认参数,-创建命名的中间对象
  2. -需要指定所有参数,-需要记住参数的顺序,+更紧凑,+需要更少的内存,+不需要命名的中间对象。

    这篇文章的作者提倡的第三种方法(采用TURBO的风格)有其自己的一套
  3. +允许您不显式指定与标准匹配的参数,+不需要记住参数的顺序,-多语言,-更大的内存开销,-您需要了解默认参数,+以lambda样式工作,+使得标准错误难以实现,-看起来有些奇怪,因为有许多右括号。

好吧,还有第四种选择,没有任何缺点,但是要求C ++不低于14-我们舔嘴唇然后过去。

我们开始调试,运行程序,并在编程环境提供的终端窗口中打开调试板提供的两个串行端口之一。 这两个端口中的哪个端口(一个是调试端口,第二个是用户端口,您可以在系统中看到它们的编号)很难事先告知,有时是最小的端口,有时是高级的端口,至少在重新连接电路板时不会改变,因此可以将其写在电路板上。 嗯,还有一个不便之处-打开的终端不会与项目一起保存,并且在打开调试会话时不会恢复,尽管退出时并不会关闭。 我们检查了程序的运行情况后立即发现了另一个缺点-无法配置终端,例如,它基本上以Unix风格运行,带有/ r结束符,我对这种极简主义失去了联系,尽管没有人为使用外部终端程序而烦恼。

Pnp:我们注意到调试的另一个功能,这对任何开发环境都是如此-当我们使用sheduler切换任务时,我们失去了焦点,断点将帮助我们解决此问题。

首先,请考虑创建串行端口实例的过程-这里的一切似乎都是标准的,使用了结构,该结构的字段分配了对象的必需参数。 请注意,在专业人士方面,我们有机会完全“隐藏”在C中,而完全隐藏所有初始化,但是我已经表达了支持第二种解决方案的可能论点。 有一个初始化调优结构的函数,这很好(听起来很矛盾,对于某些库的作者来说,此函数似乎不是必需的)。 在故事的这一点上,蜜月结束,普通的(夫妻)生活开始了。

仔细研究资料表明,并非一切都那么好。 有什么问题-初始化函数将常量区域中的对象的默认值复制到我们的控件结构中,这很不错,但是出于某些原因:

  1. 该对象是全局的,尽管唯一的函数使用它来初始化参数(一次类似的做法使Toyota付出了可观的费用)-很好,添加static指令很容易;
  2. 控件对象被命名,在C语言中没有完美的解决方案,或更确切地说,有一个带有匿名副本的解决方案,我在很长一段时间内都给了它,但是很多右括号不允许将该选项称为真正的美观,此外还有很棒的美观解决方案,但是梦见白日梦
  3. 对象的所有字段在位深度上显然都是多余的,即使位字段(两个可能值的枚举)也存储在32位字中;
  4. 枚举模式常量以define的形式定义,这使得无法在编译阶段进行检查,并且无法在运行时进行检查;
  5. 在可能发生故障的不同位置重复无限循环的一部分,使一个(在这种情况下为空)处理程序更为正确。
  6. 好吧,所有用于设置和启动任务的操作都可以(并且应该)隐藏在一个功能或宏中。

但是接收缓冲区的初始化做得很好-我们使用了预先保留的内存,没有对堆的操作,调用链有些复杂,但是所有内容都是可读的。

Pnp:在调试窗口中,在我们眼前,是调用堆栈,所有事情都按其应有的方式完成-尊重和尊重。 唯一令人惊讶的是,试图隐藏此窗口导致调试会话结束。

好吧,还有一个更出乎意料的决定-将样式中的串行端口和此调试板的枚举中可能的对象数量设置为1

typedef enum CC1310_LAUNCHXL_UARTName { CC1310_LAUNCHXL_UART0 = 0, CC1310_LAUNCHXL_UARTCOUNT } CC1310_LAUNCHXL_UARTName; 

这样的解决方案是实际传输的标准,但是对于硬件对象的描述却是标准的-我不知道这是可能的,尽管它对我自己有用。 我们已经完成了铁的初始化,让我们继续。

在一个正在运行的任务中,我们观察到一个经典的无限循环,其中该函数从串行端口读取数据
 UART_read(uart, &input, 1); 
并立即通过功能发回
 UART_write(uart, &input, 1); 
。 让我们进入第一个,看看尝试从接收缓冲区读取字符的尝试
 return (handle->fxnTablePtr->readPollingFxn(handle, buffer, size)) 
(我讨厌这样的事情,但是在C语言中是不可能的),我们会更深入地发现自己在UARTCC26XX_read中,并从中进入环形缓冲区的实现-一个函数
 RingBuf_get(&object->ringBuffer, &readIn) 
。 在这里,普通生活进入了一个紧急阶段。

我不想说我不喜欢这个特定的模块(ringbuf.c文件),它写得很糟糕,而且我个人可能会羞愧地踢出这个受人尊敬的这一部分作者的公司(您仍然可以代替我,但是我很害怕) (印度同事的薪水水平不适合我),但我可能不知道该怎么办。 注意你的手:

1)读/写指针的重新滚动是通过除法的其余部分实现的

 object->tail = (object->tail + 1) % object->length; 

并且由于缓冲区的长度不是常数,因此在执行此操作时,例如覆盖位掩码,编译器没有任何优化。 是的,该MK具有硬件除法运算,并且速度非常快(我已经写过),但是仍然不需要2个时钟周期,因为在正确的实现中,有了诚实的重新滚动(我也写了这个),

Pnp:最近我在实现中看到了有关新M7架构的描述,我不记得任何人,因此由于某种原因,开始以2-12个周期而不是2-7个周期执行32除以32。 这是翻译错误,还是...我什至不知道该怎么想。

2)此外,此代码段在多个位置重复-imp夫,ctrl + C和ctrl + V规则的宏和内联,DRY原理遍历整个森林,

3)实现了一个完全冗余的已填充缓冲区位置的计数器,这带来了以下缺点:

4)阅读和写作中的关键部分。 好吧,我仍然可以相信,该模块的作者没有阅读我在Habré上的帖子(尽管这种行为对于固件领域的专业人员而言是不可接受的),但是他们应该熟悉《野马手册》,在此对该问题进行了详细的研究,

5)就像蛋糕上的樱桃一样,引入了最大缓冲区大小的指示符,其名称非常含糊,没有完全描述(后者通常适用于整个模块)。 我不排除此选项可能对调试有用,但是为什么将其拖到发行版中-RAM是否有处理器周期呢?

6)同时,完全不存在缓冲区溢出处理(关于这种情况有-1返回信号)-即使在Arduino中,我们也将抛弃这种处理的质量,但是这种处理的质量会更糟。 还是作者受到众所周知的事实的启发?对于一个相对空的集合,包括不虚的事实,任何假设都是正确的吗?

总的来说,我的评论与激励者的第一行代码评论“ 10行代码-10条评论”完全一致。

顺便说一句,所指出的缺点的倒数第二个使我们考虑了更多的全局性问题-但是我们如何甚至实现基类以便能够对其进行深刻的修改。 确保所有领域的安全都是一个可疑的想法(尽管可能是唯一的正确选择),将友好功能的调用插入继承人非常像拐杖。 如果在这种特殊情况下,对于引入缓冲区充满指示符的问题有一个简单的答案-生成的类具有重叠的写入和读取以及一个额外的计数器,则可以实现读取而无需提前缓冲区(在这种情况下)或替换最后放置的字符(我看到这样环形缓冲区的实现),就无法访​​问父类的内部数据。

同时,对于从串行接口进行实际读取的实现没有任何抱怨-输入被阻塞,在接收缓冲区中没有足够数量的字符的情况下,信号量被竖起,控制权转移到了sheduler-一切都正确且正确地实现了。 就我个人而言,我真的不喜欢用通用程序控制设备,但这无论如何意味着减少了程序的嵌套并降低了圈复杂度的指标。

现在让我们注意接收到的数据到串行通道的传输,因为在创建对象时,它仅提供一个环形缓冲区-接收环形缓冲区。 确实,硬件的内部缓冲区用于传输字符,并且当填充字符时,进入等待就绪状态(至少在阻塞操作模式下)。 我无能为力,以免批评相应功能的样式:1)由于某种原因,对象具有通用指针,该指针在函数内部不断变为字符指针
 *(unsigned char *)object->writeBuf); 
2)工作的逻辑是完全不透明的,有些混乱。 但是,所有这些都不是那么重要,因为它对于用户仍然是隐藏的,并且“不会影响最大速度”。

在研究过程中,我们遇到了另一个功能-在调试模式下看不到某些内部函数的源代码-这是由于更改了不同的编译选项(ROM / NO_ROM)而引起的。 替换所需的源文件(C:\ Jenkins \ jobs \ FWGroup-DriverLib \ workspace \ modules \ output \ cc13xx_cha_2_0_ext \ driverlib \ bin \ ccs /./../../../ driverlib / uart.c--)我失败了(但是我并没有真正尝试过),尽管我找到了源代码(当然,在uart.c文件中的文件,谢谢,队长),幸运的是,这个片段很简单,而且很容易用C中的源代码来识别汇编代码(特别是如果您了解ITxxx团队的功能)。 我不知道如何为功能复杂的库解决此问题,我们会在需要时考虑。

最后,我要说一点-我愿意相信MK CC13x0模型的串行通道实现的硬件与CC26x0的相同,并且不能复制正确的名称为UARTCC26XX.c的文件---而是正确的解决方案,而是创建一个包含我将欢迎源文件,其中覆盖了功能和相应的注释,因为这会使程序更易于理解,vout应该始终受到欢迎。

因此,在测试用例可行的情况下,我们了解了很多标准库的内部结构,并指出了它们的优点而不是很好的一面。在总结之后,我们将尝试找到程序员通常在两难选择中的问题``OS or not OS''(上下文切换时间)。 这里有两种方法:1)考虑源代码是一种理论方法,它需要一定程度的我不准备演示的主题,以及2)实际实验。 当然,与第一种方法不同的是,第二种方法并不能给出绝对正确的结果,但是“真理永远是具体的”,如果正确组织测量结果,则可以认为所获得的数据是足够的。

首先,为了估计切换时间,我们需要学习如何评估各种程序片段的整体执行时间。 在此体系结构中,有一个调试模块,其中一部分是系统计数器。 关于此模块的信息很容易访问,但细节仍然隐藏着魔鬼。 首先,让我们尝试通过访问寄存器直接使用句柄配置必要的模式。 我们快速找到了CPU_DWT寄存器块,并在其中找到了CYCCNT计数器本身和使用CYCCNTENA位的CTRL控制寄存器。 当然,或者,正如他们所说的,什么也没有发生,ARM网站对为什么这个问题有一个答案-必须使用DEMCR寄存器中的TRCENA位来启用调试模块。 但是最后一个寄存器不是那么简单-在DWT块中不存在,在其他块中它懒惰地搜索-它们很长,但是我没有在寄存器窗口中找到任何按名称的搜索(但是拥有它会很好)。 我们进入内存窗口,输入寄存器的地址(从日期起我们就知道了)(顺便说一句,由于某种原因,地址的十六进制格式不是默认值,您需要用钢笔添加0x前缀),然后突然看到一个命名为CPU_CSC_DEMCR的命名存储单元。 至少可以这样说,这很有趣,为什么该公司与该架构的许可人提议的名称进行重命名,这可能是必要的。 恰好,在寄存器CPU_CSC的块中,我们找到了我们的寄存器,在其中设置了所需的位,返回到计数器,启用了它,然后一切正常。

Pnp:仍然有一个按名称搜索,它被Ctrl-F组合调用(自然地),它仅存在于上下文菜单中,但是在通常的情况下,它被取消了,我向开发人员表示歉意。

我立即注意到内存窗口的另一个缺点-通过指示命名的单元格来中断内容的打印,这会使输出撕裂,而无花果被分割成16个字(8.32,64,替换必要的字)。 此外,调整窗口大小时,输出格式也会更改。 也许可以根据用户需要配置所有这些功能,但是基于我自己的经验(以及我应该从其他内容中学到),我声明设置内存查看窗口的输出格式不适用于直观上明显的解决方案。 我完全赞成启用这样的便利功能,例如在查看窗口中显示已命名的内存区域,否则许多用户将永远不会知道它,但是对于那些有意识地想要禁用它的用户也必须小心。

顺便说一句,我不会完全放弃创建用于环境的宏(或脚本)的可能性,因为我必须在重置MK之后每次都进行此寄存器设置(以启用时间测量),因为我考虑通过出于调试目的插入寄存器操作来进行代码更正。不是很正确。 但是,尽管我从未发现宏,但是由于可以在表达式窗口中包含单个(必要的)寄存器,因此可以大大简化使用寄存器的工作,从而极大地促进了它们的使用并加快了使用它们的速度。

为了强调工程师对MK系列的感觉尚未冷却(否则我会责骂开发环境的不同方面),我注意到计数器工作正常-在任何调试模式下我都找不到任何额外的循环,但是在此之前至少在LuminaryMicro开发的MK系列中。

因此,我们概述了用于确定上下文切换时间的实验计划-创建第二个任务,该任务将增加某个内部计数器(无限循环),启动MC一定时间,找到系统计数器和任务计数器之间的关系。 接下来,以相似的时间(不一定完全相同)启动MK,并以大约每秒一次的速度输入10个字符。 可以预期,这将导致10切换到回显任务和10切换回计数器任务。 是的,这些上下文切换将不会根据sheduler计时器执行,而是根据事件执行,但这不会影响所调查功能的总执行时间,因此我们开始实施计划,创建计数器任务并启动它。

在这里,我们发现了RTOS的一项功能,至少是在标准配置中-它并没有“真正”排挤:如果优先任务始终准备就绪,可以执行(并且相反的任务是这样),并且没有将控制权交给sheduler(不希望有信号,也不能入睡,而不是被标志等阻止),则根本不会从该字执行较低优先级的单个任务。 这不是Linux,在Linux中使用了各种方法来保证每个人都有一个量,“这样就不会有人冒犯”。 这种行为是可以预期的,许多轻量级RTOS都具有这种行为,但是问题更加严重,因为管理人员在不断准备的情况下没有收到同等优先级的任务。 这就是为什么在此示例中,我将回显任务置于保持状态的原因,该任务的优先级高于始终准备就绪的计数器任务,否则后者将及时捕获所有处理器资源。

我们开始实验,第一部分(仅等待执行时间)给出了关于计数器比率406181/58015= 7的数据-这是非常可预期的。 第二部分(带有10个连续字符,持续10秒)给出结果351234k-50167k * 7 = 63k / 20 = 3160个周期,最后一位是与上下文切换过程相关的时间(以MK周期为单位)。 就我个人而言,这个价值似乎比预期的要大,我们正在继续研究,似乎还有一些行动破坏了统计数据。

PNP:实验人员的一个常见错误是不评估以前的预期结果,不要相信收到的垃圾(对737开发人员而言)。

很明显(“是的,很明显”),结果除了实际的上下文切换之外,还包含执行从缓冲区读取字符并将其输出到串行端口的操作所需的时间。 不太明显的是,它也有时间处理接收到的中断并将字符放置在接收缓冲区中。 我们如何将猫和肉分开-为此,我们有一个棘手的技巧-我们停止程序,输入10个字符并启动它。 我们可以预期(我们应该看一下源代码)接收中断只会发生1次,并且所有字符都会立即从接收缓冲区发送到第一环,这意味着我们将看到更少的开销。 确定到串行端口的交付时间也很容易-我们将每秒输出一个字符,并用2个未知数求解得到的2个线性方程。 而且有可能甚至更简单-不推论我所做的任何事情。

这是这些棘手的操作的结果:我们使数据包的输入和丢失的滴答声变得更小-2282,关闭输出并且成本下降到1222滴答声-更好,尽管我希望300滴答声。

但是随着阅读的时间,没有什么像这样的事情了;它在与所需的上下文切换时间同时缩放。 我唯一能提供的就是在输入接收到的字符开始时关闭内部计时器,然后在等待下一个字符之前再次打开内部计时器。 然后,两个计数器将同步工作(切换除外),并且可以轻松确定。 但是,这种方法需要深入执行本文中的系统程序,并且仍然保留中断处理的组件。 因此,我建议将自己限制在已经获得的数据上,这可以使我们坚定地断言,所考虑的TI-RTOS中的任务切换时间不超过1222个时钟周期,对于给定的时钟频率,该时间为30μs。

PNP:仍然很多-我计算了保存上下文的周期为100:30,确定完成任务的周期为40,恢复上下文的周期为30,但是我们得到的数量级更多。 尽管现在已经关闭了优化,但是打开–o2并查看结果:它并没有太大变化-它变为2894,而不是3160。

还有一个想法-如果操作系统支持切换对等任务,那么您可以运行带有计数器的两个任务,在一段时间内神奇地获取有关交换机数量的数据并计算系统计数器的损失,但是由于sheduler的特殊性,我已经说过,这种方法不会导致成功。 尽管另一种选择是可行的-通过信号量在两个对等(甚至对等)任务之间进行ping-pong,但在此处计算上下文切换的数量很容易-您将不得不尝试,但这将是明天。

这次帖子末尾的传统调查将不着眼于演讲的水平(任何不偏不倚的读者都知道他超出了任何赞美,超出了任何期望),而是讨论了下一篇帖子的主题。

Source: https://habr.com/ru/post/zh-CN451918/


All Articles