I think you could do it much faster with SPI.
There's a little trick when calculating the bitvalues
You can calc every bit with only 5 cycles and without branching
you can just use the carry flag result from the cp instruction and shift it to a register, like this:
ldd value
cp value, counter
rol register
(I think the carry flag is set when the counter is greater than the value, so this is exactly what you need for pwm)
So after executing this 8 times you got 8 values packed to one byte. you can now send this with the spi.
when you put your PWM Values in an array you can generate a whole byte with this code:
ldi r20, 8
label:
ld r21, X+
cp r21, counter
rol r22
dec r20
brne label
at this point you have a calculated a whole byte (in reg22) with 1+8*(2+2+1+1)+72+1 = 1+86+14+1 = 64 cycles
(I have not tested it so this might not be completely right but I think you get the idea)
Very nice way to do it. I don't like to use assembly directly in my code, so any idea which c statement would result in a rol instruction?
There are more issues with using a 1280. Because the memory is 16 bit wide, extra instructions are added. I can only look at the assembly now, but I'll try to get my hands on a 1280.
Thank for share.
I currently do something like this but in avr studio. using tpic6b595 rather than 74hc595.
please check adafruit HL1606-LED stripPWM. HL1606 use SPI way like 74hc595.
I've #defined F_CPU to 8E6, rebuilt and re-uploaded and have exactly the same result, so it appears you're right that there's something else
You asked for the output from the serial monitor:
Load of interrupt: 0.1252302646
Clock cycles per interrupt: 264.51
Interrupt frequency: 7575.00 Hz
PWM frequency: 75.00 Hz
Timer1 in use for highest precision.
Include servo.h to use timer2.
OCR1A: 2111
Prescaler: 1
The output was identical whether I #defined F_CPU or not.
I also tried including servo.h to see what difference it made (hey, I'm clutching at straws ) and got the following:
Load of interrupt: 0.1250287890
Clock cycles per interrupt: 264.09
Interrupt frequency: 7575.00 Hz
PWM frequency: 75.00 Hz
Timer2 in use, because Timer1 is used by servo library.
OCR2A: 65
Presclaler: 32
Again, it was identical whether or not I #defined F_CPU.
Is there any more information I can supply that would be useful or interesting? I also have some TLC5940s sat around if you're interested in any kind of comparison?
There are more issues with using a 1280. Because the memory is 16 bit wide, extra instructions are added. I can only look at the assembly now, but I'll try to get my hands on a 1280.
Hum, I think you have a misconception about the 1280 Vs 328 chips. Both have the same number of instructions, same memory widths. 1280 just has more memory size in length, not width for flash, sram, eeprom, more I/O pins, more timers, more serial ports, etc. But both use the same AVR 8 bit instruction set.
First of all, AWESOME!!! You saved me tons of work, I just started working on a project like this one. I tried the example on 10 RGB LEDs with 4 registers, and I encounter some serious flickering on two of my LEDs, and two others (the ones connected to the last register) don't seem to work at all. They go on and off at random, and always all the inputs go at the same time and at full power. Has anyone encountered anything similar? Any ideas? Also, the serial monitor always says the load is 0.
UPDATE: I figured out the two that don't work, it was a faulty jumper wire. That seems to have cured the flickering as well, although maybe not completely.
Also, I can look up the assembly language by checking the datasheet, but where can I learn about using C (libraries, functions, keywords, etc.)?
You can do bit shift in C with >> and << operator!
int X;
X = 0x1; // hex
X = X << 2; // move bits 2 places left
or
X = X >> 7; // moves bits 7 bits right
But I really think you can just use XOR and AND instructions togheter to achive bit change for each one of 8 bits inside the serialized word. I use this a lot with PIC (sorry, I used to program in ASM pic and now using arduino too)
You can use A,B,C,D as variables that control each bit time (like on time).
and aa,bb,cc,dd as as timer variable for each pin
A,B,C and aa,bb,cc may be nibbles, this way you can store 2 information peer byte.
byte counter;
if(counter == 0){
resetAllPins and send out();
fillZeroAll aa,bb,cc,dd counters;
send_ZERO_DataToSPI and SET START BIT at SPI
}else{
(aa XOR counter)? out = out AND aaPinBit // if they are equal, them result is ZERO and so i change the pinBit
(bb XOR counter)? out = out AND bbPinBit //
sendDataToSPI and SET START BIT at SPI
}
counter ++;
if (counter >100) counter =0; // reset counter - I supose use 100 levels for each PWM out, but you can use less; that depend of
// int frequency and what you want
Surely you have to create a for loop to change all the 8 bits
Also you have to create a simple way to generate aaPinBit, bbPinBit, ccPinBit, and so on, but
there are some elegant way to do this.
I this you can achive good results.
You maybe dont need to use BIT ROT operators (>> and <<)
Pehaps to create the __PinBin you should use (>> <<);
but i supose that will be better to use some pre-calculated data that create __PinBit and take care of witch bit you are using with reference to witch of the ~500 out that you are testing.
YOu can achive great performance with ASM. Dont get woried about it.
It is easy after you get in touch. And you will enjoy the results!
Sorry my poor english.
Good luck.
I realy delight with your work.
With is you blog or page?
ISR(TIMER0_COMPA_vect)
{
uint8_t i = 0, d;
// write out data out
i=0;
for (i=0; i< sizeof(rgbPWM); i+= 2*3) // device by 2 RGB of each 74HC595 and RGB 3 LED 1 free port
{
d = 0;
if (rgbPWM[i+0] > pwm) d |= 0x80;
if (rgbPWM[i+1] > pwm) d |= 0x40;
if (rgbPWM[i+2] > pwm) d |= 0x20;
if (rgbPWM[i+3] > pwm) d |= 0x08;
if (rgbPWM[i+4] > pwm) d |= 0x04;
if (rgbPWM[i+5] > pwm) d |= 0x02;
while(!(SPSR & _BV(SPIF))); // check that previous completed
SPDR = d; // send new data
}
while(!(SPSR & _BV(SPIF))); // make sure completed
// latch
LOAD_PORT |= (1<<LOAD_BIT); // set 1
LOAD_PORT &= ~(1<<LOAD_BIT); // set 1->0
There are more issues with using a 1280. Because the memory is 16 bit wide, extra instructions are added. I can only look at the assembly now, but I'll try to get my hands on a 1280.
Hum, I think you have a misconception about the 1280 Vs 328 chips. Both have the same number of instructions, same memory widths. 1280 just has more memory size in length, not width for flash, sram, eeprom, more I/O pins, more timers, more serial ports, etc. But both use the same AVR 8 bit instruction set.
Lefty
You are right. The addresses can be 16 bit, because the memory is longer. I was confusing the data and the addresses.
I spent a while yesterday trying to interface with a PCA9685 to get reliable PWM output and was getting nowhere, but your library is just what I need! It's also easier to manage 8-channel output boards for my project since I need 24 channels and want to use pretty big MOSFETs.
I wonder: is there a magic multiplier (or divisor) for the PWM frequency? I'd like to go faster than 75Hz to avoid artifacting when in motion, but tried 150Hz in the test sketch and had odd flickering, not fading.
Can you be a bit more specific? then I'll try to help you.
A schematic is easier to read than the board file
What are you pwming? Big LED's? Motors?
Are the shift registers running at 5v? (big mosfets often need a bit more (a small mosfet to switch the bigger mosfet).
It seems you have found a bug, thanks!
Up to 127Hz, 256 levels, 6 shift registers its all good. 128 Hz, 256 levels -> crap.
When the interrupt frequency exceeds 32768 (max of int), a variable overflows and the interrupt frequency is set to a way to low value.
I put together a test sketch on a breadboard with two 74HC595's driving 16 red LED's-- worked great. I removed the RGB calls since I was just using red LED's and it worked fine.
Then I tried changing the frequency from 75 to 150, and it didn't fade, but instead flashed the LED's in an odd pattern. Putting it back to 75, it worked great.
Should I be able to change the frequency?
The board is just for my application, sending the 595's outputs to N-channel MOSFETs to drive LED strips, not specific to ShiftPWM. The 595 is running at 5V but I might want to run it at 3.3V later.
Update: I posted about my successful test on my blog along with links to design files for the shift register MOSFET board. THANK YOU ELCO!!