# How to use ATmega328 SPI with a 31 bit shift register

I'm using an Allegro 6280 PWM LED driver for a project. This is a cool little IC except for the fact that it is a 31 bit long input register. This is no problem if I simply loop through my 31 bits and set the pin states accordingly, but I'm wanting to use the atmega's on-board SPI to speed things up a bit (the 6280 requires an external clock source for it's 10 bit long PWM cycle).

The 31 bit long register looks like this in my application where R,G & B indicate the 10 bit PWM values for the attached LEDs.

0BBBBBBBBBBGGGGGGGGGGRRRRRRRRRR


These shift registers are chainable, so I've built a linked list to represent them:

struct Pixel {
uint16_t red;
uint16_t green;
uint16_t blue;
Pixel *next;
};


So, the idea is to set the latch pin low, iterate through all the Pixels and then after sending the last one set the latch high to latch the data into each register. This would seem easy except that due to the uneven length of the register I have to construct nasty-looking code to be able to loop around to the beginning again so that it never sends any empty bits. Also, the latch might need to happen after an arbitrary number of bits are transmitted, not just the last one.

Does anyone have any ideas on how to get this done, or am I simply barking up the wrong tree and stuck with manually twiddling the pin states?

Thanks.

I've been monitoring this question, and since no one has answered yet, I'm going to take a stab at it. I apologize in advance if my reasoning ends up being flawed, but I'll do my best.

I think the possibility of making your project work depends upon how many RGB LEDs you plan on using. I've been going over the Allegro datasheet, and it looks like you need to pair one IC with one each of red, green, and blue LEDs (i.e. one RGB pixel). All of the data gets transmitted down the chain of 6280s.

If you want to only have one RGB pixel, or more specifically, N pixels displaying the same temporal information, then I think you might be able to get away with using SPI. I think your linked list idea is the right way to go, but obviously the key is to latch LI at the right time (every 31 bits), and it won't be on 8 bit boundaries. The only SPI libraries I've ever used on micros take a byte, clock out the data and read the response byte. In your case, you'll need to figure out how to make an SPI function that can trigger LI during a byte transfer.

Since SPI requires transferring one byte at a time, you'll be forced to send 32 bits when you only want 31. So the 32nd bit will actually be the first bit of your second set of RGB data. You'll set LI before clocking out the 32nd bit. For the next round of data, you'll trigger LI before clocking out the 31st bit. You might be able to do this without writing your own SPI library, but I'm not sure.

If you want to support scrolling data, then things look like they will be a bit trickier. I'm having a hard time formulating the explanation, but timing LI will be interesting. LI determines how quickly your display is going to scroll, since it dictates when one 6280 outputs the data to the next.

For the first set of RGB data, you will trigger LI as soon as you've clocked out 31 bits. You've also clocked out 1 bit for the next set of data. You will then clock out another 3 bytes of data, plus 6 more before triggering LI. But if your scroll time is slow, you can't send out the byte that contains the last 6 bits until you are ready to trigger LI, because you don't want to overflow the shift register buffer! But once you are ready to trigger LI, you'll send out this byte. Now 2 bits for the next set of data is already in the serial buffer. Send out another 3 bytes (26 bits total), and wait until you need to trigger LI. When it is time, send out the next byte (that has the remaining 5 bits). Repeat this over and over again.

I hope this makes sense. Like I said, I don't have experience with this chip, but what I've written here is at least the first step I'd take to trying to solve this problem using SPI.

Good luck, and keep us posted!

• Hi Dave. THanks for that. You've pretty much summarised exactly where I'm at with the problem. The nub of the issue for me seems to be that you dump your byte into SPDR and then wait for SPSR and SPIF to indicate that transmission is complete, as I understand it that's all the resolution you get. – jamesotron May 12 '11 at 21:54
• @jamesotron: I see. My only other suggestion would have been to tie data out to an input pin, and then have an ISR toggle LI any time a counter % 31 == 0. But if you're going through that hassle, that defeats the point of using SPI -- you might as well just bitbang an output pin. I actually don't see why SPI is any more convenient. You're forced to jump through hoops that, in the end, result in less manageable code... and you haven't gained any I/O by using SPI. – Dave May 13 '11 at 0:57
• mainly I'm trying to get a speed gain by using SPI because the MCU will handle clock generation and things faster than I can do it in software. – jamesotron May 13 '11 at 1:06
• @jamesotron but it's not like you need things clocked precisely. The beauty of something like SPI is that the data is clocked by edge transitions, but it's not like RS232, where the timing needs to be precise. You should be able to clock out the 31 bits as fast as you can go. Reset LI, loop over each bit and strobe CI each time, set LI. Maybe I'm missing something, but it seems that such an approach will work fine. – Dave May 13 '11 at 5:04
• Okay. I had an idea. Because it's a shift register I can essentially just keep shifting in zeros to drive the clock as long as I only latch when actually sending data. If I create a buffer long enough to contain however many registers I have in the chain and make sure that the data is aligned with the right byte boundary of the last byte I can shift out those bytes and latch at the end and then just go on shifting zeros - at least that's my guess. I guess I'll test it tonight. – jamesotron May 23 '11 at 21:55

As it turns out the way to do this is simply to overflow the shift register with more bits than it needs and make sure that the 31 important ones are the LAST ONES out. I create a buffer long enough to hold 32 bits:

static uint8_t[BUFFER_SIZE] spiBuffer;


I populate it whenever there is a change and use the interrupt driven SPI to transmit it:

void transmitSpiBuffer() {
static uint8_t c = 0;
SPDR=spiBuffer[c++];
if (c == (BUFFER_SIZE)) {
c = 0;
digitalWrite(LATCH_PIN, HIGH);
// should I sleep? nah.
digitalWrite(LATCH_PIN, LOW);
}
}

void setup() {
// Enable MOSI and SCK as output, all others input.
PORTB = (1<<PINB3)|(1<<PINB5);

ISR(SPI_STC_vect) {
transmitSpiBuffer();
}

// Enable SPI
SPCR = (1<<SPIE)|(1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
}


This works freaking excellently for me. Problem solved.