近两年城市亮化工程中,多处可以见到一些彩色灯柱,颜色可变,可以单一颜色显示,也可以从上到下或者从下到上多彩渐变,这是APA102的一个典型应用场景。
在proteus中,发现也增加了这个器件的模拟,在使用过程中发现apa102的手册关于使用方法说的不是很详细。百度资料的时候,发现国内介绍这个灯珠的资料也比较少,这儿把APA102的使用方法简单介绍一下。
1. 硬件简介
APA102是个一个SPI接口的真彩led灯珠,rgb颜色控制,自带驱动,可以级联,同时具有256级灰度控制和32级亮度控制,是一个非常容易使用的彩灯,可以用来做led灯、大型led屏幕以及LED广告牌。
工业级设计,5V供电,单个灯珠功耗0.2瓦,最大不超过1W。
长宽高5x5x1.4mm。
单个灯珠的引脚定义如图 2所示,除去电源和地引脚外,DI、CI引脚分数是数据输入和时钟输入,分别接控制器的MOSI和SCK,DO与CO用来级联,接下个灯珠的DI和CI。使用SPI进行控制的时候,使用mode0,时钟线空闲周期为低电平,上升沿采样。
2. 软件控制方法
APA102的软件控制方法如图 4所示,除去开头一个起始帧,最后一个结尾帧以外,中间的是每个led的控制帧。
起始帧的格式是固定的,为连续的32bit的0,即连续发送4个16进制的0x00。如图 5所示。
结束帧也是发送特定长度的0,但是结束帧长度不固定,官方计算公式为:
举例来说,假设要控制256个灯珠的话,255×8/16=128,需要末尾发送128bit的0,也就是发送16个0x00。实际上使用中测试,在小于8个灯珠的时候,必须发送4个0x00才可以正确的结束一个控制流程,不影响下次的控制,大于8个的时候,未作测试。
起始帧和结束帧中间的是LED的颜色帧,每个led需要一个颜色帧数据,有多少个led中间就需要发送多少个颜色帧。单个led的控制帧格式如图 6所示。控制一个led需要四个字节的数据,第一个字节是亮度信息,第5位用来做32级亮度控制。后面紧跟着三个字节是蓝色、绿色、和红色颜色分量控制,每个led灯珠需要4个字节的控制信息。
3. 示例代码
使用stm32,管脚模拟的方式做SPI通信,用proteus模拟,代码使用外设库开发,示例代码如下。
示例代码:颜色定义
/*红 绿 黑 橙 蓝 黄 紫 白*/
u8 color[8][4] = {{255,220,20,60},{255,0,100,0},{255,0,0,0},
{255,0,140,255},{255,255,0,0},{255,0,255,255}, {255,148,0,211},{255,255,255,255}};
/**apa102 display color**/
#define RED 0x00
#define GREEN 0x01
#define BLACK 0x02
#define ORANGE 0x03
#define BLUE 0x04
#define YELLOW 0x05
#define ZISE 0x06
#define WHITE 0x07
#define FCOLOR 0x08
示例代码:apa102显示
/**apa102c display color defined in color**/
void apaDIS(u8 incolor)
{
switch(incolor){
case RED:
SPI_SendByte(0x00);//apa启动,写4个0
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
for(int i=0;i<8;i++)
{
SPI_SendByte(0xff);
SPI_SendByte(60);
SPI_SendByte(20);
SPI_SendByte(220);
}
SPI_SendByte(0x00);//停止
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
break;
case GREEN:
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
for(int i=0;i<8;i++)
{
SPI_SendByte(0xff);
SPI_SendByte(00);
SPI_SendByte(100);
SPI_SendByte(00);
}
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
break;
case BLACK:
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
for(int i=0;i<8;i++)
{
SPI_SendByte(0xff);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
}
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
break;
case ORANGE:
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
for(int i=0;i<8;i++)
{
SPI_SendByte(0xff);
SPI_SendByte(00);
SPI_SendByte(140);
SPI_SendByte(255);
}
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
break;
case BLUE:
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
for(int i=0;i<8;i++)
{
SPI_SendByte(0xff);
SPI_SendByte(255);
SPI_SendByte(00);
SPI_SendByte(00);
}
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
break;
case YELLOW:
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
for(int i=0;i<8;i++)
{
SPI_SendByte(0xff);
SPI_SendByte(00);
SPI_SendByte(255);
SPI_SendByte(255);
}
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
break;
case ZISE:
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
for(int i=0;i<8;i++)
{
SPI_SendByte(0xff);
SPI_SendByte(211);
SPI_SendByte(00);
SPI_SendByte(148);
}
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
break;
case WHITE:
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
for(int i=0;i<8;i++)
{
SPI_SendByte(0xff);
SPI_SendByte(255);
SPI_SendByte(255);
SPI_SendByte(255);
}
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
break;
case FCOLOR:
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
for(int i=0;i<8;i++)
for(int j=0;j<4;j++)
SPI_SendByte(color[i][j]);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
break;
default:
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
for(int i=0;i<8;i++)
for(int j=0;j<4;j++)
SPI_SendByte(color[i][j]);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
break;
}
}
示例代码:spi部分
void sSPI2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOD端口
/****SCLK MOSI 配置为输出**/
GPIO_InitStructure.GPIO_Pin = SPI_MOSI_PIN|SPI_SCLK_PIN|SPI_SNSS_PIN; //选择低端口2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50M时钟速度
GPIO_Init(SPI_DIFN_GRP, &GPIO_InitStructure); //GPIO配置函数
/**miso 配置为输入**/
GPIO_InitStructure.GPIO_Pin = SPI_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(SPI_DIFN_GRP, &GPIO_InitStructure);
}
u8 SPI_SendByte(u8 dt)
{
u8 temp=0,i;
SCLK=0;
for(i=8;i>0;i--)
{
if(dt&0x80)
MOSI=1;
else
MOSI=0;
dt<<=1;
SCLK=1;
Delay_us(5);
temp<<=1;
if(MISO)
temp++;
SCLK=0;
Delay_us(5);
}
SCLK=0;
return temp;
}
u8 SPI_RecvByte(void)//数据接收函数,方便调用
{
u8 spi_bValue;
for (u8 no=0;no<8;no++)
{
SCLK=1;
spi_bValue=(spi_bValue <<1);
Delay_us(5);
SCLK=0;
Delay_us(5);
if(MISO ==1)
spi_bValue|=0x01;
else
spi_bValue&=~0x01;
}
return spi_bValue;
}