Hi everyone,
sorry for boring with this topic but i can't find the solution at this issue from a very long time.
I'm using arduino UNO and struggling with the set and clear of digital pin 9 (OC1A).
I need to precise generate a pulse on that pin when an external event triggers the pin 2 (INT 0) only, with variable delay form trigger and variable pulse width.
Let's start with the simple variant of fixed delay and fixed pulse width.
My code is this:
volatile byte isr;
void setup()
{
TCCR1A=0;
TCCR1B=0;
TIFR1=1;
TIMSK1 =0;
TIMSK1 |= bit(OCIE1A);
TCNT1=0;
OCR1A=1000; //500us delay and pulse width
pinMode(9,OUTPUT);
pinMode(2,INPUT);
attachInterrupt(2,triggerIN,RISING);
}
void triggerIN()
{
TCCR1A |= bit(COM1A1) | bit(COM1A0); //set OC1A on compare match
TCCR1B |= bit(WGM12) | bit(CS11); //CTC mode
isr=1;
}
ISR(TIMER1_COMPA_vect)
{
if(isr==1) {TCCR1A = 0; TCCR1A = bit(COM1A1);} //clear OC1A on next compare match
if(isr==2) {TCCR1B=0; TCNT1=0;} //stop the timer
isr++;
}
I'm looking the output pulse with the oscilloscope: triggering it once i see the behavior is as expected, so at the rising edge of the signal on pin 2 the timer starts and after 500us the OC1A is high and after 500us again is low. But, on the next cycle thi is not true again and i have the OC1A set at the timer start and reset after 500us; I clear see on the scope sometimes the correct behavior and sometimes not.
Please, clearly specify which one of the following you want to achieve by operating the TC1 in Mode-4 (CTC).
Figure-1: CTC Mode operation of TC1
1. Initially, the DPin-9 (OC1A signal/pin) will remain at LOW-state. Whenever an external interrupt signal (logic-low) will arrive at INT0-pin (DPin-2, Fig-1), the TCNT1 will start counting up; the DPin-9 will assume HIGH-state when match occurs with OCR1A Register (after the delay determined by OCR1A Register and clkTC1; say 1 sec). Connect a LED with a 2.2k series resistor at DPin-9 to see the result.
void setup()
{
pinMode(9, OUTPUT); //DPin-9 (OC1A signal/pin must be set as output)
pinMode(2, INPUT_PULLUP);
do //check if Logic-low signal is assertd on DPin-2
{
bool n = digitalRead(2);
if ( n == LOW)
{
break;
}
}
while (1);
//-Mode-4 CTC=TC1;LH->OC1A after match;delay 1 sec;clkTC1 = fosc/256
TCCR1A = 0x0000; //reset register to cancel Arduino's setting int init() function
TCCR1A |= bit(COM1A1) | bit(COM1A0); //set OC1A on compare match
TCCR1B = 0x0000; //reset register
TCCR1B |= bit(WGM12) | bit(CS12); //CTC mode; clkTC1 = 62500
OCR1A = 62500;// 1-sec time delay
TCNT1 = 0x0000;
}
void loop()
{
}
2. Initially, the DPin-9 (OC1A signal/pin) will remain at LOW-state. Whenever an external interrupt signal (logic-low) will arrive at INT0-pin (DPin-2, Fig-1), the DPin-9 will begin toggling at interval determined by OCR1A Register and clkTC1; say 1 sec).
void setup()
{
pinMode(9, OUTPUT); //DPin-9 (OC1A signal/pin must be set as output)
pinMode(2, INPUT_PULLUP);
do //check if Logic-low signal is assertd on DPin-2
{
bool n = digitalRead(2);
if ( n == LOW)
{
break;
}
}
while (1);
//-Mode-4 CTC=TC1;LH->OC1A after match;delay 1 sec;clkTC1 = fosc/256
TCCR1A = 0x0000; //reset register
TCCR1A |= bit(COM1A0); //toggle at compare match
TCCR1B = 0x0000; //reset register
TCCR1B |= bit(WGM12) | bit(CS12); //CTC mode; clkTC1 = 62500
OCR1A = 62500;// 1-sec time delay
TCNT1 = 0x0000;
}
void loop()
{
}
3. Same as Step-2; but, the togging interval could be dynamically changed by varying the content of OCR1A Register.
BTW: The 'theory of operation of CTC Mode' needs to be clearly understood and then the users' objectives are to be accurately specified; only then, the coding will be a straight forward task.
Hi gfvalvo,
is not a problem related to the use of the input capture mode instead of external interrupt. If you have a solution to my problem let me know.
Hi GolamMostafa,
i do not want to achieve none of your proposal. I need to achieve a pulse on OC1A any time there is an edge on the external trigger input.
The pulse on OC1A shall have a delay (for example 500us) from the trigger event and a width (for example 500us). After the pulse finish a new trigger event can arrive to trigger a new pulse on OC1A.
Please check my code to understand well what i need.
But, on the next cycle thi is not true again and i have the OC1A set at the timer start and reset after 500us
I think that happens because on the "next cycle" (presumably, you mean next input trigger), the initial timer configuration from setup() has not been recreated. At the moment I don't have the means to test this theory, though.
Try this:
if(isr==2) {TCCR1B=0; TCNT1=0; TCCR1A=0;} //stop the timer
ekjk:
is not a problem related to the use of the input capture mode instead of external interrupt. If you have a solution to my problem let me know.
Basically, use Input Capture mode to capture the counter’s value and trigger an interrupt at the moment your input transitions.
In the capture ISR use the captured timer value plus a small delta to set up an Output Compare event that sets OC1A or OC1B on match and triggers an interrupt.
When the match interrupt fires, use the output match value plus the required pulse width to set up another Output Compare event that clears OC1A or OC1B on match and triggers an interrupt.
When this final interrupt fires, clean things up and set up for the next transition of your trigger input.
ekjk:
The pulse on OC1A shall have a delay (for example 500us) from the trigger event and a width (for example 500us). After the pulse finish a new trigger event can arrive to trigger a new pulse on OC1A.
Hi,
i've done some trials and the nearest solution is this
void SetTimer()
{
cli(); // disable global interrupts
//Initialize TIMER1 for delay, input capture and compare
TIMSK1 |= (1 << ICIE1) | (1 << TOIE1) | (1 << OCIE1A); //Enable input capture
TCCR1B |= (1 << ICNC1); //Enable input capture noise canceller
TCCR1B &= ~(1 << ICES1); //Enable input capture on falling edge
TCNT1 = 0;
TCCR1B |= (1 << CS11);// normal mode
isr = 0;
sei();// enable global interrupts
}
void ResetTimer()
{
cli(); // disable global interrupts
TCCR1A = 0; //Clear TCCR1A
TCCR1B = 0; //Clear TCCR1B
TIMSK1 = 0; //Clear TIMSK1
TIFR1 = 1; //Clear interrupt flag
timer_set = false;
sei();// enable global interrupts
}
ISR(TIMER1_CAPT_vect) // INPUT SIGNAL DETECTION on falling edge
{
TCNT1 = 0;
OCR1A=100;
TCCR1A = (1 << COM1A1) | (1 << COM1A0); //set on compare match
isr = 1;
}
ISR (TIMER1_COMPA_vect) // DELAY AND WIDTH GENERATION
{
if (isr == 1)
{
TCNT1 = 0;
OCR1A+=100;
TCCR1A = (1 << COM1A1); //clear on compare match
isr++;
}
if (isr == 2)
{
isr++;
}
}
ResetTimer() and SetTimer() are called once during the setup().
Now the code is working but i see an output flicker on the pulse waveform: at 1 second (more or less) interval the pulse disappears for a more or less 500ms and then appears again. this behavior is cycling.
Do you think that the following timing diagram has adequately contained the objectives that you want to achieve using TC1? If so, I will try to convert it into codes with due respect to the below-stated constraints and see what the outcomes are.
1. We must have clear idea about the minimum and maximum frequency of the occurrence of the external 'triggering rising edge' at DPin-2 of the UNO. If we take the example of your 2500 us (delay and then HIGH) timing, the separation time (t2 - t1) between the two rising edges of the trigger signal must be greater than 1 ms (2500 us = 1 ms). This is to say that the frequency of the trigger signal is to be less than 1 kHz.
2. The 'variable delay time + variable HIGH Period' of the OC1A signal at DPin-9 must always be less than 't2 - t1 '.
3. So please, specify the minimum and maximum separation times of the rising edges of the trigger signal at DPin-2 in order to help in coding.
Do you think that the following timing diagram has adequately contained the objectives that you want to achieve using TC1? If so, I will try to convert it into codes with due respect to the below-stated constraints and see what the outcomes are.
1. We must have clear idea about the minimum and maximum frequency of the occurrence of the external 'triggering rising edge' at DPin-2 of the UNO. If we take the example of your 2500 us (delay and then HIGH) timing, the separation time (t2 - t1) between the two rising edges of the trigger signal must be greater than 1 ms (2500 us = 1 ms). This is to say that the frequency of the trigger signal is to be less than 1 kHz.
2. The 'variable delay time + variable HIGH Period' of the OC1A signal at DPin-9 must always be less than 't2 - t1 '.
3. So please, specify the minimum and maximum separation times of the rising edges of the trigger signal at DPin-2 in order to help in coding.
HI GolamMostafa,
the time sequence you have drawn is correct and i agree with all your considerations; the min and max input signal frequency will be 10Hz-600Hz.
In the code i posted previously i use the timer 1 in capture mode, but this is not a problem or a limit.
The delay at high frequency of the input signal shall be little, like 50us maximum. The pulse width shall be 500us at any input signal frequency.
Thanks
Not a fan of constantly setting counter value to 0 like you are. Also, given your timing requirements, I think you can use pre-scaler = 1. Finally, no need to turn off interrupts once you’ve stopped the timer. The state machine code below compiles but is untested.
#include "Arduino.h"
enum State
: uint8_t {WAITING_FOR_INPUT, WAITING_FOR_RISING, WAITING_FOR_FALLING
};
const uint16_t pulseWidth = 8000; // 500us at 16MHz clock
const uint16_t waitTime = 160; // 10us at 16MHz clock
volatile State current_State = WAITING_FOR_INPUT;
void setup() {
pinMode(8, INPUT);
pinMode(9, OUTPUT);
digitalWrite(9, 0);
// Set up Timer 1
TCCR1B = 0; // no need to disable interrupts, this stops the timer
TCCR1A = 0;
TCCR1B |= (1 << ICNC1); //Enable input capture noise canceller
TCCR1B &= ~(1 << ICES1); //Enable input capture on falling edge
TIFR1 = (1 << TOV1) | (1 << OCF1A) | (1 << OCF1B) | (1 << ICF1); // clear all flags
TIMSK1 = (1 << ICIE1); // Enable capture interrupt
TCCR1B |= (1 << CS10); // start timer pre-scaler = 1
}
void loop() {
}
ISR(TIMER1_CAPT_vect) {
uint16_t capturedValue;
TIFR1 = (1 << TOV1) | (1 << OCF1A) | (1 << OCF1B) | (1 << ICF1); // clear all flags
if (current_State == WAITING_FOR_INPUT) {
capturedValue = ICR1;
OCR1A = capturedValue + waitTime; // set time for rising edge
TCCR1A |= (1 << COM1A1) | (1 << COM1A0); // output rising edge on compare match
TIMSK1 = (1 << OCIE1A); // Enable match interrupt
current_State = WAITING_FOR_RISING;
}
}
ISR (TIMER1_COMPA_vect) {
TIFR1 = (1 << TOV1) | (1 << OCF1A) | (1 << OCF1B) | (1 << ICF1); // clear all flags
switch (current_State) {
case WAITING_FOR_RISING:
OCR1A += pulseWidth;
TCCR1A &= ~(1 << COM1A0); // output falling edge on compare match
current_State = WAITING_FOR_FALLING;
break;
case WAITING_FOR_FALLING:
TCCR1A &= ~(1 << COM1A1); // leave output LOW
TIMSK1 = (1 << ICIE1); // Enable capture interrupt
current_State = WAITING_FOR_INPUT; // set for next input pulse
break;
default:
break;
}
}
gfvalvo:
Not a fan of constantly setting counter value to 0 like you are. Also, given your timing requirements, I think you can use pre-scaler = 1. Finally, no need to turn off interrupts once you’ve stopped the timer. The state machine code below compiles but is untested.
#include "Arduino.h"
enum State
: uint8_t {WAITING_FOR_INPUT, WAITING_FOR_RISING, WAITING_FOR_FALLING
};
const uint16_t pulseWidth = 8000; // 500us at 16MHz clock
const uint16_t waitTime = 160; // 10us at 16MHz clock
ekjk:
the time sequence you have drawn is correct and i agree with all your considerations; the min and max input signal frequency will be 10Hz-600Hz.
The delay at high frequency of the input signal shall be little, like 50us maximum. The pulse width shall be 500us at any input signal frequency.
The range of the trigger signal is: 10 Hz - 600 Hz. When you say 'delay at high frequency' as in the above quote, then that high frequency begins from where. Is it 500 Hz and above or what?
GolamMostafa:
The range of the trigger signal is: 10 Hz - 600 Hz. When you say 'delay at high frequency' as in the above quote, then that high frequency begins from where. Is it 500 Hz and above or what?
I mean, high frequency is 600Hz maximum. so the delay at 600Hz shall be 50us maximum and the pulse width 500us, so the total required time is not greater than the minimum time period (1/600). At low freq (e.g. 20Hz) the delay could be maximum 2ms. The range of variability of the delay should be under my control and whithin the mix-max time period of input signal.