I also had problems interpreting the structure of the .LED file however another method would be to create your own bitmaps. I created a LED image using Microsoft Paint, under File | Properties
I set the width and height both to 10 pixels followed by a File | Save As
and selecting 24-bit Bitmap
as the type. If you zoom in to maximum it's easy to select pixels individually and Linux and Mac OSX have plenty of applications that can write Windows bitmaps if you're using a different O/S.
Then I created the following Visual C++ command line application to read the bitmap and create a constant array definition. It writes the code to stdout so for example you can invoke it using bitmapconvert test.bmp > test.h
. Note that it makes various assumptions regarding the bitmap size and bit depth so in no way is 'production code' so at some stage you may want to look further into the bitmap file format.
#include "stdafx.h"
#include <stdio.h>
#include <stdint.h>
#define BITMAP_WIDTH 10
#define BITMAP_HEIGHT 10
#pragma pack (1)
struct PIXEL_DEF {
uint8_t B, G, R;
} pixels[BITMAP_HEIGHT][BITMAP_WIDTH];
void read_bitmap_data(FILE *bmp)
{
uint32_t data_offset, curx = 0, cury = 0;
fseek(bmp, 10, SEEK_SET); // Skip to offset to bitmap bits
fread(&data_offset, sizeof(data_offset), 1, bmp);
fseek(bmp, data_offset, SEEK_SET); // Move to start of bits
while (fread(&pixels[cury][curx], sizeof(PIXEL_DEF), 1, bmp))
{
curx++;
if (curx >= BITMAP_WIDTH)
{
// Rows padded to 4 bytes, so we may need to skip some data
fseek(bmp, 32 - curx * sizeof(PIXEL_DEF) % 32, SEEK_CUR);
curx = 0;
cury++;
}
}
fclose(bmp);
}
// Following assumes top-left to bottom-right order, you may need to change
void dump_bitmap()
{
int x, y;
printf("const uint8_t bitmap_data[] = {\n");
for (y=0; y < BITMAP_HEIGHT; y++)
{
for (x=0; x < BITMAP_WIDTH; x++)
{
if (x == 0)
printf("\t");
else
printf(",");
printf("0x%02X,0x%02X,0x%02X", pixels[y][x].R, pixels[y][x].G, pixels[y][x].B);
}
if (y < BITMAP_HEIGHT)
printf(",");
printf("\n");
}
printf("};\n");
}
int _tmain(int argc, TCHAR* argv[])
{
FILE *bmp;
if (argc != 2)
printf("Usage: BitMapConvert infile.bmp");
else {
if (bmp = _wfopen(argv[1], _T("rb")))
{
read_bitmap_data(bmp);
dump_bitmap();
}
}
return 0;
}
The 10 x 10 24-bitmap I used for testing has the following pattern.
The output generated from the above program has been included at the start of following pseudocode that is my interpretation from the datasheet of how data should be sent to the device. The following delays are fow low speed mode, use half the values for high speed (although reset can remain the same). Pin 7 is connected to VDD for low speed, and left disconnected for high-speed so you'll need to check how your hardware is configured. If possible low speed will be easier.
const uint8_t bitmap_data[] = {
0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,
0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,
};
void send_zero()
{
set_DI_high();
delay_us(0.5);
set_DI_low();
delay_us(2);
}
void send_one()
{
set_DI_high();
delay_us(1.2);
set_DI_low();
delay_us(1.3);
}
void send_reset()
{
set_DI_low();
delay_us(50);
set_DI_high();
}
void send_image()
{
uint8_t i;
send_reset();
for (i=0; i < sizeof(bitmap_data); i++)
{
if (bitmap_data[i] & 0x80) send_one() else send_zero();
if (bitmap_data[i] & 0x40) send_one() else send_zero();
if (bitmap_data[i] & 0x20) send_one() else send_zero();
if (bitmap_data[i] & 0x10) send_one() else send_zero();
if (bitmap_data[i] & 0x08) send_one() else send_zero();
if (bitmap_data[i] & 0x04) send_one() else send_zero();
if (bitmap_data[i] & 0x02) send_one() else send_zero();
if (bitmap_data[i] & 0x01) send_one() else send_zero();
}
}
As you can see the protocol is not SPI and one challenge you will have getting the timing right within the +/- 150nS specified by the datasheet which is about 1 cycle at 8MHz. Rather than using any form of delay_us
you'll probably have to use nop
instructions and take into account timing delays caused by setting the ports and function calls etc. Sometimes examining the assembler output of the compiler can be useful in that regard and a scope is always useful to verify it. The datasheet appears to show the same 150nS value for both low and high speed modes. It may be in low speed mode it's a little less critical in practice.
Having said that I just looked at the FastSPI library you mentioned and it does mention support for that device. It appears to use the SPI channel in a non-standard way (not that there's any problem with that) to help alleviate the timing constraints. So a good starting point would be try using that in combination with the above bitmap table.
Best Answer
The LEDs are almost certainly controlled via PWM, so the RGB signals are 12 V PWM.
You can read these into an Arduino (with a slight loss of precision), by low-pass filtering the PWM signal to get an analog voltage between 0 and 12 V, and then adding a voltage divider to scale the 12 V down to a maximum of 5 V so that you can use the Arduino ADC to read the values. The order of the voltage divider and LPF is non-critical, either way works.
However, beware that the impedance of the voltage divider will affect your LPF and vice versa, so you need to either carefully design your circuit, or add a buffer of some sort in between.
One of the simplest ways to avoid this inter-circuit interaction would be to use the 12 V PWM to switch a MOSFET supplied from 5 V, effectively level shifting the 12 V down to 5 V, and then choosing an appropriate RC LPF to adequately smooth the 5 V PWM on the Arduino analog input. You will want to choose your filter cutoff based on both the PWM frequency and your expected response time (how fast you want to detect changes); a larger filter time constant will give you better accuracy at the cost of slower response time.