sujingliang · 1月6日

【Mini-F5265-OB开发板试用测评】4、SPI归零码驱动LED恒流芯片

手头有这种LED装饰照明灯条

1.png
主控芯片是GS8206,驱动7颗非RGB LED灯,工作电压24V。

查了一下资料:
GS8206是一颗3通道,带断点续传及内控花样的LED恒流驱动芯片。
芯片内置7805,工作电压:5V-24V
默认17.5mA 恒流输出,最小输出电流11mA
GS82xx点光源系列芯片可任意串联,无限连接
GS8206采用拓展式归零码作为信号传输方式,可逐通道控制输出电流,并无限级联。串行数据频率800kHz。

GS8206和WS2812都可以用归零码驱动,原理上是一样的,所有决定用Mini-F5265-OB+WS2812驱动方式实现驱动。

一、归零码

2.png
GS8206可以做级联,每个GS8206有3通道输出,VDH最高电压24V.图中control部分是要实现部分,接入SDI,归零码协议。

GS8206 采用拓展归零码数据传输方式,单通道 8bits 数据,每颗 IC 支持 3 通道显示。传输数据经过内部滤波,支持数据防抖动功能。拓展型归零码兼容普通归零码,因此适用于市面上大多数归零码控制。

2.png

单码采用 1:3 的占空比,标准 800kHz 传输速度,最高可达 1MHz。每级数据整形转发,芯片间数据延时<0.7us,满足实际动态效果需求。
3.png

二、SPI实现归零码

这部分与WS2812驱动基本一致,所以完全不用重复制造轮子。
科学上网并不容易,应该是从这个地址下载的WS2812驱动:https://github.com/libdriver/...
其中实现方式SPI。

其中发一帧数据(也就是一个0或一个1)的实现函数

static void a_ws2812b_write_one_frame(uint32_t rgb, uint8_t temp[48])
{
    uint8_t r, g, b;
    uint8_t i, j;
    uint32_t c, point;
    const uint16_t one_code = 0xFFF8U;
    const uint16_t zero_code = 0xE000U;
    
    r = (uint8_t)((rgb >> 16) & 0xFF);                               /* set red */
    g = (uint8_t)((rgb >> 8) & 0xFF);                                /* set green */
    b = (uint8_t)((rgb >> 0) & 0xFF);                                /* set blue */
    c = ((uint32_t)(g) << 16) | ((uint32_t)(r) << 8) | b;            /* set color */
    
    memset(temp, 0, sizeof(uint8_t) * 30);                           /* clear the temp buffer */
    
    point = 0;                                                       /* clear point */
    for (i = 0; i < 24; i++)                                         /* set 24 bit */
    {
        if (((c >> (23 - i)) & 0x01) != 0)                           /* if bit 1 */
        {
            for (j = 0; j < 16; j ++)                                /* 16 bit */
            {
                if (((one_code >> (15 - j)) & 0x01) != 0)            /* if one code */
                {
                    temp[point / 8] |= 1 << (7 - (point % 8));       /* set bit 1 */
                }
                else
                {
                    temp[point / 8] |= 0 << (7 - (point % 8));       /* set bit 0 */
                }
                point = point + 1;                                   /* point++ */
            }
        }
        else                                                         /* if bit 0 */
        {
            for (j = 0; j < 16; j ++)                                /* 16 bit */
            {
                if (((zero_code >> (15 - j)) & 0x01) != 0)           /* if zero code */
                {
                    temp[point / 8] |= 1 << (7 - (point % 8));       /* set bit 1 */
                }
                else
                {
                    temp[point / 8] |= 0 << (7 - (point % 8));       /* set bit 0 */
                }
                point = point + 1;                                   /* point++ */
            }
        }
    }
}

在函数中用1个字节对应归零码中的一个0码或1码。
其中:
0码的数值表达是:zero_code = 0xE000U;化成二进制就是1110000000000000
对应:
4.png
也就前面3个1表示高电平,后面13个0为低电平。

1码的数值表达是:one_code = 0xFFF8U;化成二进制就是1111111111111000
对应:
5.png
也就前面13个1表示高电平,后面3个0为低电平。

上面并没有精确地匹配1/4T和3/4T,但应该是在误差允许范围,实际效果没有影响。

发送多个数据的函数如下:

uint8_t ws2812b_write(ws2812b_handle_t *handle, uint32_t *rgb, uint32_t len, uint8_t *temp, uint32_t temp_len)
{
    uint32_t i, bit_size;

    if (handle == NULL)                                                     /* check handle */
    {
        return 2;                                                           /* return error */
    }
    if (handle->inited != 1)                                                /* check handle initialization */
    {
        return 3;                                                           /* return error */
    }
    if (rgb == NULL)                                                        /* check rgb */
    {
        handle->debug_print("ws2812b: rgb is null.\n");                     /* rgb is null */
       
        return 4;                                                           /* return error */
    }
    if (temp == NULL)                                                       /* check temp */
    {
        handle->debug_print("ws2812b: temp is null.\n");                    /* temp is null */
       
        return 5;                                                           /* return error */
    }
    
    bit_size = WS2812B_EACH_RESET_BIT_FRAME_LEN * len;                      /* set the bit size */
    bit_size = bit_size / 8;                                                /* set the bit size */
    if (bit_size > temp_len)                                                /* check temp length */
    {
        handle->debug_print("ws2812b: temp buffer is too small and "
                            "size will be %d.\n", bit_size);                /* temp buffer is too small*/
       
        return 5;                                                           /* return error */
    }
        

    for (i = 0; i < bit_size; i++)                                          /* set the reset frame */
    {
        temp[i] = 0x00;                                                     /* set 0x00 */
    }
        

    if (handle->spi_write_cmd(temp, (uint16_t)bit_size) != 0)               /* write command */
    {
        handle->debug_print("ws2812b: write command failed.\n");            /* write command failed */
        
        return 1;                                                           /* return error */
    }

    bit_size = 24 * 16 * len ;                                              /* set the bit size */
    bit_size = bit_size / 8;                                                /* set the bit size */
    if (bit_size > temp_len)                                                /* check temp length */
    {
        handle->debug_print("ws2812b: temp buffer is too small and "
                            "size will be %d.\n", bit_size);                /* temp buffer is too small*/
       
        return 6;                                                           /* return error */
    }
   
    for (i = 0; i < len; i++)                                               /* set the color frame */
    {
                
        a_ws2812b_write_one_frame(rgb[i], &temp[i * 48]);                   /* set color */
                
    }
    
    if (handle->spi_write_cmd(temp, (uint16_t)bit_size) != 0)               /* write command */
    {
        handle->debug_print("ws2812b: write command failed.\n");            /* write command failed */
        
        return 1;                                                           /* return error */
    }
    
    return 0;                                                               /* success return 0 */
}

归零码的思想是驱动RGB灯的,RGB是一个83=24位的数据,而SPI表示归零码每两个字节对应一个0码或1码,也就是说SPI需要传2416个位才能标识一个RGB数值。

三、SPI配置

uint8_t spi_init(void)
{
        GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDef  SPI_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    SPI_StructInit(&SPI_InitStruct);
    SPI_InitStruct.SPI_Mode      = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize  = SPI_DataSize_8b;
    SPI_InitStruct.SPI_DataWidth = 8;
    SPI_InitStruct.SPI_CPOL      = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA      = SPI_CPHA_2Edge;
    SPI_InitStruct.SPI_NSS       = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    SPI_InitStruct.SPI_FirstBit  = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStruct);

    SPI_BiDirectionalLineConfig(SPI1, SPI_Enable_RX);
    SPI_BiDirectionalLineConfig(SPI1, SPI_Enable_TX);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    SPI_Cmd(SPI1, ENABLE);

        return 0;
}

配置SPI1,波特率10Mhz,其中PA7为MOSI

spi写数据:

void SPI_TxData_Polling(uint8_t *Buffer, uint16_t Length)
{
    uint16_t i = 0, Data = 0;

    for (i = 0; i < Length; i++)
    {
            
        SPI_SendData(SPI1, Buffer[i]);

        while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
        {
        }

        while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL))
        {
        }

        Data = SPI_ReceiveData(SPI1);
    }

}

四、测试程序

uint8_t ws2812b_basic_init(void)
{
        uint32_t i,j;
        uint32_t num=21;
    uint8_t res;
    const uint32_t color[7] = {0xFF0000U, 0xFF7F00U, 0xFFFF00U, 0x00FF00U, 0x00FFFFU, 0x0000FFU, 0x8F00FFU};    //两种测试数据
        //const uint32_t color[7] = {0x002000U, 0x003000U, 0x004000U, 0x005000U, 0x006000U, 0x007000U, 0x008000U};
        
    /* link interface function */
    DRIVER_WS2812B_LINK_INIT(&gs_handle, ws2812b_handle_t);
    DRIVER_WS2812B_LINK_SPI_10MHZ_INIT(&gs_handle, ws2812b_interface_spi_10mhz_init);
    DRIVER_WS2812B_LINK_SPI_DEINIT(&gs_handle, ws2812b_interface_spi_deinit);
    DRIVER_WS2812B_LINK_SPI_WRITE_COMMAND(&gs_handle, ws2812b_interface_spi_write_cmd);
    DRIVER_WS2812B_LINK_DELAY_MS(&gs_handle, ws2812b_interface_delay_ms);
    DRIVER_WS2812B_LINK_DEBUG_PRINT(&gs_handle, ws2812b_interface_debug_print);
    
    /* ws2812b initialization */
    res = ws2812b_init(&gs_handle);
    if (res != 0)
    {
        //ws2812b_interface_debug_print("ws2812b: init failed.\n");
                printf("ws2812b: init failed.\n");
       
        return 1;
    }
    
        
        
        for (i = 0; i < 1000; i++)
    {
         for (j = 0; j < 21; j++)
        {
            gs_rgb[j] = color[i % 7];
        }
        res = ws2812b_write(&gs_handle, (uint32_t *)gs_rgb, num, gs_buffer, 1500);
                printf("\r\nws2812b_write,%d\r\n",res);
                delay_ms(1);
            
        }
    return 0;
}

color是控制显示花样,测试了两种:渐亮和旋转显示

五、效果

渐亮:
tutieshi_480x272_6s.gif

旋转显示:
tutieshi_480x272_3s.gif

普通3通道白色LED,不是RGB只能显示成这样了。

全家福:
2.jpg

推荐阅读
关注数
0
文章数
4
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息