SCT作为LPC系列非常有特色的一个定时器来讲,虽然做WS2812灯控不太划算,但是用WS2812作为例子稍微了解下如何使用SCT是完美的方法。在NXP官方的SCT cookbook里也专门有一章讲WS2811的驱动例子的,WS2811的时序和WS2812差不多,当然WS2813也是... 所以攻城狮一直很想用SCT试试,刚好这次一把抓了。
SCT用来做WS2812的控制有一点不太好,就是需要中断的介入,虽然开销不大,总归没有EZH之类的完全不占资源来的舒服。所以这里还是以学习为主。
SCT介绍
SCT是NXP独创的一种定时器,结合了状态机的概念。所以这个SCT定时器既可以完成普通的定时器功能,也可以具有状态机的功能,帮助用户自动切换不同的状态而无需MCU内核参与。具体的使用方面,NXP提供了一份cookbook,里边详细的介绍了SCT可以实现哪些功能,带死区的PWM,多通道PWM,RC5编解码等等。具体搜索AN11538即可。
SCT实现WS281x的优点
使用cookbook中的例程实现WS281x的控制还是比较简单的,例程使用SCTimer/PWM定时器的半组(16bit)定时器(原理是SCT是一个32bit的定时器,可以拆分成2组16bit的定时器),6个事件(events)和12个状态,基本上这个SCT除了驱动WS281x外还有额外50%的能力去干别的,前提是16bit定时器够用的情况下。
按照cookbook介绍,使用SCT的好处有
仅使用了其中1个16bit定时器,另外一个16bit定时器还可以做其他用途
使用了预分频将SCT的时钟降到最低,以降低功耗
自动的发送24bit的RGB数据,具有双缓冲的功能
每帧(既一个WS281x灯颗粒)产生一次中断,为CPU留足了时间处理下一帧的数据
数据发送完成后SCT自动挂起
自动产生RESET信号
SCT针对WS281x的配置
我们通过配置CONFIG.UNIFY=0来实现拆分SCT为两个16bit定时器。
匹配寄存器
MATCH0/MATCHREL0用来控制WS281x的频率,对WS2812来讲就是800kHz
MATCH1/MATCHREL1用来设置T1H的时间
MATCH2/MATCHREL2用来设置T0H的时间
也许你们忘了什么是T1H,什么是T0H,这里放张图恢复下记忆
输入和输出
给WS281x数据信号的管脚可以是任意SCTx_OUTx,只要我们按照代码要求设置好对应状态下的输出SET和CLR的寄存器即可。
这里cookbook的实例还用了一个辅助用的输出功能,用来实现双缓冲区机制,也可以把这个输出功能设置到任意SCT输出,也可以不引到实际的物理引脚,具体看用户使用情况。
我们这次的例子,使用SCT0_OUT5既PIO0_26为WS2812的DATA IN数据输入信号,SCT0_OUT4既PIO1_3为辅助观测的信号输出。
状态
利用SCT状态机的机制,可以帮助我们自动的完成WS281x数据的循环发送。由于芯片配置的原因,一些LPC的SCT最多只有15个状态(比如LPC1500),所以我们不得不考虑采用2次burst传输12bits数据(使用了12个状态)方式,完成一个WS281x RGB的数据。
每个bit传输完成后,状态机会从状态11开始向下做状态迁移。最后一个bit传输完成后,SCT状态机会迁移到状态0,状态机自动切换到状态11并开始下一次12bit的数据传输。状态11意味发送的是最高位(MSB),状态0发送的是最后一个bit(LSB)。
根据状态机的设置,每当开始传输一个新的bit的时候,SCT的Data OUT输出为高电平,此时SCT的两个匹配寄存器会先设置定时器的T0H(MATCH1,EV13)和T1H(MATCH2,EV14)触发事件,这个触发事件会自动清除Data输出(既输出0)。如果没有新的事件(Event)发生则Data输出信号一直为0。
为了实现双buffer的机制,这里我们需要另外一个事件来决定在另外一个12个状态里每个状态传输的bit是0还是1。这里官方的例子用的是事件12。
SCT如何操作生成WS281x的时序
关于如何操作SCT生成时序,各位可以参考如下步骤:
- 配置SCT为Split模式,既16bit计数,分成2个SCTimer
- 配置匹配寄存器
a. MATCHREL0 = 系统时钟/DATA_SPEED - 1
b. MATCHREL1 = 32% x 系统时钟/DATA_SPEED - 1
c. MATCHREL2 = 68% x 系统时钟/DATA_SPEED - 1 - 配置事件:
a. 事件 15: MATCH0, 所有状态,DATA = 1 和 STATE += 31
b. 事件 14: MATCH2, 除了0状态外的所有状态, DATA = 0
c. 事件 13: MATCH1, 所有状态,DATA = 0
d. 事件 12: MATCH1 && AUX==0, 所有的状态 [11:0]需要输出逻辑1的, DATA = 1
e. 事件 11: MATCH1 && AUX==1, 所有的状态 [11:0]需要输出逻辑1的, DATA = 1
f. 事件 10: MATCH2,状态0, IRQ, AUX = 翻转,STATE = 12
传输一组数据
- 暂停SCT定时器
- 预先复位SCT定时器,将WS281x复位的时间值赋值给COUNT_H,以产生复位的低电平信号。
- 设置STATE_L 为 12。
- 将发送的数据分成2个12bit的数据配置到Event12和Event11的状态寄存器中,确保这两个寄存器的bits[31:12]为0.
- 启动定时器L
中断处理
当一帧(12bit)的数据传输完成后,会触发一次中断。
- 如果是最后一帧,则停止定时器
-
读取g_WS2812SCTCount,如果是奇数(既24bit数据头12bit传输完成),则把下次传输的24bit数据拆成2个12bit。如果是偶数(既24bit数据后12bit传输完成),则把上次拆分的2个12bit数据分别配置进Event12和Event11
中断中配置Event12和Event11
输出的波形
实际的点灯代码
实际的点灯代码的API,我这边将SPI,EZH,双核,SCT都是统一的,main函数里边的内容一样,参考EZH那篇(之二)末尾介绍的API即可。