1

I set up my Atmel ATMega32U4 with a relay (using an NPN transistor to control the 5V supply to the relay, not driving it directly with the microcontroller). I want to use PD4 as a digital output to control the relay's state. As I'll be using USB communication later, I've got an external crystal oscillator attached, so I set the appropriate fuse and left the others at their default.

When I run a very simple program like the one below, which should just switch the relay on once, instead I hear the relay switching on and off rapidly at about 10 Hz.

#include <avr/io.h>

int main(void)
{
    // toggle PD4 as an output
    DDRD |= _BV(4);

    // set output high
    PORTD |= _BV(4);
}

I noticed that commenting out the PORTD |= _BV(4); line stops the oscillation, but of course that doesn't solve my problem. It seems to be that line that's creating the problem, anyway.

I'm using Atmel Studio 6.2, programming with JTAG via an Atmel-ICE programmer.

Taking a wild stab in the dark, I guess it might have something to do with PD4's alternative function as ICP1/ADC8 as shown in the datasheet (section 10.3.3, p78), but I don't know how to disable this functionality.

Does anyone have any ideas where I'm going wrong?

Sean
  • 1,346
  • 13
  • 24
  • You didn't link to the datasheet which details the alternative functions. For PD4 there are no alternative output functions, it's either an ADC input or a timer trigger input, so maybe the chip is being reset which would turn the relay off as PORTD goes tristate. What happens after main() exits? Have you tried putting a while(1); loop after the setting of PORTD, so main() doesn't exit, see if the output is stable then? Have you tried driving the relay from a pin on a different port? Have you got a series resistor driving the base of the transistor, a diode across the relay coil? – DisappointedByUnaccountableMod Dec 12 '16 at 18:12
  • @barny, I tried a while(1) loop and got the same results. The relay is driven with a resistor and diode as you mention. I've not tried a different port because this is on a PCB I printed, but I can hack something together if nothing else works. Do you know if it's possible to disable the tristate functionality of that port? – Sean Dec 12 '16 at 18:59
  • When the CPU is reset the ports by design default to inputs, i.e. High impedance, which would turn your relay off. You can't and don't want to change that behaviour. Do you have an oscilloscope or other way of looking at the signal on the port? You don't have anything like a hardware watchdog timer in the circuit which would reset the CPU? – DisappointedByUnaccountableMod Dec 12 '16 at 22:14
  • @barny, I looked at the signal on the port at the time and it contained a "HIGH" pulse every 100ms or so and was "LOW" the rest of the time. Perhaps something is switching the microcontroller off and on quickly, and it's running the program before switching off again and repeating the cycle. To investigate this I guess I can add a long delay before setting the pin output. If the relay still oscillates, I can probably rule out microcontroller power cycling being the problem. – Sean Dec 12 '16 at 22:25
  • @barny, it turns out it was indeed a watchdog. I thought I had disabled it by setting the WDTON fuse to false, but apparently it was still on. Adding `MCUSR &= ~(1 << WDRF);` to the top of my `main()` function stopped the oscillation. Thanks for your help, much appreciated! – Sean Dec 13 '16 at 10:27

1 Answers1

1

The problem was the watchdog timer. Setting the WDTON (watchdog timer always on) fuse to false didn't help with the oscillation problem - I guess that setting it false makes sure it's not always on, but doesn't make sure it's definitely off. Putting the following line in main() did:

MCUSR &= ~(1 << WDRF);

You also need to import the watchdog timer header avr/wdt.h at the top of your script, or in the header file:

#include <avr/wdt.h>

So now the code reads:

#include <avr/io.h>
#include <avr/wdt.h>

int main(void)
{
    // turn off watchdog
    MCUSR &= ~(1 << WDRF);

    // toggle PD4 as an output
    DDRD |= _BV(4);

    // set output high
    PORTD |= _BV(4);

    // wait
    while(1)
    {
        // do nothing
    }
}
Sean
  • 1,346
  • 13
  • 24