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.
-
What Arduino board?Edgar Bonet– Edgar Bonet2018年06月26日 18:32:02 +00:00Commented Jun 26, 2018 at 18:32
2 Answers 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);
.
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:
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.
-
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)Greenonline– Greenonline2018年06月26日 19:22:12 +00:00Commented Jun 26, 2018 at 19:22 -
Just to clarify: I need to use
bitRead(PIND,6);
?Greenonline– Greenonline2018年06月26日 19:33:24 +00:00Commented 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.Edgar Bonet– Edgar Bonet2018年06月26日 19:43:35 +00:00Commented Jun 26, 2018 at 19:43