12-bit PWM for LED lighting

I tried your suggestions, (even downloaded ide 1.6.1 to try it) but the results are exactly the same... Furthermore I can t control a led with the other librares i found, I can t seem to access the PWM of the Led, it s not clear which function to use.. (i miss the simple analogWrite command :stuck_out_tongue: )

I m starting to think that it was not such a good idea to buy this board.

maybe if I could change the prescaler here, so that I have the desired frequency (100-1000 hz) it would do the job for me. (even though I have read forums and the datasheet and tried to change it myself, I am still missing something, some help here would be really appreciated! )

GGE5:
TCCR1A = (1 << COM1A1) | (1 << WGM11); // Enable Fast PWM on OC1A (Pin 9)
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); // Mode 14 Fast PWM/ (TOP = ICR1), pre-scale = 1

This Brunnels library works perfectly here, so I suggest you keep trying.

The PWM frequency is set/stored inside the PCA9685, so don't change PWM frequencies of the Arduino pins.
They have nothing to do with each other.

I think you're using the wrong commands.
You have to use the new PCA9685 library commands.
Here are some.

whiteDrv.setFrequency(200); // without the PWM

whiteDrv.setChannels8bit(0, 15, 0); // turns everything on the "whiteDrv" board off

whiteDrv.setChannel8bit(3, 255); // sets output four (ch 3) to maximum

whiteDrv.setChannels8bit(8, 11, 200); // sets output 9 to 12 to level 200

Note the Channel (one output) and Channels (range of outputs)

The 8bit commands (value 0-255) use the 12bit log lighting lookup table.
There are also direct 12bit commands. And RGB commands.
You can also read the value of a channel, e.g to feed/update a web page, or print to serial monitor.
I never used this, but I think it's "whiteDrv.getChannel()
Hope this helps.
Leo..

Yes, it helped a lot! Finally, I got to control it somehow (but I dont understand why the test code of the library had wrong commands :confused: ) When I set a single value for the led, it is ok, it is bright and continuous. But when I try and use it in my project, where it needs to read a value from an ultrasonic sensor and map the distance to brightness, and THEN light the led accordingly, it ends up blinking the led, and not even at the correct brightness, it is really faint.

What I understand is that the arduino sends "data" to the board every time the code loops, and that causes the blinking effect ( but I still have know clue why it is really faint.. )

Is there any way I can overcome this and make it nice and continuous, without blinking?

Thanks again for your help and patience... :slight_smile:

At some stage the Brunnels library got updated, and several key commands got changed.
Don't know why, but there must have been a reason.

Did you try the short code from post #19 with the changes.
Did that work.

The Arduino writes a command to the PCA9685 board.
After that, the Arduino is not needed anymore untill the next command.

Post your new code (use code tags).
I'm not a good programmer, but maybe we can figure it out.
Leo..

Ok, finally got it to work as expected!! ( though I still cannot understand what I was doing wrong and created the blinking effect..) Anyway, these are great news!

I have one last question, and I wont bother you again :stuck_out_tongue:

I notice inside the library that there is also a lookup table with 1024 values

//lookup table for 1024 CIE lab brightness corrected values with 12 bit resolution
const uint16_t CIEL_10_12[] PROGMEM = {
  0, 0, 1, 1...

Is it possible to access it and use it, like you use the 256 lookup table with the 8bit commands?

Good to hear you got it sorted.

Just looking through the library, and I can only find 10-bit RGB commands.

Don't know the answer to that one.
Maybe someone with experience in libraries can chime in.
Leo..

PCA9685.zip (5.34 KB)

Dear GGE5

Thanks. This code worked well for me. Thanks for sharing.

GGE5:
I modified your code a bit to work. I had to set the pinMode of 9 to output before anything else. I had to erase your line setting TCCR4C because there is no Timer4 on my ATMEGA328 (and it was throwing an error), but also because TCCRxC is only active in non-PWM modes.

Also, when using C code, you don't have to set the HIGH and LOW registers separately, as the compiler takes care of that for you.

Here is the working code:

//12-Bit PWM on PIN9 using direct access to Timer1

//Fade in and out an LED connected to that pin

#define LEDValue OCR1A

const int PWMMax = 4095;
int value = 1;
int direction = 1;

void setup() {
  pinMode(9,OUTPUT);
 
  TCCR1A = (1 << COM1A1) | (1 << WGM11);                // Enable Fast PWM on OC1A (Pin 9)
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);  // Mode 14 Fast PWM/ (TOP = ICR1), pre-scale = 1
 
  ICR1 = PWMMax;  //Set the TOP value for 12-bit PWM
  LEDValue = 0;      //Set the PWM output to full off.
}

void loop() {
  //Fade the LED between 0 and PWMMax and then back to 0
  value += direction;
  if (value <=0){
    direction = 1;
  }
  else if (value >= PWMMax){
    direction = -1;
  }
  LEDValue = value;
  delay(1);
}




Thank you so much for your help!

Thanks for all that contributed to this topic! I got a really nice fade in / fade out combining the information from this topic with the information from this site.

This is my test code:

//12-Bit PWM on PIN9 using direct access to Timer1
//Fade in and out an LED connected to that pin

#define LEDValue OCR1A

const int PWMMax = 4095;

// table generated using code from: http://jared.geek.nz/2013/feb/linear-led-pwm
const unsigned int cie[256] = {
   0,    2,    4,    5,    7,    9,   11,   12,   14,   16,   18,   20,   21,   23,   25,   27,
  28,   30,   32,   34,   36,   37,   39,   41,   43,   45,   47,   49,   52,   54,   56,   59,
  61,   64,   66,   69,   72,   75,   77,   80,   83,   87,   90,   93,   96,  100,  103,  107,
 111,  115,  118,  122,  126,  131,  135,  139,  144,  148,  153,  157,  162,  167,  172,  177,
 182,  187,  193,  198,  204,  209,  215,  221,  227,  233,  239,  246,  252,  259,  265,  272,
 279,  286,  293,  300,  308,  315,  323,  330,  338,  346,  354,  362,  371,  379,  388,  396,
 405,  414,  423,  432,  442,  451,  461,  470,  480,  490,  501,  511,  521,  532,  543,  553,
 564,  576,  587,  598,  610,  622,  634,  646,  658,  670,  683,  695,  708,  721,  734,  748,
 761,  775,  788,  802,  816,  831,  845,  860,  874,  889,  904,  920,  935,  951,  966,  982,
 999, 1015, 1031, 1048, 1065, 1082, 1099, 1116, 1134, 1152, 1170, 1188, 1206, 1224, 1243, 1262,
1281, 1300, 1320, 1339, 1359, 1379, 1399, 1420, 1440, 1461, 1482, 1503, 1525, 1546, 1568, 1590,
1612, 1635, 1657, 1680, 1703, 1726, 1750, 1774, 1797, 1822, 1846, 1870, 1895, 1920, 1945, 1971,
1996, 2022, 2048, 2074, 2101, 2128, 2155, 2182, 2209, 2237, 2265, 2293, 2321, 2350, 2378, 2407,
2437, 2466, 2496, 2526, 2556, 2587, 2617, 2648, 2679, 2711, 2743, 2774, 2807, 2839, 2872, 2905,
2938, 2971, 3005, 3039, 3073, 3107, 3142, 3177, 3212, 3248, 3283, 3319, 3356, 3392, 3429, 3466,
3503, 3541, 3578, 3617, 3655, 3694, 3732, 3772, 3811, 3851, 3891, 3931, 3972, 4012, 4054, 4095
};

void setup() { 
  pinMode(9, OUTPUT);
  
  TCCR1A = (1 << COM1A1) | (1 << WGM11);                // Enable Fast PWM on OC1A (Pin 9)
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);   // Mode 14 Fast PWM/ (TOP = ICR1), pre-scale = 1
  
  ICR1 = PWMMax;  //Set the TOP value for 12-bit PWM
  LEDValue = 0;   //Set the PWM output to full off.
}

void loop() {
  cieFadeIn();
  led(0);
  delay(1000);

  cieFadeOut();
  delay(2000);
}

void cieFadeIn()
{
  for(int i = 0; i <= 255; i++)
  {
    led(i);
    delay(75);
  }
}

void cieFadeOut()
{
  for(int i = 255; i >= 0; i--)
  {
    led(i);
    delay(75);
  }
}

void led(int brightness)
{
  LEDValue = cie[brightness];
}

Hi,
Thank you very much for this contribution.

I am doing a similar project for the regulation of LEDs at 12 bits of resolution and with a frequency 3.9KHz. This works perfectly with Arduino Leonardo using timer1 and output 9.

But in my specific case I need to use 2 outputs with these same characteristics, pin 9 and pin 10, which correspond to the same timer1, but to regulate independently the light intensity of each PWM output.
I've been reading a lot about the records and I don't know if I'm doing well.
Could you help me with this?

Thank you!!!!

But in my specific case I need to use 2 outputs with these same characteristics, pin 9 and pin 10, which correspond to the same timer1, but to regulate independently the light intensity of each PWM output.

The code in reply#27 (and also in several other earlier postings) uses Timer1 in Mode 14, fast PWM with ICR1 as the top value.

You should be able to use that mode and have use of both timer output pins. What have you tried?

Thank you very much for your answer cattledog.
Sorry to take so long to respond. The coronavirus situation has left us out of the office and now that I return to work I find myself with pending work. :sweat_smile:
Unfortunately I am not familiar with modifying registers and do not understand much of their specific functions. I have relied on copying the parameters to do this test program and I don't get the expected result.
When you say "use Timer1 in Mode 14, fast PWM with ICR1 as the top value", I don't quite understand what I should be doing.
I would appreciate if possible if you could tell me what I have to change.
Thank you very much again.

//12-Bit PWM on PIN9 using direct access to Timer1
//Fade in and out an LED connected to that pin

#define LEDValueWarm OCR1A  
#define LEDValueCool OCR1B

const int PWMMax = 4095;
int value = 1;
int direction = 1;

void setup() { 
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  
// va bien mas o menos
  TCCR1A = (1 << COM1A1) |(1 << COM1B1) | (1 << WGM11);                // Enable Fast PWM on OC1A (Pin 9 warm) and in OC1B (Pin 10 cool)
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);   // Mode 14 Fast PWM/ (TOP = ICR1), pre-scale = 1
    

  ICR1 = PWMMax;  //Set the TOP value for 12-bit PWM
  LEDValueCool = 0;      //Set the PWM output to full off.
  LEDValueWarm = 0;      //Set the PWM output to full off.
}

void loop() {
  //Fade the LED between 0 and PWMMax and then back to 0
  value += direction;
  if (value <=0){
    direction = 1;
  }
  else if (value >= PWMMax){
    direction = -1;
  }
  LEDValueWarm = value;
  LEDValueCool = value;
  delay(25);
}

I have relied on copying the parameters to do this test program and I don't get the expected result.

Please explain exactly what is the issue using the code you posted. What is expected, and what do you see?

I was convinced that one of the outputs did not do what I expected, since one Led turned on and it turned out at percentages notably different than the other led. I've checked all the electronics to describe you the problem well, and... hey! there was the problem! a damaged component was the cause.
I am delighted to know that I can successfully complete my project.

Thanks to this forum and your help. :wink:

Very interesting read. I have 1 question though. The author was dimming from 0 to 100% and vice versa. I am interested in the look up table but would like to know how to make the look up table work if say I decide the led should only dim up and down from 40%

Proietti:
Very interesting read. I have 1 question though. The author was dimming from 0 to 100% and vice versa. I am interested in the look up table but would like to know how to make the look up table work if say I decide the led should only dim up and down from 40%

Easy. Find the 40% output threshold in the table and don't exceed that table index.

Not that easy in my case. I have a Aquarium light where there are 4 inputs. I am using an Adafruit 16Channel PWM driver with the frequency set at 1500 hertz

1/ Time when light come on and when light goes off.
2/ Time in seconds for fade up
3/ Time in seconds for fade down
4/ Max intensity of the LED

Code of the LED function

void SetLed(int fpin,                             //Master channel pin
            int fpin2,                                 //Slave channel pin
            unsigned long ftime,                //Current time in millis
            unsigned long fstart,               //Start time of LED channel
            unsigned long ffadeup,             //Fadeup period in millis
            unsigned long fperiod,             //Total photo period
            unsigned long ffadedown,        //Fadedown period in millis
            unsigned long fstop,                //Stop time of LED channel
            float fpwm,                             //Max value of brightness
            int Vpin)                                 //Progress bar pin
{
  //ON PERIOD//
  if (ftime > fstart + ffadeup && ftime <= fstart + fperiod - ffadedown)
  {
    pwm.setPin(fpin, fpwm, false);
    pwm.setPin(fpin2, fpwm, false);
    progress = round((fpwm*100)/4096);
    Blynk.virtualWrite (Vpin, progress);
  }
  //FADEUP PERIOD//
  else if (ftime > fstart && ftime <= fstart + ffadeup)
  {
    brightness = map(ftime - fstart, 0, ffadeup, 0, fpwm);
    pwm.setPin(fpin, brightness, false);
    pwm.setPin(fpin2, brightness, false);
    progress = round((fpwm*100)/4096);
    Blynk.virtualWrite (Vpin, progress);
  }
  //FADEDOWN PERIOD//
  else if (ftime > fstart + fperiod - ffadedown && ftime <= fstart + fperiod)
  {
    brightness = map(ftime - fstart - fperiod + ffadedown, 0, ffadedown, fpwm, 0);
    pwm.setPin(fpin, brightness, false);
    pwm.setPin(fpin2, brightness, false);
    progress = round((fpwm*100)/4096);
    Blynk.virtualWrite (Vpin, progress);
  }
  //OFF PERIOD//
  else if (ftime > fstop || ftime < fstart)
  {
    pwm.setPin(fpin, 0, false);
    pwm.setPin(fpin2, 0, false);
    Blynk.virtualWrite(Vpin, 0);
  }
}

It works OK except as expected at low levels the LED will dim to a point then just switch off. How could I incorporate the look up table into my code above?

I assume you use drivers between the PCA9695 and your aquarium lights.
They have PWM frequency limitations. Are you sure they like 1500Hz.
Too high could mean not dimming to very low light levels.
Did you first try the default 200Hz.

Your code is incomplete. Do you use a library for that PCA9685.
I use this one, with an equal dim step lookup table designed for LEDs,
which then makes setting dim level (in 8-bit steps) to 40% easy.

You should have started your own thread.
Can still ask a moderator to split it.
Leo..

Hi,
I return to this issue of Light control using 12-bit pwm and almost 4Khz. This is working perfectly on both outputs 9 and 10 via timer 1.
Now I am expanding the programming by enabling a serial port by software, in my case with the <SoftwareSerial.h> library to communicate with other devices.
I am using a 32u4 processor, like the one in the Arduino Leonardo, and the serial1 port is already occupied by a Bluetooth rs232 module.
The problem I find is that when I add the <SoftwareSerial.h> library, the program stops working. It seems to me that there is some conflict. Could it be that I am modifying Timmer 1 and the <SoftwareSerial.h> library is modifying it too? I can not think of anything else.
Anyone have any ideas how to fix this?

Thank you!

The problem I find is that when I add the <SoftwareSerial.h> library, the program stops working.

What pins are you using for Software Serial on the Leonardo?
Not all pins on the Leonardo and support change interrupts, so only the following can be used for RX: 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI).

Does the program stop working if you are not actually sending or receiving but if you just include the library and call .begin()?

Your pwm output is indeed using Timer1, but not any timer interrupts. Software serial disables interrupts on sending.

You may want to take a look at AltSoftSerial which uses pins 5 for TX and 13 for RX.

Thanks cattledog,
I am testing with different pins but I think that there is not the problem, since I have done the following:
I have configured the SoftwareSerial like this: SoftwareSerial mySerial (11, 12); // RX, TX
I have tried the program and it does not work,
I have made a small program where there are no Timmer 1 settings that outputs the PWM signals from pins 9 and 10 at 12bits and a maximum of 4096 resolution steps. So this little program works fine. I understand that the configuration of pins 11 for RX and 12 for TX would be fine.
So I still think there is some interference between the timmer 1 prescaling and the SoftwareSerial.
Also add that I am using the RTC <DS3231_Simple.h> which is connected by I2C.
Any ideas?
So I still think there is some interference between the timmer 1 prescaling and the SoftwareSerial.
Also add that I am using the RTC <DS3231_Simple.h> which is connected by I2C.
Any ideas?