12 to 8 bit conversion

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello

I am trying to send data from my atmega32 to these
TLC5947 chips from TI.

Basically these are 24 channel PWM led drivers .
The pwm is 12 bit on each channel.
They recieve data serially and work like SIPO shift registers , where you have to raise and lower a latch pin after you've transfered all the data to all the channels.

I've managed to make it work by manually sending data (bitbanging??) through the i/o port and it works fine. That is if I send binary data.

I don't know how I can convert decimal values into binary and then the data bit by bit .
I could probably do it but it would be very inefficient.

Problem:

What if I want to send the data using the AVR SPI ?
The issue is that the SPI sends data in 8 bit chunks and the TLC driver has 12 bit registers for each one of the 24 channels .

Let's say my data is stored in a 24 element long array where the numbers range from 0 to 4096 in decimal (12bits). How would I have my SPI peripheral take that data in chunks of 8 bits and send it?

I would even be satisfied with an 8 bit pwm accuracy but the problem is that the communication is serial and I HAVE to shift all 12 bits to get to the next channel so I can't just send 8 bits and skip to the next channel etc ( this is probably obvious but thought I'd say it anyway)

Would something like this be more efficient to do manually with software(bitbanging?)rather than coverting the data to the SPI?

I really can't think of a way to convert a 12x 24 string of bits into chunks of 8 . Anyone have experience with this?

Thanks in advance

Last Edited: Sun. Sep 1, 2013 - 02:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you align your 12 bit data correctly in your 16 bits you send, all should be well. You may need to left shift 4 bits then split the word into two 8 bit chunks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
If you align your 12 bit data correctly in your 16 bits you send, all should be well. You may need to left shift 4 bits then split the word into two 8 bit chunks.

How can I convert a decimal into a binary and allign it?
Can I just shift a decimal number and expect the compiler to understand ?

1480 << 4 ?

But even If I do that , I'll still be sending 2 chunks of 8 bits and zeros will be shifted into the next channel in the TLC . I can't make the SPI send 8 bits and then only 4 bits and stop can I? So that would make this method invalid .
Or am I missing something?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you pack the 16 channel values of 12 bits each into an 24 byte array, then you just send that array byte by byte. You can do something like this to set a 12 bit value in the array:

// Packed array with 12 bit grayscale channel data
uint8_t tlc5940_data[24];

void tlc5940_set(uint8_t channel, uint16_t value)
{
    if (channel & 1)
    {
        // Odd channel, half first byte on low
        tlc5940_data[channel*3/2] &= 0xF0;
        tlc5940_data[channel*3/2] |= value >> 8;
        tlc5940_data[channel*3/2 + 1] = value;
    } else {
        tlc5940_data[channel*3/2] = value >> 4;
        tlc5940_data[channel*3/2 + 1] &= 0x0F;
        tlc5940_data[channel*3/2 + 1] |= (value << 4);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Snigelen ,

I think you understand what I'm trying to do but
I am having trouble understanding what exactly you did .

Just to make sure that we are on the same boat here :

What needs to happen is that I have an array for example: int tlc5940_data[24];
This array will hold decimal values from 0 to 4096 for all 24 channels (hence why it's 24 elements long)
, and what I'm going to be doing is packing that 24chan - 12 bit array into a 36 channel 8 bit array right?

I could then have the SPI look into the 36 chan 8 bit array and then send the data to the TLC since the TLC doesn't care in how many chunks it recieves the data because it's serial anyway .

Or if there was a way that I could just send the 24 chan 12 bit array bit by bit using software that would be fine aswell .

I am trying to understand the code that you wrote . I think I approximately see what you are doing but not fully . Could you explain in abit more detail if you have the time please?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also , if I was going to go for a "cheaper" solution what I would do is have the tlc5940_data[24] array (grayscale data) in 8 bits and I could send 8 bits with the SPI and then manually shift in four zeros which would make it 12 bit per channel with an 8 bit precision .

I thought about it and I think this would work fine for my purpose , but I'm interested to hear how I could send the full 12 bit resolution

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Just to make sure that we are on the same boat here :
Yes, I just read sloppy and thought we were on a smaller 16 channel boat (tlc5940), but I now see that it's a 24 channel boat. But it's about the same. So either

Use a 36 byte array and store the channel data packed as in my sample above and send the whole array byte by byte.

Or

As you say, have a 24 element array with 2 bytes (at least 12 bits) per element with one channel per element. But then you have to do the packing (as above) when you send the data.

By the way, can you pass me that lifejacket? We're going on deep water now. :-)

And I don't have time to explain my code right now, maybe someone else? :-)
Family is hungry, I have to make dinner now.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
By the way, can you pass me that lifejacket? We're going on deep water now.

This is unfortunately still shallow water :/
Deep water is going to be when I tell you that I am going to want to make function transferable PWM mapping! (if that even makes sense)

So I think I'm going to go with the version where I pack the 24chan 12 bits into 36chan 8 bit and send it with SPI .
I am having trouble understanding your code though :D

I got advised on another forum to do it like this :

 for (i = 0; i < 24; i += 2)
    {
        wait_for_spi();              // Check SPI port and make sure it's clear
        spi_out = (char) (data[i] >> 4);
        wait_for_spi();
        spi_out = (char) (((data[i] << 4) & 0xF0) + ((data[i+1] >> 8) & 0xF));
        wait_for_spi();
        spi_out = (char) (data[i+1]);
    }

It's slightly easier to understand for me , does your code do the same thing as that code?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
And I don't have time to explain my code right now, maybe someone else? Smile
Family is hungry, I have to make dinner now.

Ok sure , no problem .
Thanks for all the help!
I'll be making a tutorial on this on youtube once I finish so newbies don't have to go through the same hell :D
Try not to overcook the steaks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I've settled with

c[i] = x[j] >> 4;
c[i+1] = (x[j] << 4) + (x[j+1] >> 8);
c[i+2] = x[j+1];

As the packing algorithm.

Thanks alot of the help !

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As for decimal values - it all gets stored as binary. The actual number base representation is just for us humans.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok thanks for the info