Go Down

Topic: ShiftPWM support topic. Latest update: Schematics, high power LED's, LED strips (Read 88912 times) previous topic - next topic

elcojacobs

To answer another question:
I did not try 96 shift registers in a chain. With the printInterruptLoad() function I can check that the arduino is able to generate the signals, but I don't know if you would run into problems with long load lines. Maybe a buffer (for example two inverters) in series for the clock line would be required.

elcojacobs

On second thought, I might try SPI. It's just a few changes.
The SPI can output the byte while the avr is already calculating the next byte.
I will probably try this next week.

MexWave

Hi Elco,

Thanks very much for sharing this work!

I've tried the example sketch on a 1280-based mega (you asked if someone could try it with a 1280 in your original post?) and found initially it wouldn't compile. To get it to work I (rightly or wrongly) changed line 90 of "pins_arduino_compile_time.h" from:

Code: [Select]
volatile uint16_t * const port_to_output_PGM_ct[] = {

to:

Code: [Select]
volatile uint8_t * const port_to_output_PGM_ct[] = {

I'm not sure if this was correct, because when I run the sketch all three LEDs in the package (I'm currently only using a single RGB LED) seem to stay at a constant brightness. The circuit I've thrown together uses a single 595 and connects:






Arduino Pin595 Pin
814 (SER)
911 (SRCLK)
1012 (RCLK)


It's all running off 3.3v rather than 5v.

Any ideas?

Many thanks,

Mark

steene

Hey,

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)+7*2+1 = 1+8*6+14+1 = 64 cycles

(I have not tested it so this might not be completely right but I think you get the idea)








elcojacobs


Hi Elco,

Thanks very much for sharing this work!

I've tried the example sketch on a 1280-based mega (you asked if someone could try it with a 1280 in your original post?) and found initially it wouldn't compile. To get it to work I (rightly or wrongly) changed line 90 of "pins_arduino_compile_time.h" from:

Code: [Select]
volatile uint16_t * const port_to_output_PGM_ct[] = {

to:

Code: [Select]
volatile uint8_t * const port_to_output_PGM_ct[] = {

I'm not sure if this was correct, because when I run the sketch all three LEDs in the package (I'm currently only using a single RGB LED) seem to stay at a constant brightness. The circuit I've thrown together uses a single 595 and connects:






Arduino Pin595 Pin
814 (SER)
911 (SRCLK)
1012 (RCLK)


It's all running off 3.3v rather than 5v.

Any ideas?

Many thanks,

Mark


Ah, thanks for trying this. I think the correction you made is right and that it should be unint8.
The problem could come from running on 3.3V. The atmega1280 has to run on 8 MHz. Please be sure that your code reflects this (F_CPU = 8E6). The calculations for the interrupt load have to be done with 8MHz as well. The built in function uses F_CPU for this.

What does the code report on the serial port?

elcojacobs


Hey,

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)+7*2+1 = 1+8*6+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?

elcojacobs

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.

Thanks for posting this Elco, was just working on something similar  8)

whoami

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.

MexWave

Hi Elco,

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?

Thanks,

Mark

retrolefty

Quote
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


ken_zein

#26
Jul 22, 2011, 01:47 am Last Edit: Jul 22, 2011, 04:05 am by ken_zein Reason: 1
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.)?

Hernandi

:) 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?

whoami

try below. I use in my application

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

   pwm++;                        // increment pwm
   if (pwm >= MAXBRIGHTNESS)
      pwm=0;                     // reset pwm
}

Hernandi

I think you can change > by XOR and maybe save some CPU cicles
like
     if (rgbPWM[i+0] XOR pwm) d |= 0x80; // means rgbPWM[i+0] == pwm

But you need to use the start cicle to reset all pins.(count 0)
and save 'd' out between ISR_Calls

Go Up