I've descided that the only reliable answer to prevent missing rotation and cause the error i explained is to use timer interrupt as @jremington hinted at, so that the position is updated at a steady and fast enough interval, this is just a little tutorial so I will need to speed up the timing still.
I've read on the forum here that it is usually not recommended using timer interrupts or interrupts in general but this is my only option as far as i know.
I had some trouble making sense of it all with online tutorials that just don't give any information of what is really going on so i digged into the datasheet of the arduino processor, started from a small tutorial i found online and added pretty much all relevant info to it so that its noobfriendly.
I do have some questions about the matter still so any help is greatly appreciated.
Original tut: https://electronoobs.com/eng_arduino_tut140.php
ATmega datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/DataSheets/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061B.pdf
Next up i'm gonna do the same thing for the ESP32 cpu and add it to this code/tutorial
/* (elaborated ) Example code with timer intyerrutp that will create an interruption each (original source: https://electronoobs.com/eng_arduino_tut140.php)
* 500ms using timer1 and prescalar of 256.
Calculations (for 500ms):
System clock 16 Mhz and Prescalar 256;
Timer 1 speed = 16Mhz/256 = 62.5 Khz ==> 1 pulse every (1 000 000us/62 500): 16us
Count up to = 500 000us / 16us = 31250 (so this is the value the Output Compare Register should have)
ATmega docs: https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/DataSheets/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061B.pdf
*/
volatile /*static?*/ uint16_t CritVal = 0;
uint8_t CritValPin = A1;
/* Goal: Let counter count, when enough time has passed call the ISR event/interrupt vector where we will perform a simple analogread on a single input pin,
reset the counter and repeat.
*/
/* Questions:
- Should i declare the critical val as static aswell?
- initially the status register is completely blank (all 0's), so by default on the arduino mcu interrupts are disabled? and become disabled again when powered down and a new program is uploaded to the arduino?
& Does the I-Bit in the status register need to be manually set to 1 in order for interrupts to work or is this bit automaticly set by the mcu whenever an interrupt in the Interrupt Mask Register is activated or set high?
- What happens when the mcu goes to sleep mode to preserve power, will interrupts & Timers/counters still work?
- Do continous non stop usage(24/7) of high frequency(triggering ISR every couple milisecond/microseconds) timer interrupts increase power consumtion, decrease life expectancy of the mcu/increase breakchance?
- In Clear Timer on Compare Match (CTC) Mode, do we need to set OCIE1A or OCIE1B to 1 aswell in the iterrupt mask register? or is it enough to change the WGM bits so mode 4(CT output compare) is active?
- In the datasheet there is nothing to be found about TIMER1_COMPA_vect, only TIMER1_COMPA why or where does the _vect part come from and will the interrupt work when using "ISR(TIMER1_COMPA){}" instead
- Can I set a single bit in a byte to 1 or 0 without changing the entire bit at once?
*/
void setup() {
cli(); //stop interrupts while we edit the settings
/* 1. First we reset the control register to make sure we start with everything disabled.
TCCR1 register is a 24 bit(3 byte) register for timer 1 that has the possibility to configure 2 seperate interrupts with the one timer/counter
By default registers TCCR1A, TCCR1B and TCCR1C are initially all 0's */
TCCR1A = 0; // Reset entire TCCR1A byte to 0; is the same as &= B00000000 or = B00000000
TCCR1B = 0; // Reset entire TCCR1B byte to 0; is the same as &= B00000000 or 0x00
TCCR1C = 0;
/* TCCR1A Register (adress = 0x80):
- Bit 7:6 – COM1A1:0: Compare Output Mode for Channel A
- Bit 5:4 – COM1B1:0: Compare Output Mode for Channel B
The COM1A1:0 and COM1B1:0 control the Output Compare pins (OC1A and OC1B respectively) behavior.
If one or both of the COM1A1:0 bits are written to one, the OC1A output overrides the normal port functionality of the I/O pin it is connected to.
If one or both of the COM1B1:0 bit are written to one, the OC1B output overrides the normal port functionality of the I/O pin it is connected to.
However, note that the Data Direction Register(DDR) bit corresponding to the OC1A or OC1B pin must be set in order to enable the output driver.
When the OC1A or OC1B is connected to the pin, the function of the COM1x1:0 bits is dependent of the
WGM13:0 bits setting. Table 16-1 shows the COM1x1:0 bit functionality when the WGM13:0 bits are set to a
Normal or a CTC mode (non-PWM).
COM1A1/COM1B1 COM1A0/COM1B0 Description
0 0 Normal port operation, OC1A/OC1B disconnected.
0 1 Toggle OC1A/OC1B on Compare Match.
1 0 Clear OC1A/OC1B on Compare Match (Set output to low level).
1 1 Set OC1A/OC1B on Compare Match (Set output to high level).
- Bit 2:3 - Unused
- Bit 1 – WGM11 Waveform Generation Mode (PWM11)
- Bit 0 - WGM10 Waveform Generation Mode (PWM10)
Combined with the WGM13:2 bits found in the TCCR1B Register, these bits control the counting sequence of
the counter, the source for maximum (TOP) counter value, and what type of waveform generation to be used,
see Table 16-4. Modes of operation supported by the Timer/Counter unit are:
Normal mode (counter), Clear Timer on Compare match (CTC) mode, and three types of Pulse Width Modulation (PWM) modes. (See ”Modes
of Operation” on page 131).
Table 16-4: Waveform Generation Mode Bit Description :
TCCR1- B(Bit4) B(bit3) A(bit1) A(bit0)
Mode WGM13 WGM12 WGM11 WGM10 Timer/Counter Mode of TOP Update of TOV1 Flag
(CTC1) (PWM11) (PWM10) Operation OCR1x at Set on
0 0 0 0 0 Normal 0xFFFF Immediate MAX
1 0 0 0 1 PWM, Phase Correct, 8-bit 0x00FF TOP BOTTOM
2 0 0 1 0 PWM, Phase Correct, 9-bit 0x01FF TOP BOTTOM
3 0 0 1 1 PWM, Phase Correct, 10-bit 0x03FF TOP BOTTOM
We need --> 4 0 1 0 0 CTC OCR1A Immediate MAX <-- ( WGM13:0 = 4; ? )
5 0 1 0 1 Fast PWM, 8-bit 0x00FF BOTTOM TOP
6 0 1 1 0 Fast PWM, 9-bit 0x01FF BOTTOM TOP
7 0 1 1 1 Fast PWM, 10-bit 0x03FF BOTTOM TOP
8 1 0 0 0 PWM, Phase and Frequency Correct ICR1 BOTTOM BOTTOM
9 1 0 0 1 PWM, Phase and Frequency Correct OCR1A BOTTOM BOTTOM
10 1 0 1 0 PWM, Phase Correct ICR1 TOP BOTTOM
11 1 0 1 1 PWM, Phase Correct OCR1A TOP BOTTOM
12 1 1 0 0 CTC ICR1 Immediate MAX
13 1 1 0 1 (Reserved) – – –
14 1 1 1 0 Fast PWM ICR1 BOTTOM TOP
15 1 1 1 1 Fast PWM OCR1A BOTTOM TOP
*/
/* TCCR1B Register (0x81):
- Bit 7 – ICNC1: Input Capture Noise Canceler
Setting this bit (to one) activates the Input Capture Noise Canceler. When the noise canceler is activated, the
input from the Input Capture pin (ICP1) is filtered. The filter function requires four successive equal valued
samples of the ICP1 pin for changing its output. The Input Capture is therefore delayed by four Oscillator cycles
when the noise canceler is enabled.
- Bit 6 – ICES1: Input Capture Edge Select (for use with Input Capture Interrupt(ICIE1))
This bit selects which edge on the Input Capture pin (ICP1) that is used to trigger a capture event. When the
ICES1 bit is written to zero, a falling (negative) edge is used as trigger, and when the ICES1 bit is written to one,
a rising (positive) edge will trigger the capture.
When a capture is triggered according to the ICES1 setting, the counter value is copied into the Input Capture
Register (ICR1). The event will also set the Input Capture Flag (ICF1), and this can be used to cause an Input
Capture Interrupt, if this interrupt is enabled.
When the ICR1 is used as TOP value (see description of the WGM13:0 bits located in the TCCR1A and the
TCCR1B Register), the ICP1 is disconnected and consequently the Input Capture function is disabled.
- Bit 5 – unused
- Bit 4 – WGM13: Waveform Generation Mode (See TCCR1A (1:0) Register description)
- Bit 3 – WGM12(CTC1): Waveform Generation Mode (See TCCR1A (1:0) Register description)
- bit 2:0 - CS12:0: Clock Select
(CS12-CS11-CS10) prescalar config
0 0 0 (Timer Stopped)
0 0 1 PCK (System Clock)(prescalar 1)
0 1 0 PCK/8
0 1 1 PCK/64
1 0 0 PCK/256
1 0 1 PCK/1024
*/
/* TCCR1C Register (0x82):
- Bit 7 – FOC1A: Force Output Compare for Channel A
- Bit 6 – FOC1B: Force Output Compare for Channel B
The FOC1A/FOC1B bits are only active when the WGM13:0 bits specifies a non-PWM mode. When writing a
logical one to the FOC1A/FOC1B bit, an immediate compare match is forced on the Waveform Generation unit.
The OC1A/OC1B output is changed according to its COM1x1:0 bits setting. Note that the FOC1A/FOC1B bits
are implemented as strobes. Therefore it is the value present in the COM1x1:0 bits that determine the effect of
the forced compare.
A FOC1A/FOC1B strobe will not generate any interrupt nor will it clear the timer in Clear Timer on Compare
match (CTC) mode using OCR1A as TOP. The FOC1A/FOC1B bits are always read as zero.
- Bits 5:0 - Unused, only readable, always 0.
*/
/*2. in the TCCR1B Register we set the prescalar to the desired value by changing the CS10 CS12 and CS12 bits.
and the WGM to mode 4(CTC mode) (bit3 to 1) */
TCCR1B |= B00001100; //alter bits in OR comparison CS12 to 1 so we get prescalar 256
// how can i assign the value of each bit independantly from the entire byte?
//WGM12 |= (1 << 0); Doesn't work..
//CS12 = 1; Doesn't work..
//WGM13 &= ~(1<<0); Doesn't work.. (setting to 0)
/*3. We enable compare match mode on timer1A in register TIMSK1 – Timer/Counter1 Interrupt Mask Register:
- Bit 0 – TOIE1: Timer/Counter1, Overflow Interrupt Enable
When this bit is written to one, and the I-flag in the Status Register(SREG – AVR Status Register, bit 7) is set (interrupts globally enabled), the
Timer/Counter1 Overflow interrupt is enabled. The corresponding Interrupt Vector (TIMER1_OVF (Timer/Counter1 Overflow))
is executed when the TOV1 Flag, located in TIFR1, is set.
- Bit 1 – OCIE1A: Timer/Counter1, Output Compare A Match Interrupt Enable ( includes Clear Timer on Compare Match (CTC) Mode? )
- Bit 2 – OCIE1B: Timer/Counter1, Output Compare B Match Interrupt Enable ( includes Clear Timer on Compare Match (CTC) Mode? )
When this bit is written to one, and the I-flag in the Status Register(SREG – AVR Status Register, bit 7) is set (interrupts globally enabled), the
Timer/Counter1 Output Compare B Match interrupt is enabled. The corresponding Interrupt Vector (TIMER1_COMPB (Timer/Counter1 Compare Match B))
is executed when the OCF1B Flag, located in TIFR1, is set.
* The 16-bit comparator continuously compares TCNT1 with the Output Compare Register (OCR1x). If TCNT
equals OCR1x the comparator signals a match. A match will set the Output Compare Flag (OCF1x) at the next
timer clock cycle. If enabled (OCIE1x = 1), the Output Compare Flag generates an Output Compare interrupt.
The OCF1x Flag is automatically cleared when the interrupt is executed. Alternatively the OCF1x Flag can be
cleared by software by writing a logical one to its I/O bit location. The Waveform Generator uses the match
signal to generate an output according to operating mode set by the Waveform Generation mode (WGM13:0)
bits and Compare Output mode (COM1x1:0) bits.
* In Clear Timer on Compare or CTC mode (WGM13:0 = 4 or 12), the OCR1A or ICR1 Register are used to
manipulate the counter resolution. In CTC mode the counter is cleared to zero when the counter value (TCNT1)
matches either the OCR1A (WGM13:0 = 4) or the ICR1 (WGM13:0 = 12). The OCR1A or ICR1 define the top
value for the counter, hence also its resolution. This mode allows greater control of the compare match output
frequency. It also simplifies the operation of counting external events.
- Bit 5 – ICIE1: Timer/Counter1, Input Capture Interrupt Enable
When this bit is written to one, and the I-flag in the Status Register(SREG – AVR Status Register, bit 7) is set (interrupts globally enabled), the
Timer/Counter1 Input Capture interrupt is enabled. The corresponding Interrupt Vector (TIMER1_CAPT (Timer/Counter1 Capture Event))
is executed when the ICF1 Flag, located in TIFR1, is set.
Input capture events can be used to measure the width of a pulse. This is done by using an input capture event that triggers on the rising edge of a pulse.
The ISR servicing this interrupt saves the time when the interrupt was received and then resets the interrupt so it triggers on the falling edge.
- Bit 3,4,6,7 - Reserved/unrelated, always checks 0
*/
TIMSK1 |= B00000010; //Set OCIE1A to 1 so the system triggers the TIMER1_COMPA interrupt vector? (should be the same as TIMSK1 = 2 (only incase all other bits are 0)?)
/*4. Set the value of the Output Compare Register Timer1A to 31250
* The Output Compare Registers contain a 16-bit value that is continuously compared with the counter value
(TCNT1). A match can be used to generate an Output Compare interrupt, or to generate a waveform output on
the OC1x pin. The Output Compare Registers are 16-bit in size.
* There is also an Input Compare Register (ICR1x):
The Input Capture is updated with the counter (TCNT1) value each time an event occurs on the ICP1 pin (or
optionally on the Analog Comparator output for Timer/Counter1). The Input Capture can be used for defining the
counter TOP value.
The Input Capture Register is 16-bit in size. */
OCR1A = 31250; //Finally we set compare register A to this value
sei(); //Enable back the interrupts
}
void loop() {
// put your main code here, to run repeatedly:
}
//With the settings above, this Interrupt Service Routine will trigger each 500ms.
ISR(TIMER1_COMPA_vect){
//TCNT1 = 0; //First, set the timer back to 0 so it resets for next interrupt (Redundant as the system already does this for us in CTC mode)
CritVal = analogRead(CritValPin);
}