2
\$\begingroup\$

first post here, so sorry for any bad styling.

I'm working on a group project and using an Atmega328, without going into specifics, I need to drive two fans, two servos, and 1 CTC based function from 3 timers. To my question then; there's registers OCRx A and B that I'm using to drive the fans at variable speeds, and using a similar method for the servos. Setting up a very basic PWM script, I've noticed that both registers will drive a fan, however when I attempt to use either register independently (fan 1 on, 2 off, then change), OCRxB will not produce an output.

Here's a cut-out of my setup code:

void setup_timers () {
// Set Timer0 to Fast PWM mode
TCCR0A |= (1 << WGM01) | (1 << WGM00);

// Set Timer0 to clear OCR0 A/B on match
TCCR0A |= (1 << COM0A1) | (1 << COM0A0) | (1 << COM0B1) | (1 << COM0B0);
TCCR0B |= (1 << CS01);
TCNT0 = 0;

// Set OCR0 A/B
DDRD = 0xFF;}

Now, ideally, this will set up both registers in Timer 0 for outputs. I'm sending this signal to a transistor, which then powers the fans as determined by this next bit:

int main () {
setup_timers();

while (1) {

    /* Timer 0; OCR0A (i = 0, L), OCR0B (i = 1, R), i = 2 denotes both fans */

    for (uint8_t i = 0; i <= 3; i++) {
        // Cycle Fans Left/Right/Both

        for (uint8_t j = 1; j <= 4; j++) {
            // Cycle Speed 

            if (i == 2) {
                // Check motor; run timer 0 A/B as required
                OCR0A = 0xFF * j / 4;
                OCR0B = 0xFF * j / 4;
                _delay_ms(2000);
            }
            else if (i == 1) {
                OCR0A = 0xFF * j / 4;
                OCR0B = 0;
                _delay_ms(2000);
            }
            else if (i == 0) {
                OCR0A = 0;
                OCR0B = 0xFF * j / 4;
                _delay_ms(2000);
            }
        }
    }
}
return 1; }

Now I know this could be done better, this segment has a heap of other tests cut out. The main issue here is that Fan 1 will run (directed by OCR0A), but Fan 2 is completely ignored. I have read through the data sheet a few times, but it was a bit unclear as to whether this is actually possible, it did say something about OCRxB not being stored, and I have seen other posts asking after that. From what I understand they are independent registers, but is it possible that OCRxA is retained, but after writing OCRxB it is impossible to execute?

Or maybe I'm completely wrong, certainly wouldn't be the first time.

\$\endgroup\$
  • \$\begingroup\$ your output pins may not actually share the same timer. check the timer output mapping to the physical pins you are using. \$\endgroup\$ – KyranF Oct 13 '14 at 7:34
  • \$\begingroup\$ It should work. Are you sure fan is connected to (PCINT21/OC0B/T1) PD5 pin ? BTW: I think outer loop should go to <3 only and 0xFF * j / 4 probably does not provide 0xFF for j=4 but 252. \$\endgroup\$ – TMa Oct 13 '14 at 8:41
  • \$\begingroup\$ @TMa - You may want to rethink your comment about "j". If j=4 and noting that 0xFF=255 it follows that (255*4)/4 == 255, not 252. \$\endgroup\$ – Michael Karas Oct 13 '14 at 9:37
  • \$\begingroup\$ I think it's compiled as 255/4 * j=63.75-->63 * j. And in runtime evaluated only 63 * j, i.e. 63 * 4 = 252. At least depends on compiler optimization. But it's not important issue in test. Sorry if I'm wrong. \$\endgroup\$ – TMa Oct 13 '14 at 11:43
  • \$\begingroup\$ @KyranF, I'm aware that there are two output pins, I've been able to get each to work solely from OCR0A, but not have OCR0B (D6) acting independent of OCR0A (D5). \$\endgroup\$ – Oman Oct 13 '14 at 23:23
1
\$\begingroup\$

It appears that there was some issue with predetermined values, by setting COM1A0 and COM1B0, Timer 1 can be configured to give independent signals. This is the setup code that works:

void setup_timers () {

TCCR1A |= (1 << WGM11) | (1 << WGM10);
TCCR1A |= (1 << COM1A1) | (1 << COM1A0) | (1 << COM1B1) | (1 << COM1B0);
TCCR1B |= (1 << CS11);
TCNT1 = 0;

DDRB = 0xFF;}

I'm still having trouble getting it to properly control the motors, but the servos work well, and it can be done.

Note: Timer 1 actually has two separate outputs, but Timer 0/2 do not. To get dual outputs, you need to toggle WGMx2 and the COMxB 0/1 registers, when WGMx2 is set, OCRxA will not output properly, but OCRxB will, with some fiddling. When WGMx2 is not set, it disable OCRxB, and OCRxA will work as normal, to get independent outputs, you will need to take this into account.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.