一、简介
CH466 是一款 6*18 矩阵三色LED驱动芯片,3*6路PWM驱动,最多支持18个COM公共端动态扫 描,最多可驱动108组三色LED或324只单色LED。可选7位或8位单色PWM数据,最高3*8位组合 色PWM,支持16777216 种组合颜色。可选6位或7位或8位亮度PWM数据,支持256级灰度。所有 PWM 引脚具有源电流简单恒流功能。
下图为CH466的系统框图。

二、特点
(1)3x6路PWM和18路COM,可驱动最多108组三色LED或324只单色LED
(2)可选7/8位单色PWM数据,最高3*8位组合色PWM,支持16777216种组合颜色
(3)可选6/7/8位亮度PWM数据,支持256级灰度
(4)矩阵大小可调
(5)PWM引脚具有源电流简单恒流功能
(6)接口丰富,支持I2C/UART/USB/SPI接口
-
1MHz I2C 接口,多个I2C地址可配置
-
6MHz SPI 接口,支持3线/4线SPI模式
-
3MHz UART 接口,多种波特率可配置
-
12MHz 全速USB接口和单线USB接口
(7)支持1路ARGB灯驱动
(8)LED消影时间多级可调
(9)支持自动呼吸功能
(10)免晶体
三、功能概述
1、工作模式配置
在芯片复位后,会根据SPI4_EN、SPI3_EN、USB_EN和UART_EN引脚上的22K下拉电阻选择对应 的工作模式。22K下拉电阻只能同时连接到其中一个引脚上。如果SPI4_EN、SPI3_EN、USB_EN和UART_EN 都未连接22K下拉电阻,芯片将默认工作在I2C模式。在不同的工作模式下,CH466支持的最大RGB 矩阵大小不同。如果芯片使能了RESET#引脚,相应的矩阵COM数量会减少1,详见下表。

2、SPI模式
SPI 接口支持3线模式和4线模式,兼容模式0和模式3。在3线模式下,SCS、SCK、MOSI分别 作为片选、时钟和数据引脚;在4线模式下,SCS、SCK、MOSI、MISO分别充当片选、时钟、主机输出 从机输入数据、主机输入从机输出数据引脚。
4线SPI接口时序如下:

TRI 为传输等待时间 主机发送的第一个字节为命令(Command),其含义见下图。随后,发送需要操作的寄存器地址 和一个Dummy字节。Dummy字节没有实际意义,可以使用0x00或0xFF填充。

(1)写CH466寄存器 主机拉低 SCS,发送命令和需要写入的寄存器地址和数据。连续写入时,支持寄存器地址自增。
(2)读CH466寄存器 主机拉低SCS,发送命令和需要读取的寄存器地址,CH466 会返回对应的寄存器数据。连续读取 时,支持寄存器地址自增。
在三线SPI下,需要注意的是其通讯引脚为MISO,并非MOSI,需要着重注意!!!
三线SPI的读写时序稍微有点区别,写时序和上述4线SPI无明显区别;而读时序就要做出区分,因为其只有1根通讯线,所以在读数据时需要把主机端的MOSI引脚状态改为输入模式,来接收芯片返回的数据。
四、硬件设计
1、芯片外围
此芯片是免晶振设计,所以芯片的外围十分的精简,如下如所示:

需要使用SPI模式,就按章节3.1表格所示,讲对应引脚对地接22K下拉电阻。
2、LED灯设计(部分展示)
芯片目前只支持共阴极的三色LED灯,在选型时需要注意,部分设计如下:

五、程序开发
1、接口初始化
在SPI模式下,根据是3线模式还是4线模式,去使能双线全双工还是单线模式,我们以3线SPI为例:
void MySPI3Line_Init()
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
SPI_InitTypeDef SPI_InitStructure = {0};
DMA_InitTypeDef DMA_InitStructure={0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
}
2、寄存器读写
具体的寄存器地址和具体每一位的功能可以参考芯片的datasheet,我们使用硬件DMA来进行读写,如果不是频繁的切换灯效,也可以不使用DMA。代码如下:
uint8_t spi3_tx_rx_data(uint8_t *p_tx,uint8_t tx_len,uint8_t *p_rx,uint8_t rx_len)
{
uint8_t *ptr;
SPI_CS_LOW();
GPIOA->CFGLR = GPIOA->CFGLR & ~0xF0000000 | 0x90000000;
SPI1->CTLR1 |= SPI_CTLR1_BIDIOE; // MOSI Tx Enable
DMA1_Channel3->CFGR &= ~ DMA_CFGR2_EN;
DMA1_Channel2->CFGR &= ~ DMA_CFGR2_EN;
DMA1_Channel3->MADDR = (uint32_t)p_tx;
DMA1_Channel2->MADDR = (uint32_t)p_rx;
DMA1_Channel3->CNTR = tx_len;
DMA1_Channel2->CNTR = tx_len;
DMA1_Channel3->CFGR |= DMA_CFGR2_EN; // Tx
// DMA1_Channel2->CFGR |= DMA_CFGR2_EN; // Rx
SPI1->CTLR1 |= SPI_CTLR1_SPE;
while( (!DMA_GetFlagStatus(DMA1_FLAG_TC2)) && (!DMA_GetFlagStatus(DMA1_FLAG_TC3)) );
while (SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY));
if (rx_len)
{
GPIOA->CFGLR = GPIOA->CFGLR & ~0xF0000000 | 0x40000000; // 切换为浮空输入
ptr = p_rx + tx_len;
SPI1->CTLR1 &= ~SPI_CTLR1_SPE;
Delay_Us(3);
if (rx_len == 1)
{
SPI1->CTLR1 &= ~SPI_CTLR1_BIDIOE; // MOSI Rx Enable
SPI1->CTLR1 |= SPI_CTLR1_SPE;
SPI1->CTLR1 &= ~SPI_CTLR1_SPE;
while (!(SPI1->STATR & SPI_STATR_RXNE));
*ptr++ = SPI1->DATAR;
}
else
{
SPI1->CTLR1 &= ~SPI_CTLR1_BIDIOE; // MOSI Rx Enable
SPI1->CTLR1 |= SPI_CTLR1_SPE;
// 接收前N-1的字节
for (size_t i = 0; i < rx_len - 1; i++)
{
while (!(SPI1->STATR & SPI_STATR_RXNE));
*ptr++ = SPI1->DATAR;
}
Delay_Us(3);
SPI1->CTLR1 &= ~SPI_CTLR1_SPE;
while (!(SPI1->STATR & SPI_STATR_RXNE));
*ptr++ = SPI1->DATAR;
}
}
SPI_CS_HIGH();
Delay_Ms(1);
return 0;
}
3、LED驱动
驱动LED只需要按照手册要求初始化各个寄存器的参数,然后给相应的值即可完成LED的驱动,这里以点亮全部灯为例,参考代码如下:
void set_all_led(uint8_t i,uint8_t r,uint8_t g,uint8_t b)
{
for (uint8_t com = 0; com < 18; com++)
{
for (uint8_t seg = 0; seg < 6; seg++)
{
rgb_buf[6*com + seg][0] = i;
rgb_buf[6*com + seg][1] = r;
rgb_buf[6*com + seg][2] = g;
rgb_buf[6*com + seg][3] = b;
}
}
}
以上仅为部分程序,如需完整工程可以私信联系获取。