8位处理器可以有效地处理这些功能

描述

  至少有两个因素表明转向功能更强大的处理器并不一定意味着需要占用更多比特。随着应用程序的成熟,功能强大,功能特定的处理器或引擎可用于承担大部分繁重的工作。例如,使用MP3播放器,您可以购买完整的数字音频解码引擎。您只需要一个用户

  

  接口和PC的I/O接口,可以充实您的数字播放器。 8位处理器可以有效地处理这些功能。而且,对于需要更多功能的任务,您可能会惊讶于那些小型8位处理器已经成为40-MHz时钟,可扩展程序存储器,向量中断表,浮点库,C编译器的强大工具,和外围设备的范围。您可能会发现选择使用较少位的处理器可以更好地帮助您满足成本和性能限制,同时节省空间。

  我花了10多年的时间在工业控制应用程序中使用16位和32位x86组装。对于这个动手实践的项目,我想看看我所有的16/32位偏差会让我们后退并使用8位处理器进行设计。毕竟,因为处理器不那么复杂,设计过程应该更简单,不应该吗?

  一个人的愚蠢错误往往会产生最好的学习材料。因此,一些设计经验可能有点令人尴尬。幸运的是,总是存在这样的安慰:在完成这个项目之后,我不再是那些犯了这些错误的程序员。如果你正在考虑转向8位处理器,那么阅读这个项目可能会让你免于我所遇到的一些痛苦和痛苦。

  项目

  对于我的项目,我决定制作一个音序器。我的一些工程师和我最近发现了“Cool-Neon”,也称为电致发光线(参考文献1)。您可以使用逆变器将9V电源转换为300V交流电,从而驱动电线以创建多色光雕塑。我们首先建造了一条二维鱼,我们可以骑在自行车上,创造一个游泳鱼群的幻觉。对于我们的下一代项目,我们需要一个可以循环通过光线模式的音序器,为移动的雕塑或电子万花筒创造一种运动的幻觉,其中不同时间点亮的不同光束代表短帧和循环动画中的帧序列

  8位处理器似乎非常适合这个项目。我需要一个输出引脚来驱动每根导线;对于10股容量,我需要10个引脚。一个按钮(使用另一个引脚)将允许某人循环通过模式选项。在功能列表中,我快速添加了传感器的输入。例如,放置在自行车车轮周围的传感器可以基于外部事件而不是内部计时器来触发模式。决定使用Microchip的PIC系列产品,我发现18引脚封装处理器有13个I/O引脚,所以我有一个引脚备用,但不会长时间保留备用。

  资源有限:记忆

  您通常可以通过在更强大的处理器上投入资源(如内存,处理和I/O)来解决问题。然而,由于配置的多样性,8位世界中的家庭成员之间的差异可以直接节省成本。例如,具有2千字节板载内存的处理器成本低于4千字节的处理器。我记得有64千克的银行可用;你可以通过使用一个大表来加速一个关键的计算 - 一个可行的,有时是至关重要的选项来燃烧记忆。

  早期,我做了什么才能成为我的音序器的关键设计决策。我没有用1位(10个灯等于10位)表示活动灯,而是创建了一个10字节长的表,其中包含适当逻辑光的物理端口代码;也就是说,I/O端口A上的指示灯1 =位3)。我制作这张桌子是因为,当我测试是否填充了灯光时,我必须计算物理端口。当时,存储这个预先计算的值似乎更容易。鉴于我的心态,这个选择是明智的:在开始时(主处理循环之外)执行一次计算,并在每次稍后使用计算时保存那么多循环。

  不幸的是,这个选择很糟糕。首先,定序器将大部分时间花在轮询循环中,等待按键是否被按下或是否已触发定时器。因此,在轮询循环中,我节省了几个周期(最多10个灯,10个指令= 100个周期)。其次,更重要的是,我使用了比我需要多8个字节的RAM。我习惯丢掉数百字节的填充缓冲区来覆盖可能出现的最坏情况。但是,当8个字节几乎占总数据存储器的八分之一时,这是一个糟糕的决定。

  在我用完数据存储器之前,我没有意识到这种选择的重要性。我添加了一个变量,程序无法运行,因为另一个变量被推入未定义的0xFF区域。我的项目似乎没有足够的复杂性来使用这么多的内存,但是当我添加更多功能时,我忘了留意内存。当我努力恢复字节时,击中数据存储器的顶部使我的开发暂时停止。然后我犯了一个愚蠢的错误,即偶然试图加倍临时变量。例如,某些函数需要循环计数器。但是,您可以使用这些循环计数器来计算除这些函数之外的其他内容。为了完成这项工作,我需要严格控制变量的范围,以确保一个函数不使用相同的变量调用另一个函数。

  我的第一个直觉指示我只给一个双重任务变量一个名字。我可以想象调试的噩梦是没有记住两个不同的变量实际上是相同的变量并相互改变。至少如果他们有相同的名字,如果我不小心违反变量的范围,我就有更好的机会抓住自己。

  当我尝试按范围对变量进行分组时,我了解到在整个程序中使用变量之前我可以更轻松地执行此任务。例如,变量Temp具有直接范围,这意味着如果函数调用另一个函数,则第一个函数应该假定Temp被销毁。另一个极端是系统或全局变量,例如Flags,所有函数都可以使用。然后是灰色区域。例如,循环计算当前正在处理灯光模式中的哪个步骤。因为一次只能执行一个模式,所以每个模式函数都可以使用Cycle。此外,当用户编辑用户可编程模式时,没有预定义模式正在运行,因此您也可以在此处使用Cycle。但是,当我想在编辑时显示用户可编程模式时,我确实存在潜在的范围违规。我必须在显示之前存储Cycle并在编辑后恢复它,或者我可以让用户通过在他或她想要编辑的周期停止显示来重置Cycle,这是我实现的选择。

  在这种情况下存储Cycle会需要一个全局变量,因为显示一个模式使用了主程序循环,但是为此目的专用变量会使变量首先加倍。我也可以选择实现数据堆栈。在16位和32位的世界中,堆栈是一个方便的朋友。但是,我使用的微处理器只有一个内部堆栈用于程序地址。 (我确信有一些方法可以欺骗处理器来存储数据,但只有8个级别,如果我想保存多于一个或两个值,我会冒着堆栈的风险。)堆栈至少需要一个额外的字节用于尾部管理,你必须缓冲他们最坏情况下使用。我避免实现堆栈,因为我认为我的值太少而无法保存。

  变量的范围对于通过加倍使用变量来跟踪和创建变量的依赖性至关重要。这项任务应该是一个谨慎的决定。我试图将变量划分为功能块,但是,即使仔细规划,似乎总是可以同时使用两个块,因此也就是所有变量。然后我会发现自己正在追逐自己造成的错误。

  一旦您决定将处理器放在电路板上,您就有可能显着降低处理器的成本。当你发展时,给自己一些重新编程的空间是有意义的;但是,您可能会发现自己只需几个字节就可以将设计融入更便宜的处理器中。较便宜的选项并不总是等价的,并且会影响开发,因为它们定义了内存和性能阈值。通过在开始开发时而不是在完成开发时了解这些阈值,您可以确定限制可用资源的紧密程度。无论如何,你可能永远不会雇用一个字节作为一个角色。

  资源有限:处理

  由于“功能蠕变”-extra功能使设计复杂化并抛弃计划 - 继续为顺控程序添加选项和增强功能,我意识到我可能也会在程序内存中占据优势。在16位和32位的世界中,我经常为一个问题投入内存以减少总计算时间。我发现自己正在使用我的音序器进行反向权衡。

  当我设计用户可编程模式(存储在EEPROM中)时,我使用位来表示每个光。但是,10位是1字节,剩下2位,您可以打包成四个组以完全使用另一个字节。这个决定产生了一个问题,因为模式将以与我用来打开灯光的格式不同的格式存储。 (EEPROM格式使用逻辑格式来跟踪被点亮的灯:点亮零到九。灯格式使用物理格式:每个端口一个字节,A和B,每个位代表相应的I/O引脚,由于板布局考虑而无序。)存在两种格式,因为物理格式是用于驱动输出端口的实际掩码。但是,物理格式使用16位而不是像逻辑格式那样仅使用10位。因此,我需要一个转换函数。

  我通常会同时写两个转换函数的方向。为了最大限度地减少总处理周期,我将EEPROM逻辑格式转换为光/物理格式。当用户编辑模式的这个循环时,我将制作并显示光/物理格式的变化。当用户完成编辑此循环后,我会将光/物理格式转换回EEPROM/逻辑格式并保存循环。

  我决定转而支持内存而不是处理性能。通过直接更改EEPROM/逻辑格式,我可以调用转换函数来创建光/物理格式并显示修改后的周期。每次运行此转换需要更多的处理周期,但同样,这些周期仅来自轮询循环。我获得了废除逆转换函数的选项,因为它从未被调用过。因此,我节省了程序内存和一些开发时间。我还获得了将所有更改立即保存在EEPROM中的好处,使我无需在用户移动到新周期时管理保存更改,想要显示模式,左编辑模式等等。

  计时器

  在我的问题上投掷处理能力而不是记忆力几乎适用于我。 PIC16F84有一个内部定时器TMR0,粒度为256个周期,预分频器大小为2 8 (256×256 = 65,536个周期)。在没有预分频器的情况下运行,每256个周期发生一次TMR0中断。因为我将每个模式的循环连接到TMR0,所以每个循环必须在256个循环内完成执行,或者在完成处理最后一个循环之前我会遇到TMR0事件,偶尔在模式期间丢失一个循环。增加预分频器可以选择将TMR0间隙增加2倍,以防止超出。通过尽可能多地处理中断以避免中断处理期间的中断延迟,保持中断处理程序的清洁和简单也很重要。

  我使用电位计让用户调整音序器的速度。鉴于你需要在模式的周期之间进行一点处理,我选择使用RC时钟而不是晶体。这个决定节省了晶体成本,系统复杂性和I/O引脚;电位计驱动RC时钟,而不是告诉PIC应该运行多快,并在内部管理这个速度。如果我想让两个序列发生器相互通信,我将不得不创建一个连接两个节点的协议,这些节点具有独立且可能变化的时钟速度。

  我想要几种速度选项 - 从几分之一到几秒 - 用于循环模式。电位器给了我一个范围,所以我创建了两个用户可选择的速度区域,为TMR0使用了不同的预分频器。我的设计最初支持三个区域,但有两个简化的用户选择,将速度参数切换为1位,并简化了TMR0事件的内部管理。

  预分频器3(2 4 = 16)和4(2 5 <之间的差异/sup> = 32)可以变宽,特别是RC时钟被调到5和50 kHz。当我的设计支持三种模式时,我发现添加(TMR0_COUNT预分频器让我可以更好地控制中断。如果你用一个10-MHz时钟运行PIC,你可能会发现这个额外的预分频器让你测量有用的时间长度。

  调试

  鉴于要重复这个项目,我会获得一个用于软件开发的模拟器。尝试解决困难编程所节省的时间很快就会弥补费用。相反,我非常依赖MPLAB模拟器。

  模拟器的运行速度比我预期的要慢得多。有时,我认为系统已经冻结,因为它努力到达我的断点。有时候,我引起了一个延迟,例如当我在轮询循环后将断点放在代码中的某个点时,模拟器卡在轮询循环中等待我响应。我的设计有一个短于256个时钟的主循环,所以我可以快速测试迭代和情况。以10 MHz运行的设计可能有更长的主循环或更多的轮询。

  我发现使用Define创建模拟器标签很有用。在真正的董事会上,我想要真正的延迟。但是,通常没有理由在模拟器中的轮询或延迟循环中永远等待。例如,我有一个Flash功能,可以闪烁特定的线W次。在模拟器中,我没有必要“看到”这种闪烁。因此,我添加了以下代码以跳过无用的函数和速度模拟:

  TMR0也触发了事件。我将所有延迟量命名为常量,以便我可以将所有模拟器常量设置为零。我还添加了一些代码来模拟按钮按下或其他事件。有时,等待TMR0中断变得令人难以忍受。在我的轮询循环中,然后,我添加了仅模拟器代码,它将设置我的TMR0事件标志(然后将TMR0重置为零,以便最终不会触发我)。

  模拟器代码的另一个有用位置是中断处理程序的开头。我的中断处理程序足够短,可以将它留在代码的前面,而不是调用放在内存中其他地方的函数,这会增加中断的延迟。但是,当我模拟输入时,会调用一个中断,我不得不单步执行所有中断代码。我添加了代码:

   此代码创建了一个调用,我可以用键击来代替。

  我通常发现MPLAB环境包含很多很好的功能,但是我很痛苦地追逐一个接口问题。例如,我想测试需要按两次按钮才能到达的代码。 MPLAB有一个“激励模拟器”窗口,通过它我可以激活输入端口。不幸的是,当刺激窗口打开时,模拟器的运行速度明显变慢,所以我通常不得不关闭窗口并在几秒钟后重新打开它。选择刺激时钟似乎比它值得更麻烦,因为我必须弄清楚我希望输入触发的时钟周期。通过查看合适的输出引脚,我也可以看到灯是否亮起。但是,对于任何复杂的输出序列,验证准确的反馈可能很困难。

  与MPLAB环境一样有用,我建议您访问一个快速而肮脏的开发环境,例如Basic,来测试算法。例如,微芯片应用笔记库中的随机数生成器功能似乎在0到255的值中具有不均匀分布的数字。由于存储器限制,在Microchip环境中测试此功能似乎很困难。例如,如果我有256字节的RAM,我可以很容易地计算很长一段时间内的分布。然而,如果不到40H字节,我将不得不想出一个聪明的计数方案,并且可能会多次运行我的模拟以确定传播是均匀的。在Basic中,我可以在几分钟内弹出这段代码并几乎立即看到结果。

  我遇到了其他令人沮丧的错误;如果我使用了模拟器,我会节省数小时的调试时间。

  因为我使用了可重新编程的设备,所以我不得不将设备插入板中以及从板中取出设备以将其放入设备编程器中。我在电路板上有一个插座,但是将设备插入插座意味着我必须将插座插入插座中,从而产生松动且因此不良的电气配合。我最终在一台设备上打破了领先优势,使其无用。我还需要偶尔向外弯曲引线,因为向内弯曲的引脚连接不良,I/O引脚会神秘地停止工作。使用模拟器可以消除我的大部分套接字问题。

  我安装了一个新的逆变器,可以驱动比我当前的逆变器更多的电线,从而使我能够同时点亮几根电线。虽然设计是在程序模式下,但我会闪现一条线来代表一个参数。参数One工作正常,但参数二不会闪烁。因为我专注于用户可编程模式并禁用了其他参数,所以我认为我的一些更改可能会改变代码的闪存能力 - 通过使用变量。经过大约一个小时的追逐幻象代码错误后,我终于将问题缩小到了实际的电线,除非我点亮另一根电线,否则我无法关闭。突然间,我意识到新的逆变器可能是问题所在。当我连接旧逆变器时,电线正确闪烁。

  出现问题是因为逆变器倾向于保持电线点亮;一些导线 - 但不是其他导线 - 超过驱动导线的三端双向可控硅开关中的相位间隙。我通过代码搜索,因为一根电线工作,而另一根没电。我怀疑是一个逻辑问题,并且忽视了导线出错的可能性,因为它之前一直有效。使用模拟器,我会看到端口已经变低,我正面临硬件,而不是逻辑问题。我还了解到,当我使用新设备进行测试时,我应该检查一下,在更改硬件之前是否存在先前设置中存在的错误。

  值得一提的是,我保持了“健全性” - 使用旧版本代码刻录的几个芯片。每当原型开始变得怪异时,我就会放入其中一个完整的烧伤。如果原型工作,那么我的最新版本的代码已经破坏了一些关键的东西。如果健全性刻录无法正常工作,则硬件会出现诸如断线之类的问题。

  电源问题引发了一系列令人讨厌的错误。我最初使用壁式电源为我的原型供电。但是,我得到了一些奇怪的闪烁,我很快就将其评估为电源问题。由于电路板应使用9V电池供电,因此我切换到电池供电。然而,点亮的电线消耗大量电力。我还做了一个不幸的假设,即当电池电量太低时我的电线就会死掉。 PIC实际上首先变得缺电,然后行动不稳定。我在追逐想象中的错误一天之后才发现了这个事实。使用新电池可以处理所有事情。我希望在尝试查找错误并注释掉部分,设置不必要的调试挂钩等之前保存了特殊版本的代码。

  为了避免电池问题,我回到墙上供应。然后我发现闪烁不是由电源引起的,正如我所假设的那样。 (墙上插座给了我足够的力量来看到我的弱电池上的闪烁。)相反,这是我在设置下一个周期之前短暂关闭灯并显示它们的问题。我将代码更改为只是从一个循环移动到另一个循环而不显示其间的所有灯。奇怪的闪烁消失了。

  我创建的另一个原型错误涉及使用重置按钮。我在面包板原型上添加了一个复位按钮,这样我就可以轻松地重置电路板而无需进行物理断开,然后重新连接9V电池。模拟器可以提供相同的功能。但是,最终产品没有重置按钮。使用原型,当我想离开程序模式时,我会按下重置按钮重新启动电路板。然而,这种复位不是全功率复位,这意味着标志和存储器没有被清除;在这种情况下唯一重大的变化是程序计数器重置为0x00。当我最终转移到真正的用户所拥有的制造原型时,我通过关闭然后再打开电路板离开编程模式,但是电路板没有注册我的更改。出现此问题的原因是断电复位也会清除内存。

  我列举了这些例子,因为它们说明了使用原型的一些危险。一方面,你需要一个干净的环境来开发,所以你不会追逐幻影。您还希望访问所有硬件,并且应该能够随意进行探测。另一方面,您需要使用与客户相同的硬件来查看实际发生的情况。

  时滞

  当我炸掉我唯一的控制器时,我遇到了开发周期中最大的延迟之一。我首先访问Microchip的网站以获得更多芯片。最快的送货服务是三天,这意味着我只能使用模拟器进行开发。我找到了多个销售PIC芯片的互联网网站,并且可选择更快的交付。虽然其他站点提供了有限的PIC选择,但我确实找到了PIC16F84,这是很好的开发,然后计划换成更便宜的芯片。鉴于PIC微处理器的普遍可用性,我购买了一些零件而无需与经销商建立关系。我还学到了库存的第一条规则:有一些。当出现问题时,您的时间和产品的上市时间应该得到一些额外部件的保险。

  延迟的一个更具破坏性的因素是特征蠕变。在我完成基本功能之前,我已经决定了一系列新功能,例如用户可编程模式,这些功能已经进入规范。我为需要非标准模式的专家用户构思了用户可编程模式。这些模式还需要与项目其余部分一样多的编码开发时间。突然之间,由于我努力解决专家代码中的错误,上市时间受到了冲击。具有讽刺意味的是,beta测试人员对用户可编程模式不感兴趣。换句话说,如果我关闭了规范并将专家功能推迟到第二版(参见附文“经典混音和错误”),我本可以更快地将测试板交付给beta测试人员并测试音序器的基本稳健性。

  杀手NOP

  每当我得到一个新工具时,我总会感到兴奋。安装后,我要做的最后一件事是特别注意说明。我已经阅读了足够的内容以便开始,然后向我保证我会返回并稍后完成。

  早期,当我尝试实现演示模式时,我发现了一个奇怪的问题:电路板只能点亮六盏灯。当我删除代码时,电路板工作正常。此时,我开始重新编写代码片段,每次测试电路板时都会进行测试。最后,除了一段代码之外的所有代码都重新进入。当我注释掉代码时,电路板运行了。奇怪的是,只有按下按钮才会执行此代码。不知何故,我想,事件是错误地触发的。当我删除除了Goto和Return之外的所有代码时,代码工作正常。添加CLRF标志会导致失败。但是怎么样?然后,我决定添加非操作(NOP)指令而不是CLRF标志。随着NOP,董事会失败了。如果没有NOP,代码就会运行。

  当NOP指令导致严重错误时,通常意味着存在内存误解。果然,我的一张桌子越过100H边界,因为我慢慢地在它前面添加了程序代码。在这种情况下,出现问题是因为表地址变为9位长; 14位PIC指令可以容纳Goto地址的额外位,但它们会丢弃算术指令的第9位和第10位,例如Add。因为我测试的代码长5个字节,所以表超过了4个字节。 (因此,在光7之前只有六个灯显示PIC设置为00H(复位)而不是100H。)

  我通过将所有表移动到代码部分的前面然后仔细阅读指令集来解决问题,以查看是否有任何更多的命令具有汇编程序无法捕获的偷偷摸摸的细微差别。后来,当我阅读John Peatman的使用PIC微控制器进行设计时,我发现将表放在存储器前面是PIC的标准做法,因为这个原因(参考文献) 2)。我快速阅读,看看是否还有其他标准做法我应该遵循。在中断区域,这本书给我带来了很大的悲伤。例如,在16位和32位的世界中,标志会自动保存,并且有一个堆栈来保存寄存器。使用PIC系列,您必须自己保存标志,但首先必须以一种棘手的方式保存累加器(使用Swap)而不影响标志(清单1)。我发现disable-interrupt命令特别具有欺骗性。由于PIC具有两级流水线,因此如果中断因此与请求禁用中断同时发生,则会出现问题。 PIC根据您的指令清除中断标志,然后执行挂起中断。当您从中断返回,从而启用中断标志时,即使您认为刚刚禁用了它们,也会在主代码中启用中断(清单2)。

  最后,我发现PIC规格表虽然技术上已经完整,但需要一些澄清。至少买一本像皮特曼这样的书。他建议一个强大的编程结构,可以帮助您避免许多问题。然而,他的问题部分让我烦恼,因为你必须提前阅读一两章才能最终发现答案。在任何情况下,在您发现之前阅读本书,与大多数错误一样,您无法理解架构的细微差别。

  你可能会问35条指令有多复杂。不幸的是,减少的指令集实际上导致更复杂的程序。对于一个简单的类比,想象一下,我试着写这篇文章而不使用字母T.较少的单词构成一个不太复杂的词汇,但可用的单词较少意味着说某些事情需要更长的时间。例如,高阶语言具有复杂的词汇表,可以让您在一条指令中描述复杂的汇编任务,例如打印或乘法。使用精简的指令集,例如PIC系列的指令集,即使是简单的函数,例如位移,也会变得更加复杂。例如,x86的程序集提供了诸如旋转而不进位和旋转CL次等命令(清单3)。当我需要将比特封装的位移实现到这个项目时,我不得不考虑这个过程。没有旋转 - 不进位指令,所以在右旋转的情况下,我必须在每次旋转之前将进位标志设置到最右边的位置。

  创建比特移位等功能并不困难;但是,你仍然需要创建它们。如果您从未在此级别工作 - 例如,如果您始终有可用的打印指令 - 创建基本功能库可能需要一段时间。您还会发现,第一次尝试时,您可能无法确定实现功能的最有效或最灵活的方式。因此,我的项目花费的时间比我想象的要长,因为我编写了“基本”函数,我习惯于在不太复杂的16位和32位架构中实现这些函数。

  我还发现,尽早构建我的基本功能库可以提高我布置音序器架构的能力。例如,一旦我编写了用于读写EEPROM的基本功能,我开始将EEPROM视为一种资源,而不仅仅是我后来需要编写代码的东西。这种观点的改变促使我考虑创建在断电后保持设置的静态用户参数的想法。 EEPROM还打开了一组可用的数据存储器,访问速度要慢得多,但可以想象我会忘记内存堵塞。

  硬件/软件断开连接

  在我编程16位和32位架构的过程中,我一直使用完整的硬件系统;也就是说,我在PC或已经设计,打印,填充和测试的电路板上工作。在这些条件下,我的首要任务之一是确定新功能的可行性。通常,您可以在软件中实现任何功能,因为该功能满足处理速度,内存和接口的限制。如果不是这三个限制,8位μP可以执行视频会议,虽然速度很慢。一旦我确定我有足够的处理能力,并且我可以在必要时实时执行任务,足够的内存和正确的接口,我会编写该函数。

  然而,在这个项目中,我是构建电路板的设计团队的一员。虽然我的硬件技能有限,但我提供了一个在代码中可以合理实现的视角。也就是说,包含PIC无法有效处理的硬件接口将是荒谬的。该团队开发的一项功能是自动检测电路,确定端口是否实际连接到电线。如果没有这样的电路,PIC就无法知道用户想要点亮和循环多少根导线,除非他每次给电路板加电时输入该值。相比之下,由于带有电位计的RC电路为PIC提供时钟,因此PIC永远不会知道它的运行速度。 (PIC的设计人员认为此功能不是必需或有用的。)

  请注意,在我们支付第一批电路板之前,我们没有锁定硬件规格。考虑到产品上市时间的压力,即使我们仍然需要编写代码,我们也会将电路板放到屏幕上进行筛选。我们完成了足够的代码来满足团队需求,硬件按照我们的预期运行,但是我们还没有把所有的花里胡哨都用到了。

  一旦我们锁定硬件规格,整个设计氛围就会改变:硬件和软件之间发生断开连接。到目前为止,硬件设计是瓶颈。软件随时准备好等待测试电路板。一旦我们完成硬件,软件突然成为瓶颈。此外,我们不再讨论我们在硬件或软件方面可以做些什么;现在,这是所有的软件。硬件团队离开了项目,因为没有人能做到。任何硬件“问题”,例如I/O引脚反转或引脚布局,现在都是软件问题。令人沮丧的一天,当硬件团队努力解决地面问题时,他们宣称董事会已经完成并走开了,留下了一个未能运行与会话开始时相同代码的原型。我发现,有人将输入按钮反转为驱动低而不是高,但没有给我这个信息。

  此外,每个人都对如何改善董事会的职能有很好的想法,这也带来了另一个挑战。我们拥有简单的硬件基础,为软件提供了极大的灵活性现在我在枪口下,规格变化和特征蠕变成了流行病。存在两类规范更改:功能更改以及如何向用户(接口)提供这些功能的更改。在这两者中,设计师通常认为界面变化更简单,但实际上它们更难。例如,音序器没有显示,只有两个按钮。经过一些反思,我们意识到我们可以使用线本身来向用户传达信息,所以我现在有一个原始的10位显示器。但是,我还必须创建一个用于“打印”到此显示的函数库。只有两个按钮创造了在用户可编程序列模式下尝试为多达10个命令提供用户访问的挑战。我们提出了一个扩展移位键的想法;也就是说,按住一个按钮,同时反复按下另一个按钮。尽管该计划允许我们访问许多功能,但它在按钮事件的评估中产生了复杂性。最初,当按下按钮时,我标记了按钮1或按钮2事件。现在,我仍然不得不支持这个机制来编写我编写的代码,同时创建一个计数按钮2按下的移位状态并触发按钮1的释放。鉴于我必须在模拟环境中调试此问题,扩展移位起到中断的作用,并且我必须支持触发事件的旧方法,这个函数是我设计中最复杂的部分。

  PIC汇编语言最令人烦恼的一个方面是你必须记住位组合。例如,PIC16F84的文档说您可以在累加器W(0)或正在使用的文件寄存器(1)中存储计算。或者是周围的其他方式?直到我读到Peatman的书时,我才意识到,在F84包含文件中,PIC的设计者已经为你设置了W = 0和F = 1的常数。

  使用常量和宏来简化代码的诱惑诱惑了我。对于W/F区别,使用常量是有意义的。但是,我犹豫是否尽可能使用宏。例如,我发现自己总是被SUBLW和SUBWF抛弃。首先,助记符是不一致的:SUBLW从文字(LW)中减去W,SUBWF从F中减去W(应该是SUBWF?)。我一直不得不回到手册来说明问题。此外,在比较期间使用减法设置标志始终需要仔细检查。在x86汇编中,标志JA(如果在上面跳转),JB(如果在下面跳转)和JZ(如果在零时跳转)清楚地定义比较后的动作。这些命令在一条指令中测试零和进位标志。您还可以使用PIC(清单4)在一条指令中测试高于,低于和零。但是,您必须在从A减去A或从A减去A以仅检查进位标志而不是零标志。 (交换顺序会改变进位标志是否占零情况。)

  我考虑编写一个宏来进行比较,但是认为这个任务可能变得复杂,因为值可能是数据或常量,因此需要MOVLW或SUBLW。另外,我考虑了宏的一个有害的副作用:它们创建“专有”命令。专有命令的问题在于,有人阅读代码最初会发现它们令人困惑并且必须学习它们。此外,您在其他应用程序中编写代码时会遇到问题,因为某些原因您无法使用宏,或者必须使用其他人的代码。

  Microchip可以通过管理标准宏库来简化该领域的开发。例如,几乎每个程序都使用比较宏(参考文献3)。如果您使用标准宏库,宏将成为整个行业的命令,而不是程序员之间的绊脚石。在其辩护中,Microchip确实提供了一个令人印象深刻的应用笔记库(参考文献4),展示了程序员如何在PIC​​上实现各种功能。但是,需要花时间对这些笔记进行分类,让我自己通过自己开发代码来节省时间。

  在盒子外面思考

  我们在将音序器投入生产时学到的最重要的一课是设计不是一个线性过程。团队通常分为功能组:硬件;软件;包装,制造和测试;等等。不幸的是,设计依赖性不那么整齐。例如,包装被证明是一种粗鲁的觉醒。塑料盒似乎比电路板成本更高,我们必须将大部分元件安装在电路板的背面,以使电路板适合我们选择的现成的​​盒子。设计的每个阶段,无论其看似简单,都对其他阶段具有依赖性和影响。如果我们在急于制造电路板之前已经看过盒子,我们可以用不同的外形设计电路板并扩大我们可能的盒子选项。

  总而言之,这种经历启发了我们。我天真地认为编码只需要几天时间,所以我对我们超出预计时间表的时间感到震惊。当然,我们每个人都有其他全职工作和个人生活。但是,我严重低估了我加快PIC速度所需的时间。我忘记了我有多年的x86汇编经验,并且已经花费了大量的时间来理解该架构的复杂性和细微差别,以及常见的逻辑结构,例如表格。这些知识让我在PIC开发方面处于领先地位,但我仍然不得不重新构建我可编码的许多函数,而不用考虑x86,以及克服我的偏见和假设。关于假设的令人讨厌的事情是很容易忘记你曾经做过它们。

  现实情况是,8位μP仍然存在,并且正在寻找越来越复杂的应用程序(参考文献5)。在某些方面,它们专门用于某些任务,例如接口管理,传感器监控和串行通信,因为音频解码器适用于MP3播放器。而且,在设备不断变得更加智能化的世界中,它们以低成本提供了相当大的智能。

  经典混淆和错误

  臭虫是讨厌的小家伙,特别是如果他们是你自己的错。以下是我制作的一些经典混淆和错误,希望您能避免:

  十六进制/十进制混音:示例:当您使用10代替10而不是0'0a时。我发现最好在整个程序中使用一个基数和编号约定来避免歧义。

  文字/地址混淆:我有很多关于文字和地址的混淆,因为两者都被定义为常量。在16位和32位的世界中,汇编程序通常会标记对常量的误用。例如,我没有使用ADDLW posxw_table,而是使用ADDWF posxw_table,W。很明显,这个错误是我的,因为我试图在该地址而不是偏移处添加字节。然而,对于不熟悉的助记符,更容易犯这个错误,因为我习惯于使用Offset命令而不是明确地声明我在add指令中使用了文字。

  拐杖功能:早期,我的设计需要延迟功能。我第一次写了一个计算周期。制作这种快速和肮脏功能的问题在于我继续使用它的时间比我应该的时间长得多。更有用的功能,我知道我最终还是要编写,是TMR0延迟。因为我依赖于拐杖功能,所以我避免编写TMR0延迟,它定义了我的中断结构和事件标记机制。当我写TMR0延迟时,我不得不调整并重新调整它影响的部分。您应该更早而不是更晚地编写基础和结构函数。

  始终清除内存或假设它未定义:一度,我遇到了一个看起来像参数的问题,让我想知道存储在EEPROM中的参数是如何设置的。当我检查EEPROM时,参数是正确的,但程序无法根据参数执行。问题是由物理参数转换为逻辑参数造成的。 EEPROM存储上电参数,但您可以在运行模式下在RAM中更改它们。例如,您可以将速度从慢速切换到快速。我一直在为演示模式开发代码,其中逻辑参数工作(以慢速模式启动),但是当我以正常模式运行(以快速模式启动)时它们失败了。我造成了这个问题,因为我在用比特掩码之前从未清除过逻辑参数;我错误地认为内存从0开始(当芯片通过0xff启动时尤其愚蠢)!

  确认所有模式在主要代码更改:我经常在演示模式或编程模式下进行更改,然后在返回运行模式时发现错误。模式对整个系统状态做出了不同的假设,并且在您花费时间对代码的其他部分进行重大更改后,确认您的代码不会违反这些假设。

  强大的id =“id1138604-119-strong”>了解设备的架构:没有什么比认识到芯片不能按照你想象的那样工作的尴尬或沮丧。我已经说明了PIC家族对我的一些惊喜。虽然我可能会认为我使用了不充分的文档,但我不理解芯片,并且我失去了宝贵的开发时间。我在16位和32位世界中学到的许多经验都没有延续到8位领域。仅当我无法弄清楚某些内容时才参考规格表,这不利于学习PIC架构。

  了解您自己的硬件:一两次,硬件人员的任务与他们说的不同。早期,我希望我创建了一个简单测试硬件特性的虚拟程序,例如它是否驱动输出高或低,所以我可以确认没有人在我的原型上“修复”或“改进”电路板。此程序也类似于“健全性刻录”,以确认测试运行失败是您的代码的结果,而不是因为硬件故障或更改。


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分