本帖最后由 blust5 于 2023-8-10 08:18 编辑
#申请原创# @21小跑堂 @21小跑堂 @21小跑堂
先来说一下前景提要。 最近与朋友一起搞了一个台阶流水灯项目,安装在楼梯台阶侧面和扶手上,人走过时次第点亮与熄灭。 台阶上的灯使用长条形灯条,每个灯条对应一颗LED控制芯片。 扶手或者墙壁上的灯带使用流水灯带,LED控制芯片直接焊接在灯带上面。 这种灯条和灯带都是用的RGB灯,可以显示各种颜色,不过我们做的项目目前只显示了白色,只是亮度可调。 灯条和灯带的LED控制芯片都是WS2811,这是一颗单线256级灰度三通道恒流LED驱动IC,可以很方便的级联多个芯片,只需要一个IO口就能控制一长串灯,很适合这个项目。
项目使用GD32E230K8T6芯片,带4位数码管显示和三个按键。 主芯片部分电路图。 上图是三路LED驱动电路接口。这篇帖子主要也是讲这部分内容的。
正常来说IO口直接连接灯带的数据输入口就可以实现控制,这里增加了一个三极管,是为了增加驱动能力,因为灯带的连接线可能会比较长。 根据LED驱动IC的芯片手册上说明的时序,编写驱动程序。 可以看到,时序需要控制在纳秒级。MCU的时钟使用的是72MHz,是可以实现纳秒级控制的。 但是这里使用定时器的话,会比较麻烦,各类接口跳转、入栈出栈动作耗费的时间在这里就无法忽略了。 因此这里直接使用IO翻转加上空指令的方式进行驱动,通过调整NOP指令数量来调节高低电平时间。代码内容如下: void ws_step_reset()
{
gpio_bit_set(STEP_LED_PORT,STEP_LED_PIN);
delay_1ms(1);
}
void ws_step_set1(uint32_t led_data)
{
uint8_t i;
for(i=0;i<24;i++)
{
if((led_data & 0x800000))
{
gpio_bit_reset(STEP_LED_PORT,STEP_LED_PIN);
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
gpio_bit_set(STEP_LED_PORT,STEP_LED_PIN);
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
}
else
{
gpio_bit_reset(STEP_LED_PORT,STEP_LED_PIN);
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
gpio_bit_set(STEP_LED_PORT,STEP_LED_PIN);
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
}
led_data <<= 1;
}
}
由于LED驱动接口增加了一颗三极管用来驱动,所以实际输出信号与MCU的IO口信号是反的,因此这里的高低电平和手册上是反过来的。
驱动函数到这里就完成了,使用的时候直接调用ws_step_set1()函数和ws_step_reset()函数即可,其中ws_step_set1()函数的参数为LED灯的亮度和颜色信息,为32位数据,其中有效位数为24位,R、G、B信息各占8位。 当使用级联灯带或灯条时,只需多次调用ws_step_set1()函数,并在最终位置调用ws_step_reset()函数即可。
程序编写完成,开始进行测试。 在用示波器调试波形占空比的时候,发现无法完成相对精确的时间调整。比如哪怕把某一部分的NOP指令完全删掉,还是会有相当一部分信号宽度在。 经过分析,猜测可能是库函数接口调用过程占用了时间,于是直接将gpio_bit_set()函数和gpio_bit_reset()函数直接替换成寄存器操作,减少调用过程。IO口的置位和复位操作分别替换成如下语句: GPIO_BC(STEP_LED_PORT) = (uint32_t)STEP_LED_PIN; // IO口复位操作
GPIO_BOP(STEP_LED_PORT) = (uint32_t)STEP_LED_PIN; // IO口置位操作
然后继续调整高低电平的时间,发现果然如此,换成寄存器直接操作的话,时间范围的可调整性就出来了。通过调整NOP数量完成时序匹配之后,驱动代码已经完成,准备接入灯带进行测试。
果然开发过程没办法一帆风顺。测试时发现LED灯带并没有按照预期的亮度和颜色配置亮起来,出现亮灯情况和参数配置对不上的问题。 通过示波器观察输出口波形,发现波形失真,三极管导通时可以很及时,但是关断时总是延迟一段时间,从而导致最终波形与预设参数不符。
于是考虑更换三极管。将三极管更换成MOS,进行测试,发现并没有很好的改善。 朋友忽然之间想到一个方法,在这里放一颗WS2811芯片,用这颗芯片进行驱动能力的提升。并在进行LED灯带控制的时候,跳过这一颗芯片对应的LED灯,即控制灯带时在开头增加一个灯位的数据。 于是进行测试,测试结果还算可以,WS2811确实可以很好的将信号转发出去,但是发现了另一个问题:WS2811芯片不仅仅是进行信号转发,还进行了信号的二次整形。也就是说WS2811芯片在转发时,会把符合1码的高电平时间改为600ns,将符合0码的高电平时间改为300ns,而不论之前接收到的高电平时间是多少。 这会造成一个问题:如果灯带或灯条的LED驱动芯片不是WS2811而是其他同类芯片,对应高低电平时间不一样的话,就无法仅仅通过修改代码来完成适配了。
于是继续想其他方式。最终专门找了一颗驱动增强芯片SN74LVC2G34,来进行信号驱动增强。 经过测试,这颗芯片在增强驱动能力的同时,翻转速度也能达到需求。
至此,LED灯带驱动的硬件问题,算是基本解决,可以进行软件编写和功能优化了。
|
一个很不错的小设计,有利于提升氛围感,作者对设计过程中出现的问题仔细分析,逐步解决,最终实现,值得肯定。