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!
Well let's look at your VHDL code. Please keep in mind that I'm not trying to be critical or harsh-- I'm just trying to get you up to speed quickly. Your code is currently this:
process (C, ALOAD)
begin
if (ALOAD='1') then
tmp <= "10000001";
elsif (C'event and C='1') then
tmp <= tmp(6 downto 0) & '0';
SO <= tmp(7);
end if;
end process;
But should look like this:
process (C, ALOAD)
begin
if ALOAD='1' then
tmp <= "10000001";
elsif rising_edge(C) then
tmp <= tmp(tmp'high-1 downto tmp'low) & "0"; -- '0' and "0" are equivalent in this context
end if;
end process;
SO <= tmp(tmp'high);
The changes I made are minor, for sure, but important.
The first thing is my use of "rising_edge()". This is a new-ish thing to VHDL and isn't covered in some of the VHDL books. This is considered to be better than using 'event. Likewise, there is a falling_edge().
The next thing is my use of 'high and 'low instead of hard-coding the values. This really doesn't matter for this code, but when you start doing bigger things then these will help you a lot. For example, you could just change the definition of tmp to be bigger and the rest of the code will just automatically adjust (except for the initialization to "10000001").
I should also point out that an async reset or load in FPGAs is discouraged, but is fine in CPLDs.
Also note that I moved the assignment of SO to outside the process. This may or may not be what you intended. The way you have it there is an extra flip-flop going from tmp(7) to SO. Normally, with SPI, this isn't want you want because the SPI Clock could go away at the end of the transfer and you'll never get that last bit out. On the other hand, with the way that I did it you'll start getting that first bit out when ALOAD='1', not at the rising edge of C.
Unfortunately, this doesn't really answer your question on why you're getting bad data into the AVR. There just isn't enough information in your question. Here's the kinds of things I would be looking at, or be concerned about:
Your o-scope pic doesn't show the other spi signals, like CS. CS is critical to understanding where the bits are supposed to go. Also, knowing what the SPI mode is will help with this.
On the o-scope pic, the first clock cycle where SO='1' is NOT the first bit of the SPI transfer. You loaded the shift register 10-20 mS before that, and your clock period is about 20 uS. So you had at least 1 clock cycle before SO='1', and probably more. So there is some weird stuff going on here-- we don't have enough info to understand the behavior.
You're using ALOAD to load the shift register, but normally you'd use CS_N (active low). While CS_N='1' you do an async load of the shift register, while CS_N='0' you shift it out. Using ALOAD like you have here is OK, but probably not what you wanted and doesn't work with what appears to be some strange SPI clock stuff (from the previous point).
So, here's what you should do...
Clean up the VHDL a bit. Repost the updated version. Since your scope only has 2 channels, hook up one channel to CS_N and the other channel to CLK. Trigger on the falling edge of CLK. Capture a waveform showing 2 clocks before the falling edge of CLK and 5 clocks after. Without changing the settings on the scope, remove CLK and put the probe on SO. Capture another image. Do this again for the rising edge of CLK. So, 4 waveform images total.
Do that and we can re-evaluate what might be wrong.
Edit: Updated to reflect the updated question.
I see two issues: First, if the AVR is sampling on the rising clk edge, you should clock your shift register off of the falling edge. As supercat mentioned, this will give you +/- 0.5 clock periods of setup & hold going into your AVR.
And Second: As you mentioned, you're getting 10000010 instead of 10000001. I do not believe that your VHDL code is in error on this one, but it is obviously coming out of the CPLD wrong. If I had to guess, I would guess that the problem is with some signal integrity issues with your CLK. It's hard to tell with your scope, but it looks like you have over a volt of overshoot and undershoot on that signal (and with that would come a lot of ringing). That ringing, if bad enough, could cause the CPLD to "double clock"-- meaning run the shift register twice for a single clock edge. And if _really_bad_ it could cause the CPLD to latch up and literally explode (I've seen it happen).
Here's some experiments to try:
Instead of 10000001, use 10101010 or 01010101. This will help you see which bit is getting double clocked, and if it is always the same bit.
Zoom in on the clock edges with the scope. Make sure that your scope probes are at the CPLD, not the AVR, when you do this. Yes, it makes a HUGE difference.
Assuming that it is over/undershoot and ringing on the clock, the solution is to add proper signal termination on the line. I would start with a 50 ohm series resistor at the AVR. Note: this will slow down the clock edges, but since you are clocking the CPLD on the falling edge you have a lot of time available.
How is the clock ran from AVR to CPLD? A long wire between PCB's? That would be my guess.
Best Answer
Some issues:
Probably because the DDR register is input as default, so that if you don't set it to outputs, the pins are left in a floating state. If it is important that this never happens, you should consider adding external pull resistors on these pins, to always hold them in a known state.
According to the data sheet of 74HC595: "±6-mA Output Drive at 5 V". You are driving 5/220 = 22mA per pin. 22mA * 8 = 181 mA. This is well beyond the absolute maximum ratings of the IC. It is pretty safe to assume that you have fried the chip.
Consider adding some buffer IC with more drive strength in between the shift register and the LEDs. Quad channel MOSFET driver or similar.
Bare metal microcontroller applications ("freestanding") do not return from
main()
. Return to where? Should you return frommain()
you program is pretty much guaranteed to go completely haywire. You always need an eternal loop such asfor(;;)
. The correct form of main is probablyvoid main (void)
, consult your compiler manual.Some general things: