I have a rotary encoder with STM32F4 and configured TIM4 in "Encoder Mode TI1 and TI2". I want to have an interrupt every time the value of timer is incremented or decremented.
The counting works but I only can configure an interrupt on every update event, not every changes in TIM4->cnt. How can I do this?
In other words: My MCU+Encoder in quadrature mode could count from 0 to 99 in one revolution. I want to have 100 interrupts in the revolution but if I set TIM4->PSC=0 and TIM4->ARR=1, results 50 UPDATE_EVENTs, so I should set ARR=0 but it does not work. How can I sole that?
To get 100 interrupts per revolution keep PSC=0, ARR=1, setup the two timer channels in output compare mode with compare values 0 and 1 and interrupts on both channels.
Or even use ARR=3 and setup all four channels, with compare values of 0,1,2 and 3. This will allow to detect the direction.
Normally, the whole point of using the quadrature encoder mode is counting the pulses while avoiding interrupts. You can simply poll the counter register periodically to determine speed and position.
Getting interrupts on every encoder pulse is extremely inefficient, especially with high resolution encoders. Yours seems to be a low resolution one. If you still think you need them for some reason, you can connect A & B into external interrupts and implement the counting logic manually. In this case, you don't need quadrature encoder mode.
I'm trying to count the blinks of a LED with the ESP32-CAM AI Thinker board. I need to count a LED that blinks once every 150ms. The code part works fine, but I'm experiencing some fps drops in some points, so I have some frozen frames and then the counting is wrong.
How can I have a smoother capture of the ESP32-CAM so that I don't lose any frames of the LED blinking?
There are two approaches that I can think of:
1. Find the source of the issue
Why are these frames freezing? Is it the camera board itself, or is it something on the ESP32 itself? Perhaps there is some FreeRTOS task running on the ESP32 (e.g. BlueTooth or WiFi) which occasionally requires CPU and causes disp in performance.
Run idf.py menuconfig to check if:
your CPU is running at maximum speed
you have BlueTooth/WifiEnabled
...
2. Extrapolate the missing data
With this approach, you'd first detect the frame drops, and measure the amount of frames dropped. It is only an approximation though:
When you then calculate the LED blink count, take the average measured blinks from the last second (or whichever timeframe that is suitable) and fill in the missing blinks with this this data.
I've been trying to program my ATtiny817-XPRO to interpret input data from a rotary encoder (the Arduino module), however I'm having some trouble and can't seem to figure out what the problem is. What I'm trying to do is essentially program a digital combination lock that blinks a red LED every time the rotary encoder is rotated one "tick" (in either direction), and blinks a green LED once the right "combination" has been detected. It's a little bit more involved than that, so when I ran into trouble upon testing my code, I decided to write a simple method to help me troubleshoot/debug the problem. I've included it below:
void testRotaryInput(){
if(!(PORTC.IN & 0b00000001)){ // if rotary encoder is turned clockwise
PORTB.OUT = 0b00000010; // turn on green LED
}
else if(!(PORTC.IN & 0b00000010)){ // if rotary encoder is turned CCW
PORTB.OUT = 0b00000001; // turn on blue LED
}
else{ // if rotary encoder remains stationary
PORTB.OUT = 0b00000100; // turn on red LED
}
RTC.CNT = 0;
while(RTC.CNT<16384){} // wait 500ms
PORTB.OUT = 0x00; // turn LED off
while(RTC.CNT<32768){} // wait another 500ms
}
int main(void)
{
PORTB.DIR = 0xFF; // PORT B = output
PORTC.DIR = 0x00; // PORT C = input
RTC.CTRLA = RTC_RTCEN_bm; // Enable RTC
PORTB.OUT = 0x00; // Ensure all LEDs start turned off
// ^(not necessary but I do it just in case)^
//testLED(); <-- previous test I used to make sure each LED works upon start-up
while(1)
{
testRotaryInput();
}
}
The idea here is that whichever output line arrives at the AVR first should indicate which direction the rotary encoder was rotated, as this dictates the phase shift between the two signals. Depending on the direction of rotation (or lackthereof), a red/green/blue LED will blink once for 500ms, and then the program will wait another 500ms before listening to the rotary encoder output again. However, when I run this code, the LED will either continuously blink red for awhile or green for awhile, eventually switching from one color to the other with the occasional (single) blue blink. This seems completely random each time, and it seems to completely ignore any rotation I apply to the rotary encoder.
Things I've done to troubleshoot:
Hooked up both outputs of the rotary encoder to an oscilloscope to see if there's any output (everything looked as it should)
Used an external power supply to power the rotary encoder, as I was only reading 1.6V from the 5.0V VCC pin on my ATtiny817-XPRO when it was connected to that (I suspect this was because the LEDs and rotary encoder probably draw too much current)
Measured the voltage from said power supply to ensure that the rotary encoder was receiving 5.0V (I measured approx. 4.97V)
Checked to make sure that the circuitry is correct and working as it should
Unfortunately, none of these things eliminated the problem at hand. Thus, I suspect that my code may be the culprit, as this is my first attempt at using a rotary encoder, let alone interpreting the data generated by one. However, if my code looks like it should work just fine, I'd appreciate any heads-up on this so that I can focus my efforts elsewhere.
Could anyone shed light on what may be causing this issue? I don't think it's a faulty board because I was using these pins two nights ago for a different application without any problems. Also, I'm still somewhat of a newbie when it comes to AVRs, so I'm sure my code is far from being as robust as it could be.
Thanks!
Encoders can behave in various strange ways. You'll have signal bounces like with any switch. You'll have cases where several inputs may be active at once during a turn. Etc. Therefore you need to sample them periodically.
Create a timer which polls the encoder every 5ms or so. Always store the previous read. Don't accept any change to the input as valid until it has been stable for two consecutive reads. This is the simplest form of a digital filter.
Which input is shorted does not show you what direction the encoder was turned. But the order in which they were shorted does.
Normally, rotary encoders have two outputs which are shorted to the ground pin: first shorted, then second shorted, then first released, then second released - this full sequence happened between each click. (Of course there are encoders which have additional "click" in the middle of the sequence, or have no clicks at all, but most of them do like described above).
So, generally speaking, each "CLICK!" movement you may consider as 4 phases:
0. Both inputs are released (high) - default position
1. Input A is shorted to ground (low), input B is released (high)
2. Both inputs are shorted (low)
3. Input A is released (high), B is shorted (low).
Rotation in one direction is a passage thru phases 0-1-2-3-0. Another direction is 0-3-2-1-0. So, whatever direction the encoder is rotated, both inputs will be shorted to ground at some particular moment.
As you can see from the picture above, usually the bouncing happens only at one of inputs. So, you may consider the bouncing as jumping between two adjacent phases, what makes the debounce much simpler.
Since those phases are changes very fast you have to pool input pins very fast, may be 1000 times per second, to handle fast rotations.
Code to handle the rotation may be as follows:
signed int encoder_phase = 0;
void pull_encoder() {
int phase = ((PORTC.IN & (1 << INPUT_A_PINNO)) ? 0 : 1)
^ ((PORTC.IN & (1 << INPUT_B_PINNO)) ? 0 : 0b11);
// the value of phase variable is 0 1 2 or 3, as described above
int phase_shifted = (phase - encoder_phase) & 3;
if (phase_shifted == 2) { // jumped instantly over two phases - error
encoder_phase = 0;
} else if (phase_shifted == 1) { // rotating forward
encoder_phase++;
if (encoder_phase >= 4) { // Full cycle
encoder_phase = 0;
handle_clockwise_rotation();
}
} else if (phase_shifted == 3) { // rotating backward;
encoder_phase--;
if (encoder_phase <= -4) { // Full cycle backward
encoder_phase = 0;
handle_counterclockwise_rotation();
}
}
if (phase == 0) {
encoder_phase = 0; // reset
}
}
As others have noted, mechanical encoders are subject to bouncing. You need to handle that.
The most simple way to read such an encoder would be to interpret one of the outputs (e.g. A) as a 'clock' signal and the other one (e.g. B) as the direction indicator.
You wait for a falling (or rising) edge of the 'clock' output, and when one is detected, immediately read the state of the other output to determine the direction.
After that, include some 'dead time' during which you ignore any other edges of the 'clock' signal which occur due to the bouncing of the contacts.
Algorithm:
0) read state of 'clock' signal (A) and store ("previous clock state")
1) read state of 'clock' signal (A) and compare with "previous clock state"
2) if clock signal did not change e.g. from high to low (if you want to use the falling edge), goto 1).
3) read state of 'direction' signal (B), store current state of clock to "previous clock state"
4) now you know that a 'tick' occurred (clock signal change) and the direction, handle it
5) disable reading the 'clock' signal (A) for some time, e.g. 10ms, to debounce; after the dead time period has elapsed, goto 1)
This approach is not time critical. As long as you make sure you poll the 'clock' at least twice as fast as the shortest time between the change of signal A and the corresponding change of signal B (minus bouncing time of A) you expect to see (depends on maximum expected rotation speed) it will work absolutely reliably.
The edge detection of the 'clock' signal can also be performed by a pin change interrupt which you just disable for the dead time period after the interrupt occurred. Handling bouncing contacts via a pin change interrupt is however generally not recommended because the bouncing (i.e. (very) fast toggling of a pin, can be pulses of nanoseconds duration) may confuse the hardware.
I am working on a project to control switching of gates for an inverter with python in Raspberry Pi.
There is this sequence of pins I need to turn off and on in a cycle to achieve this with some time delay inbetween for adjusting the frequency. But as I wanna develope my code to next level, now I would like to adjust the delay in loop in real time to adjust the frequency ,I am trying to change the speed of the execution of the loop without stopping the loop itself in real time, is there any way to do this in python?
I am writing an preemptive kernel in C and assembly. I've been looking at and setting up timer interrupts through the PIT and the PIC but one thing I am utterly unable to find an answer on.
We have initilized the 8254 chip to be counting on counter 0 in mode 2. We set it to fire an interrupt on IR0 on the PIC every 10 ms. After that we enable the IR0 on the PIC and things work as intended.
However lets say at certain conditions we want to alter the time that the PIT fires at by feeding it a new value. Or just restart the counter midcounting.
The intel manual for the chip has some detail on the gate and using it to restart the counter by getting a rising edge on the gate.
THe manual also says that if we give the counter a new value it doesn't reset the counter until after the current counting sequence is finished unless a trigger (rising edge on the gate) happens before the counting is over.
The manual also says that sending a new CW to the chip would reset the counter, however I don't believe this is the optimal way of restarting or altering the counter.
So the question is, how would this be done in either c or assembly? (We got full write access whenever we want).
To not leave a question unanswered and as I've somewhat of an answer I'll answer it myself.
As far as I've understood the chip has 3 counters but only counter 2 (we start counting at 0) has the gate pin connected (and this one has it connected to the speaker). As a result counter 0 which is the real timer counter doesn't have a connection on the gate which means we can't cause a trigger on it after sending it a new value.
This means that sending a value to it and then restarting it on the value before the timer is up is impossible without sending it new ICW.
In case we want to reset the timer when we get out of an interrupt caused by the 8259 chip which the 8254 is connected to at the end of the handling of that interrupt (that is we don't want the time to be running during the actual interrupt) we would be best of changing the mode to mode 0 which doesn't restart the timer on terminal count and then just manually restart it with the time we want to use for it each time we are about to end and interrupt.