近兩年城市亮化工程中,多處可以見到一些彩色燈柱,顔色可變,可以單一顔色顯示,也可以從上到下或者從下到上多彩漸變,這是APA102的一個典型應用場景。
在proteus中,發現也增加了這個器件的模擬,在使用過程中發現apa102的手冊關于使用方法說的不是很詳細。百度資料的時候,發現國內介紹這個燈珠的資料也比較少,這兒把APA102的使用方法簡單介紹一下。

图 1 ATA102在proteus库中的外形
1. 硬件简介
APA102是個一個SPI接口的真彩led燈珠,rgb顔色控制,自帶驅動,可以級聯,同時具有256級灰度控制和32級亮度控制,是一個非常容易使用的彩燈,可以用來做led燈、大型led屏幕以及LED廣告牌。
工業級設計,5V供電,單個燈珠功耗0.2瓦,最大不超過1W。
長寬高5x5x1.4mm。

图 2 引脚图
单个灯珠的引脚定义如图 2所示,除去电源和地引脚外,DI、CI引脚分数是数据输入和时钟输入,分别接控制器的MOSI和SCK,DO与CO用来级联,接下个灯珠的DI和CI。使用SPI进行控制的时候,使用mode0,时钟线空闲周期为低电平,上升沿采样。

图 3 SPI模式
2. 软件控制方法

图 4 控制协议
APA102的软件控制方法如图 4所示,除去开头一个起始帧,最后一个结尾帧以外,中间的是每个led的控制帧。
起始帧的格式是固定的,为连续的32bit的0,即连续发送4个16进制的0x00。如图 5所示。

图5 起始帧
結束幀也是發送特定長度的0,但是結束幀長度不固定,官方計算公式爲:
舉例來說,假設要控制256個燈珠的話,255×8/16=128,需要末尾發送128bit的0,也就是發送16個0x00。實際上使用中測試,在小于8個燈珠的時候,必須發送4個0x00才可以正確的結束一個控制流程,不影響下次的控制,大于8個的時候,未作測試。

图 6 LED帧
起始帧和结束帧中间的是LED的颜色帧,每个led需要一个颜色帧数据,有多少个led中间就需要发送多少个颜色帧。单个led的控制帧格式如图 6所示。控制一个led需要四个字节的数据,第一个字节是亮度信息,第5位用来做32级亮度控制。后面紧跟着三个字节是蓝色、绿色、和红色颜色分量控制,每个led灯珠需要4个字节的控制信息。
3. 示例代码
使用stm32,管腳模擬的方式做SPI通信,用proteus模擬,代碼使用外設庫開發,示例代碼如下。

图 7 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;
}