欢迎光临散文网 会员登陆 & 注册

【深圳 IO 攻略】第 11 关:变色电子烟笔

2022-06-02 10:44 作者:ココアお姉ちゃん  | 我要投稿

本文首发于 B 站《深圳 IO》文集(https://www.bilibili.com/read/readlist/rl569860)。原创不易,转载请注明出处。

关卡展示

本关的 C2S-RF901 元件会不定期地发送一些长度为 4 的数据包,数据包里的前三个数表示一个颜色的 RGB 值(不过范围不是 0~255 而是 0~100),第四个数表示当前颜色的持续时长。如果在当前颜色的时间范围内收到了新的数据包,那么立刻停止计时,切换到新的颜色上。

我们很轻易地就能设计出这样的一个算法:

  1. 从 C2S-RF901 元件读入当前时钟周期内的首数字。

  2. 首数字不是 -999 时,说明要将变色笔置为新的颜色。将当前数据包里的前三个数分别传给右侧原件的 R、G、B 通道,然后将第四个数表示的持续时长传入芯片的 acc 寄存器。

  3. 判断剩余时间是否减到了 0。若未减到 0,则令 acc -1(剩余时间 -1)。若减到了 0,则将变色笔的颜色清除。

  4. 做完以上操作后,休眠一秒,进入下一个时钟周期。

由于变色笔由三个 p 口组成,且不是只有 0/100 两种数字,所以本例里我们无法使用 DX-300,我们只能这样做:其中一块芯片传输 R、B 两个颜色通道的值,对于剩下的 G 通道,我们将值通过 x 口传给另一块芯片,委托它用自己的 p 口把收到的数据传给 G 通道。

代码如下:

这里,我们接触到了一条新的指令:等待唤醒指令 slx。它的用法如下:

slx P,作用是令芯片在收到端口 P 的信号前一直保持睡眠。我在第 9 关的攻略里说过,芯片间使用 x 口传输数据时,必须在同一个时钟周期内,一方发送数据的同时,另一方接收等长的数据,通讯才能完成。一旦不满足以上任意一个条件(包括只发不收、只收不发、发送和接收的长度不一致),就会导致运行时阻塞。那么问题来了,接收方不知道自己什么时候会收到数据,不知道自己该等待到哪个时钟周期时接收怎么办?我又不能主动去读,读不到数据的话一样会阻塞。

slx 指令就是为了解决这样的问题而存在的。它的作用是,我来随时监视外面有没有数据进来,我叫你读你再读,我不叫你的时候你就给我老老实实等着。只要我叫你的时候你立刻读就一定没有问题(除非唤醒了以后又 slp 睡过去了,或者读的长度和发的长度不一致,那样的话仍然会阻塞)

那么,这道题里,左边的芯片不会每秒钟都告诉右边芯片,现在的 G 通道值是多少。只有当颜色改变的时候,才会通知右边的芯片改写掉 G 通道。因此,右边的芯片的确不知道自己该什么时候去读 x0 口的数据。所以右边的这块芯片里,我们需要在代码的开头加入一条 slx x0 指令,相当于为 x0 端口安排一个监控哨兵,告诉哨兵:“我现在睡了,啥时候 x0 口传过来数据了就叫醒我,这时候我读这个值肯定不会阻塞。然后,我把 G 通道改写成这个值以后就继续睡了,等有新数据了再重新叫醒我。”

右边的芯片虽然只有两条指令,但这两条指令里却有这么多的学问。

现在我们回过头来看左边的芯片。首先,我们之前提到过,x 口的数据只能读一次,不像 p 口那样可以在同一秒内反复读。那么,对于可能需要多次读取的数据,我们就必须先把读入的数据放到寄存器里暂存一下。像这道题里,首数字是 -999 则无视(只读一次),首数字大于 -999 则将首数字的值发给 R 通道(读了两次),首数字是存在读两次的可能性的,所以必须将首数字暂存到 dat 里,然后在 dat 上做文章,仅当 dat 大于 -999 时才将 dat 写到 R 通道里。而对于非首数字,因为只读一次就够了,所以就没必要暂存到寄存器里了,直接将 x0 的值传走就 OK。

然后我们来逐行分析左边芯片里的代码:

点击左下角的【模拟】,稍等片刻,便会弹出结算界面:

优化电量和代码行数

我们将“剩余时间不为 0 时令剩余时间 -1”的逻辑改为“首数字为 -999 时剩余时间 -1”,这样,当某一刻颜色清零时,倒计时仍然会继续进行下去变成负数,不会停在 0。之前的方案里,倒计时到达 0 时就不会继续计时了,这就导致长时间不来新的颜色信号时,teq acc 0 这条判断始终成立,就会导致“反复清零”的负面效果。经过以上改进后,倒计时只会在来颜色信号时重置为某个数,而永远不会停止,程序也只会在倒计时到达 0 的那一刻执行一次清零效果,而当倒计时到达负数时,不做任何操作。这样就节省了电量。

现在,我们将第 2 行的 tcp dat -999 改为 tcp dat -1,然后将第 8 行的 - sub 1 挪到第 2 行代码后面。代码变成了下面的样子:

而对于代码行数的优化,则需要用到我在第 10 关里提到过的一个技巧:【读一个只写 p 口时会读到恒 0 数据,同时之前写入该 p 口的数据也会被清零】。这一关里,因为左边 MC6000 的 p0 和 p1 口连接的是只写的 R、B 通道,所以

这两行代码可以合并为一行:

最终代码如下:

最终的三项指标为:成本 ¥8,电量 378(比历史最佳减少了 129 格电),代码行数 13(比历史最佳减少了 1 行代码)


【深圳 IO 攻略】第 11 关:变色电子烟笔的评论 (共 条)

分享到微博请遵守国家法律