1

I have this code, which puts the Arduino into CTC mode with interrupts, and outputs a square wave on PD6 (OC0A):

void setup_timer(double p_ms, double duty){
  DDRD |= (1 << 6);  // set pin 3 as output  
  TCCR0A = _BV(COM0A0); // toggle OC0A on Compare Match
  TCCR0B = _BV(WGM01); // set CTC mode WGM0[2,1,0] = 0b010 
  TIMSK0 |= _BV(OCIE0A); // Enable CTC interrupt  
  OCR0A = 128;  // set on and off time
  TCCR0B |= ( (0 << CS02) | (0 << CS01) | ( 1 << CS00)); // CS02:0 - No prescaling
  sei();
}

In the ISR (which is not shown) I want to be able to see whether OC0A (i.e. PD6) is HIGH or LOW.

I assumed that I could do a digitalRead(), but this post, Re: How to read the state of an output pin ? says:

Looking at the code the digitalRead() function just does a read of the appropriate bit in the appropriate PINx register without modifying anything, so it should work.

One catch, if it's a PWM pin the PWM will be stopped.

So, will the toggling of OCR0A be stopped?

This answer to How can I digitalRead a pin that is in pinMode OUTPUT? suggests reading PORTD directly, with

bitRead(PORTD,6);

However, will that work for PWM output or will PWM also be stopped by reading PORTD?


Addendum

I plan to use this code either as (primarily) a stand alone AVR µController (48/88/168/328 IC), but maybe in a Nano/Uno (if it will work) for testing purposes. I understand that using Timer0 will affect the operation of millis(), delay() etc., but the rest of the code will not rely on any of those functions... so I presume (maybe incorrectly) that the code will work as expected. Is that correct?

In addition, I don't intend on using a setup() & loop() program structure but rather main(). Just out of interest, would loop() work (i.e. is it reliant on Timer0)? I don't believe that it is.

1
  • What Arduino board? Jun 26, 2018 at 18:32

2 Answers 2

2

If you look into the Arduino Core, you'll find this code for the digital read:

int digitalRead(uint8_t pin)
{
    uint8_t timer = digitalPinToTimer(pin);
    uint8_t bit = digitalPinToBitMask(pin);
    uint8_t port = digitalPinToPort(pin);

    if (port == NOT_A_PIN) return LOW;

    // If the pin that support PWM output, we need to turn it off
    // before getting a digital reading.
    if (timer != NOT_ON_TIMER) turnOffPWM(timer);

    if (*portInputRegister(port) & bit) return HIGH;
    return LOW;
}

That means, disabling PWM is just weird feature in digitalRead.

For reading bitRead(PORTB,3); I'm not so sure if you'll get output value of OC0A, it might be different signal line internally. However you'll get logical level on the port pin by reading bitRead(PINB,3);.

2

Preliminary note: If you are using any of the ATmega48/88/168/328, then the OC0A pin is PD6, not PB3.

As four your idea of reading the PORT register (PORTD, not PORTB), it won't work either, as that will only tell you what was the last value that was written to the port with digitalWrite(). Here is a schematic showing how the timer controls the output pin. It's taken from the ATmega328P datasheet, but the other AVRs of that family behave identically in this respect:

Compare match output unit

As shown in the schematic, the value written to the port comes from a multiplexer and is controlled either:

  • by the PORT flip-flop when the timer is not generating a PWM output
  • or the OC0A flip-flop when it is.

Quite importantly, the timer does not write to the PORT register, it only overrides it.

Thus, it would look like you want to read the OC0A flip-flop in your ISR. Alas, this schematic also shows that, contrary to the PORT flip-flop, the OC0A flip-flop is not connected to the data bus. Thus you cannot read it, and it doesn't even have an address.

What I would recommend is to read the PIN register instead. This won't tell you what the timer is writing to the port, but what you can read back. However, unless the pin is loaded well beyond its absolute maximum rating, what you read back what has been written. There is a small delay between the write operation and the value been available to read back (a couple of CPU cycles IIRC), but this is way shorter than the execution time of an ISR prologue.

3
  • Thanks for pointing out that I had the incorrect port. I mixed up datasheets, and it is the ATmega16 that uses PB3 = OC0 (page 56 of the ATmega16 datasheet) Jun 26, 2018 at 19:22
  • Just to clarify: I need to use bitRead(PIND,6);? Jun 26, 2018 at 19:33
  • 1
    @Greenonline: Either that or PIND & _BV(PD6). I tend to favor the later: avr-libc style when doing direct port access, and Arduino style when programming the Arduino way. But in the end it's just a matter of style. Jun 26, 2018 at 19:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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