I am working on a project that flashes a single common cathode RGB LED on a small PCB that is battery-powered. I am having an issue where my LED doesn't turn off fully when the R, G, and B values are set to 0. There remains a dim yellow light, so a little red and green.
When using the Arduino functions I do not get this behavior.
The current code example I show below doesn't contain any Arduino library functions.
I'm doing this because I want to save space on my chip for more features.
So my question is what could I be doing wrong that I am getting a signal from the Red and Green pins even when they are set to 0
Disable PWM'ing if == 0 ? (i.e. rainbow[num])
I have a ESP32 with an on-board RGB, but analogWrite(0) doesn't turn it full off (it has to be digitalWritten LOW).
Why are you setting the Force Output on Compare bits?
Without comments in the code, it is impossible to understand what you are trying to do with these settings, but they are not standard PWM settings.
I suggest that you get a simple PWM operation running properly with one LED, on a single timer channel, first on Timer0, then on Timer1, before trying to mix the two timers together in the final application.
I am very new to Port Manipulation, but my goal is to use 3 PWM Pins to control my RGB LED.
For the piece of code you are referring to I believe I am using it set the Timer1 to handle the PWM for PB4.
Again, this is me trying to piece things together that I have found.
Recommend you read the datasheet chapter 11.7.3 about fast PWM mode.
It describes the special case when setting OCR0A to 0. You will still get a small spike, which is what you see.
If you wire your led the other way around (and inverse your code) so that OCR0A at MAX is "off", it will be really off. But with a common cathode that might be difficult, so then explore
to reverse the polarity of the PWM output via COM0A[1:0] bits.
Ooo that's very interesting about setting the Compare Registers to 0. That's exactly what is happening!
So when I first started this project I was using a Common Anode LED and It did not create the small spike behavior showing the dim yellow on the LED. I had all of the values inversed and everything worked great.
So when it came to setting up the sleep mode to save battery when the LED is off. I found out that when in sleep mode the LED values are all set to low automatically. But since it's a common anode Low was considered full brightness and turned the LED to full white, and I could not figure out any way to override that.
So I switched to Common Cathode to remedy that issue.
But now with a common cathode sleep mode works but now observe the dim yellow behavior when Compare Registers for LED Pins are set to 0.
So is this a Common Cathode vs Common Anode issue?
Or is there a way to make either piece of hardware work in the way I expect, and it's just about the way the Registers are initialized?
Yes, you're right, I need to get more comfortable with reading datasheets. So looking into the what you mentioned about the FOCs further it does say that the FOCs are to be active only when in a non-PWM mode, so that wasn't doing anything for me. So I removed the FOCs, and the PWM for PB4 still works!
Now my initialization looks like this, I also removed other pieces that aren't applicable to this example:
Please post a complete schematic diagram of the assembly (hand drawn, not Fritzing), with parts and pins clearly labeled.
I'm fairly sure that the phase correct PWM mode does not produce the narrow spike, so give that a try. I know that it does not on more advanced ATmega MCUs, and will test that with the ATtiny85 later today.
This sets up Timer0 for phase correct PWM, and there is no spike on the output when OCRnx = 0. For inverted output operation (output HIGH when OCR0x = 0), set COM0x bits to 3
// ATtiny85, ATtinyCore, 8 MHz, Timer1 = CPU clock
//Sketch uses 314 bytes (3%) of program storage space. Maximum is 8192 bytes.
//Global variables use 9 bytes (1%) of dynamic memory, leaving 503 bytes for local variables. Maximum is 512 bytes.
void setup() {
// Timer0 phase correct PWM mode PB0 = OC0A, PB1 = OC0B cycle time on scope: 4.2 ms
DDRB = 1 << DDB4 | 1 << DDB1 | 1 << DDB0; //set PWM outputs
TCCR0A = 2 << COM0A0 | 2 << COM0B0 | 1 << WGM00; //clear OC0x on compare match up counting, set on down, phase correct PWM
TCCR0B = 0 << WGM02 | 3 << CS00; //clk/64, 125 kHz, 244 Hz PWM
OCR0A = 64; //25% PWM HIGH
OCR0B = 0; //no spike, output LOW
TIMSK = 0;
// TCCR1 = 0 << PWM1A | 0 << COM1A0 | 7 << CS10; //clk/64, 125 kHz
// GTCCR = 1 << PWM1B | 2 << COM1B0; //clear OC1B on compare match
// OCR1B = 0;
}
void loop() {}
I can tell my timer is a little slower than before, is that to be expected?
I can easily update my custom millis function to accommodate that.
Super thankful for all of the help!
Here is the full working code snippet for anyone looking!
#define F_CPU 8000000
#define PIN_R 0
#define PIN_G 1
#define PIN_B 4
#define PIN_BUTTON 3
uint8_t lightsOn;
unsigned long previousClockTime;
unsigned long main_clock;
unsigned long timerDuration;
volatile uint8_t timer;
volatile unsigned long millis_2;
int num = 0;
uint8_t rainbow[8][3] =
{
{255, 0, 0}, // Red
{255, 165, 0}, // Orange
{255, 255, 0}, // Yellow
{0, 128, 0}, // Green
{0, 0, 10}, // Blue
{255, 255, 255}, // White
{75, 0, 130}, // Indigo
{238, 130, 238},// Violet
};
ISR(TIMER0_OVF_vect) {
timer++;
if (timer == 16) {
timer = 0;
millis_2++; // every 1ms
}
}
int main(void) {
DDRB |= (1 << PIN_R) | (1 << PIN_G) | (1 << PIN_B); // Sets Pins to Output pinMode()
TCCR0A = 2 << COM0A0 | 2 << COM0B0 | 1 << WGM00; // clear OC0x on compare match up counting, set on down, phase correct PWM
TCCR0B |= (1 << CS00); // Start timer/counter 0, no prescaling
TIMSK |= (1 << TOIE0); // Enable timer/counter 0 overflow interrupt
TCCR1 |= (1 << CS10); // Start timer/counter 1, no prescaling
GTCCR |= (1 << PWM1B) | (1 << COM1B1); // Enable PWM mode on timer/counter 1 output B
sei(); // Enable global interrupt
while (1) {
main_clock = millis_2;
if (lightsOn) {
OCR0A = rainbow[num][0];
OCR0B = rainbow[num][1];
OCR1B = rainbow[num][2];
timerDuration = 1000;
}
else {
OCR0A = 0;
OCR0B = 0;
OCR1B = 0;
timerDuration = 1000;
}
if (main_clock - previousClockTime > timerDuration) {
if (lightsOn) {
num = (num + 1) % 8;
}
lightsOn = !lightsOn;
previousClockTime = main_clock;
}
}
}