初学者 - While() - 优化

Beginner - While() - Optimization

我是嵌入式开发的新手,前几次我红了一些关于 PIC24xxxx 的代码。

void i2c_Write(char data) {
    while (I2C2STATbits.TBF) {};

    IFS3bits.MI2C2IF = 0;

    I2C2TRN = data;

    while (I2C2STATbits.TRSTAT) {};

    Nop();
    Nop();
}

您如何看待 while 条件?微芯片不会为此使用大量 CPU 吗?

我问自己这个问题,意外地在网上看到了很多类似的代码。

没有更好的方法吗?


Nop() 也一样,为什么是两个?

What do you think about the while condition? Does the microchip not using a lot of CPU for that?

不,因为传输缓冲区不会长时间保持满状态。

I asked myself this question and surprisingly saw a lot of similar code in internet.

你会建议什么?

Is there not a better way to do it? (I hate crazy loops :D)

我、你或显然其他任何人都不知道。你认为它在哪些方面可能会更好?传输缓冲区不会保持足够长的时间来重新分配 CPU.

Nop() 也一样,为什么是两个?

Nop 确保信号保持稳定足够长的时间。这使得此代码在所有情况下都可以安全调用。没有它,只有在调用它之后没有立即弄乱 i2c 总线,调用这段代码才是安全的。但在大多数情况下,无论如何都会在循环中调用此代码,因此使其本质安全更有意义。

一般来说,为了与硬件进行交互,有两种方式:

  • 忙等
  • 中断基地

在您的情况下,为了与 I2C 设备交互,您的软件首先等待 TBF 位被清除,这意味着 I2C 设备已准备好接受要发送的字节。 然后您的软件实际上正在将字节写入设备并等待 TRSTAT 位被清除,这意味着数据已被您的 I2C 设备正确处理。

您显示的代码是用繁忙的等待循环编写的,这意味着 CPU 正在积极等待硬件。这确实是浪费资源,但在某些情况下(例如你的I2C中断线未连接或不可用)这是唯一的办法。

如果您要使用中断,您会要求硬件在给定事件发生时告诉您。例如,TBF 位被清除等... 这样做的好处是,当 HW 正在做它的事情时,您可以继续做其他事情。或者只是睡觉以节省电量。

我不是 I2C 方面的专家,所以我描述的中断事件很可能不准确,但这可以让您了解为什么会出现 2 个 while 循环。

现在关于中断基础实现和忙等待实现的优缺点,我会说基于中断的实现更有效但更难编写,因为您必须处理来自硬件的异步事件。 Busy wait 的实现很容易写但是速度较慢;但这对您来说可能仍然足够快。

最后,我不知道为什么那里需要 2 个 NoP。很可能需要进行调整,因为不知何故,CPU 仍然会太快。

在进行这些类型的交易时 (i2c/spi),您会发现自己处于两种情况之一:位爆炸或某种形式的硬件辅助。 bit bang 更容易实现、阅读和调试,并且通常可以从一个 chip/family 移植到另一个 chip/family。但是烧了很多cpu。但微控制器大多是定制硬件,如 cpld 或 fpga,更容易编程。他们在那里假装是硬件设计来燃烧 cpu 个周期。使用 i2c 或 spi,您试图在设备上的一些 I/O 引脚上创建特定波形,并且有时会锁定输入。公交车有规格,有时比您的 cpu 慢。有时不需要,有时当您添加软件和编译器开销时,您可能最终不需要计时器来延迟,您可能已经足够慢了。但理想情况下,您查看波形并简单地创建它,升高引脚 X 延迟 n ms,升高引脚 Y 延迟 n ms,降低引脚 Y 延迟 2*n ms,等等。这些延迟可能来自调谐循环(从 0 到 1341 计数)或轮询计时器,直到达到某个时钟的 Z 个滴答数。大量 cpu 浪费,但关键是您实际上只是可编程硬件,而硬件也会浪费时间等待。

当你的 mcu 中有一个辅助外围设备时,它可能会为你做 much/most 的计时,但也许不是全部,也许你必须 assert/deassert 芯片 select然后 spi 逻辑为您进行时钟和数据计时。而且这些外围设备通常非常特定于一个芯片供应商的一个系列,也许在整个芯片供应商中很常见,但从供应商到供应商都没有,因此非常不便携并且存在学习曲线。也许在你的情况下,如果 cpu 足够快,你可能会以违反总线时序的方式做下一件事,所以你将不得不消磨更多时间(也许你有那些Nops()).

将 mcu 视为软件可编程 CPLD 或 FPGA,这种浪费更有意义。不幸的是,与 CPLD 或 FPGA 不同,您是单线程的,因此您不能在时钟精确计时的同时并行执行几件微不足道的事情(正是这么多时钟任务切换状态并更改输出)。中断有帮助但不太一样,更改一行代码和您的时序更改。

在这种情况下,尤其是带有 nops 的情况下,您可能无论如何都应该使用示波器来查看 i2c 总线,并且 since/when 您在示波器上拥有它,您可以尝试使用和不使用这些调用来查看如何它影响波形。这也可能是外围设备中的错误或功能的情况,也许您不能太快地点击某些寄存器,否则外围设备会损坏。或者它可能是 5 年前芯片中的一个错误,代码是为此编写的,错误早已消失,但他们只是不断重复使用代码,你会在供应商库中看到很多。