Schmitt Buffer/Trigger with Encoder and buttons - Inverting... - Page 1
Forum section: electronics / general

author: elcrni
author_rank: Regular Contributor
timestamp: March 16, 2021, 12:16:11 pm
content: hi all,In a middle of a PCB design and wanted to make things proper with my buttons and rotary encoder. So apart from RC filter, i would also like to include Schmitt Triger for a proper signal of the encoder and buttons.After researching the matter for a bit, i am still a little confused on "inverting" Schmitt Triger.My schematic looks like this, except i use 10k resistors and 1uF tantalum caps:So a few questions:1. I would like to convert (end debounce) signal from encoder and buttons, so i was thinking of getting this one:74HC14DHowever, this one says "inverter", and i am aware that i can also get inverted signal output, but here i need non inverted signal. So the question is, would this one work for my case?2. If i do have inverted signal, would that mean simply swapping the contacts on the switch to work as non inverting?3. 74HC14 has 6 IN/OUT pins, can i use 2 pins for encoder and remaining 4 for 4 buttons? All on one IC? Or is it better to separate for whatever reason?4. I can use 74HC14 for mechanical toggle switches as well? I see no reason why not.Many thanks in advance,Alek

author: Zero999
author_rank: Super Contributor
timestamp: March 16, 2021, 12:36:32 pm
content: There are non-inverting Schmitt trigger ICs such as the, 74HC2G17 and 74HC7014, or use two 74HC14 in series to get non-inverting.Is this going to a microcontroller? If so, check it has Schmitt trigger inputs, then you don't need the 74HC14. Another thing to consider is doing the de-bouncing in software, so no additional components are required.

author: elcrni
author_rank: Regular Contributor
timestamp: March 16, 2021, 12:51:04 pm
content: Thanks Zero999,Yes, the signal is going to Arduino Mega, i am not sure it has Schmitt input as such.Also, i would like to avoid software debouncing as my code is decoding radio signals so i would like to keep it as light as possible.Many thanks,AlekP.S. i can use both encoder and switches on the same 74HC14 IC at the same time?

author: elcrni
author_rank: Regular Contributor
timestamp: March 16, 2021, 01:01:11 pm
content: also, if i am using inverting Schmitt IC, i could simply tell in the code that HIGH is LOW and vice versa?I have found this one though:https://www.mouser.at/ProductDetail/771-HC7014D112Many thanks,Alek

author: MarkF
author_rank: Super Contributor
timestamp: March 16, 2021, 01:30:05 pm
content: It's really not going to matter if the encoder lines are inverted or not.Just so you do the same to BOTH lines.You could invert them back in either software or hardware if you like.The bigger issue I see it that you're not using a RC filter.Just the capacitor.The problem is that the encoder switches will present a short across the capacitors when they close.Creating a non-current limited discharge. Thus, reducing the life of the encoder contacts.Use the full RC filtering as shown in some datasheets (i.e. PEC11 encoder datasheet for example).Edit:Also 1uF capacitors are pretty large (slowing down the rotation response.I've found 10nF to 47nF range the best choice with the 10K resistors.

author: elcrni
author_rank: Regular Contributor
timestamp: March 16, 2021, 01:39:41 pm
content: Thanks MarkF!Yes, that's the way i have it, sorry my scheme doesn't show that resistor on the signal line, just the pull-up one.But yes, i am using RC filter the way you have it in your scheme, but, i have 220 ohm resistor on the signal line instead of 10k.Many thanks,Alek

author: elcrni
author_rank: Regular Contributor
timestamp: March 16, 2021, 01:41:33 pm
content: Quote from: MarkF on March 16, 2021, 01:30:05 pmI am not worries about the encoder inverting the signal, but the few switches i would like to connect to Shcmitt as well. but i guess inverting those signals back should not be an issues either. Or i can just go with non-inverting IC.Many thanks,Alek

author: MikeK
author_rank: Super Contributor
timestamp: March 16, 2021, 01:54:09 pm
content: The Schmitt Trigger isn't going to do the debouncing for you. You should probably learn how to do it in software. Take a look at the ganssle article on switch debouncing.

author: elcrni
author_rank: Regular Contributor
timestamp: March 16, 2021, 01:56:48 pm
content: Quote from: MikeK on March 16, 2021, 01:54:09 pmI like to do it in hardware, and i think RC filter did a great job in de-bouncing, Schmitt would be there just to iron out already debounced signal. Or am i completely off on this one?thanks,Alek

author: Zero999
author_rank: Super Contributor
timestamp: March 16, 2021, 02:04:05 pm
content: The 74HC7014 has six chanels, which can be be used for switches, as well as the encoder. There's also the 74HC7541, which has eight channels.https://uk.rs-online.com/web/p/buffers/4836689/I should have said, I can't see the schematic, as I'm at work at the moment and the firewall blocks the image hosting site. You might be able to invert the switching signals by changing the pull-ups for pull-downs, or vice-versa.I don't know whether the Ardunio Mega has Schmitt trigger inputs or not. Look at the data sheet of the microcontroller it uses.

author: julian1
author_rank: Frequent Contributor
timestamp: March 16, 2021, 10:22:12 pm
content: The rotary encoder has a 90degree relationship between edges, which provides lots of setup and hold time, when sampling levels. Unlike a (tactile) switch, I *don't think* hardware debouncing actually gains you anything - regardless of whether decoding is done software or hardware.

author: Buriedcode
author_rank: Super Contributor
timestamp: March 16, 2021, 10:58:29 pm
content: If you don't want inverted outputs - just use two inverters in series?Also, with regards to reading rotary encoders, I've found sampling is far more reliable than running on interrupts. Sure, you can use interrupts at the start to interrupt your code to tell it someone is using the encoder, but a timer, periodically checking the status at a reasonable rate (200-500Hz?) once its detected provides more reliable results. Micros run so fast that routines that just constantly check the encoders output are effectively sampling at MHz and will pick up glitches.Of course, an RC filter + schmitt trigger will help to cut these down, but you have to handle the encoder in software anyway, and you might have to play around with RC values if you want to get nice "clean" phasing between the two channels of the encoder. It's extra hardware for something that can (and should) be done in software. I generally just put on 10k/2.2n and connect straight to an IO.

author: MarkF
author_rank: Super Contributor
timestamp: March 17, 2021, 12:08:01 am
content: Quote from: elcrni on March 16, 2021, 01:39:41 pmA 220 Ω resistor and a 47nF capacitor really are NOT doing much for you.Remember the RC cutoff frequency is: f = 1 / (2 * π * R * C)220Ω and 10nF = 15000 Hz (very little impact on response)10KΩ and 10nF = 1600 Hz (recommended values by Bourns PEC11 encoders)10KΩ and 47nF = 340 Hz (okay if you don't turn the knob very fast)

author: elcrni
author_rank: Regular Contributor
timestamp: March 17, 2021, 09:41:41 am
content: Many thanks for all your help guys! Lots of helpful tips.As for the encoder, i have always and exclusively used it on interrupt pins, which was ok for most of the projects but now i have a radio signal encoder and analyzer, and once i activate encoder and start turning the knob, i get radio signal decoding errors, i would guess this is due to the fact that interrupt is stopping other processes to execute encoder input. So, i would gladly try the other way suggested by "Buriedcode"... but not sure how the code would look like, will search for examples.thanks again guys!Alek

author: MarkF
author_rank: Super Contributor
timestamp: March 17, 2021, 11:10:49 am
content: I assume you're saying 'interrupt on change'?In which case, you will get many many interrupts if the encoder is noisy.I use a fixed 500Hz interrupt to sample the encoder lines and only decode the encoder gray_code setting a global variable with a positive or negative number with the number of turns/detentes. Increment the global count for clockwise turns and decrement the count for counter-clockwise turns.After which, you use the global count outside the interrupt routine (i.e. the main loop) and clear it for the interrupt routine to restart the counting. Inside the main loop, I copy the global count into a local variable and clear the global variable right away to avoid missing any counts. Use the local variable count inside the main loop. Note that you will NOT be processing each individual change but the number of counts since your last pass through the main loop.The 500Hz sample rate works pretty well for me. I would not sample much slower the 500Hz. A 'fixed' sample rate avoids bursts of interrupts from noisy encoder lines. You could almost consider it a form of debounce because the encoder lines can settle some between samples.

author: elcrni
author_rank: Regular Contributor
timestamp: March 17, 2021, 01:28:28 pm
content: Thanks MarkF!I have tried different approach now and moved off Interrupt pins.My encoder code now runs inside the main loop (i guess at 16MHz) on atmega2560 controller.I need to slow this way down as there is no need for it to run this fast.Again, i am seeing errors in radio signal decoding probably due to the fact that encoder now is running inside main loop.how do i slow down encoder checking rate/speed to something more reasonable, like 500Hz? (assuming someone has experience with arduino code)Code:void encoder_brightness(){state=(state<<1) | digitalRead(CLK_PIN) | 0xe000;if (state==0xf000){state=0x0000;if(digitalRead(DATA_PIN))counter++;elsecounter--;if (counter>15) {(counter = 15);}if (counter<0) {(counter = 0);}}and encoder_brightness() runs inside main loop.I know we are now outside my main topic but i now when we are at it, i would really like to sort this one out.Many thanks,Alek

author: MarkF
author_rank: Super Contributor
timestamp: March 17, 2021, 04:53:47 pm
content: See if this helps:And a little pseudo code as an outline:Code:// global variablesint count=0; // encoder increment/decrement count//************************************************************void setup(){// Set encoder pins as input. . .// Setup Timer 1 for 500Hz. . .// Enable interruptssei();}//************************************************************void loop(){int local_count;if (count != 0) {// Get encoder turn count and reset it for future turnslocal_count = count; // get encoder turnscount = 0; // reset global turn count// Use local_count to control your object, brightness or whatever. . .encoder_brightness(local_count); // example: increase or decrease brightness by local_count amount}// Do other non-encoder related processing. . .}//************************************************************ISR(TIMER1_CMPA_vect){// Read encoder lines. . .// Decode incrementing or decrementing countif (clockwise turn)count++;else if (counter_clockwise turn)count--;}

author: elcrni
author_rank: Regular Contributor
timestamp: March 17, 2021, 05:50:15 pm
content: thanks man, thats some nice material.i am not too familiar with interrupt timers, so will have to dive in more.on a side note, what would happen if i just use millis and set it up to 2 millis for encoder checkup?still sort of 500Hz...?thanksalek

author: MarkF
author_rank: Super Contributor
timestamp: March 17, 2021, 06:20:33 pm
content: Quote from: elcrni on March 17, 2021, 05:50:15 pmThat would allow you to approximate reading and decoding the encoder pins.It will NOT however allow you to do other tasks.Not worth the effort in my opinion.I typically do non-critical processing in the loop() function (such as updating a display screen) while sampling user inputs in the interrupt service code (basically just setting global flags to be used in the loop() function)(minimal processing in the interrupt code).I would concentrate on setting up the code and the interrupt service routine (ISR). You can toggle a pin inside the ISR() routine to check your setup and timing. Then, add the encoder capability afterwards when you are confident with the basic code layout and timing. Remember: the ISR() function MUST complete BEFORE the next interrupt occurs.One key edit to my previous pseudo code: Add check for (count != 0), so you only execute that code within the loop when a change has been detected. (see edited code)

author: elcrni
author_rank: Regular Contributor
timestamp: March 18, 2021, 01:59:35 pm
content: ok, i took this the other way around, haha. First did the encoder but now hang at timing, still not sure how to pull this off.my current code now goes as follows:Code:#include <EEPROM.h>#include <LedControl.h>#include <ezButton.h>#include <TimeLib.h>#include <RTClib.h>#include <OneWire.h>#include <DS1307RTC.h>#define MAXIMCCLD 12 // output - CS/LOAD#define MAXIMCCCLK 11 // output - CLOCK#define MAXIMCCDATA 10 // output - DATA#define DS1307_I2C_ADDRESS 0x68 // define the RTC I2C addressezButton button(7); // create ezButton object that attach to pin 7;LedControl MaximCC=LedControl(MAXIMCCDATA, MAXIMCCCLK, MAXIMCCLD, 1); // Define pins for Maxim 72xx and how many 72xx we useint LED = 5;int LED2 = 9;volatile signed int counter_hour_start = EEPROM.read(2);volatile signed int counter_minute_start = EEPROM.read(3);volatile signed int counter_hour_end = EEPROM.read(4);volatile signed int counter_minute_end = EEPROM.read(5);// usually the rotary encoders three pins have the ground pin in the middleenum PinAssignments {encoderPinA = 2, // rightencoderPinB = 3, // left};// a counter for the dialsigned int lastReportedPos_hour_start = 1; // change managementsigned int lastReportedPos_minute_start = 1; // change managementsigned int lastReportedPos_hour_end = 1; // change managementsigned int lastReportedPos_minute_end = 1; // change management// interrupt service routine varsboolean A_set = false;boolean B_set = false;int previousSecond = 0;int tensofhours, singlehours, tensofminutes, singleminutes,tensofhours1, singlehours1, tensofminutes1, singleminutes1;//==============================================================================// SETUP//==============================================================================void setup() {pinMode(LED, OUTPUT);pinMode(LED2, OUTPUT);pinMode(encoderPinA, INPUT);pinMode(encoderPinB, INPUT);// encoder pin on interrupt 0 (pin 2)attachInterrupt(0, doEncoderA, CHANGE);// encoder pin on interrupt 1 (pin 3)attachInterrupt(1, doEncoderB, CHANGE);MaximCC.shutdown(0,false);MaximCC.setIntensity(0,15);MaximCC.clearDisplay(0);button.setDebounceTime(50); // set debounce time to 50 millisecondsbutton.setCountMode(COUNT_FALLING);setSyncProvider(RTC.get); // the function to get the time from the RTCtensofhours = counter_hour_start / 10;singlehours = counter_hour_start % 10;tensofminutes = counter_minute_start / 10;singleminutes = counter_minute_start % 10;tensofhours1 = counter_hour_end / 10;singlehours1 = counter_hour_end % 10;tensofminutes1 = counter_minute_end / 10;singleminutes1 = counter_minute_end % 10;}//==============================================================================// LOOP//==============================================================================void loop() {tasksEverySecond();button.loop(); // MUST call the loop() function firstunsigned long count = button.getCount();if (count>0) {encoder_alarm();}}//==============================================================================// tasksEverySecond//==============================================================================void tasksEverySecond(){if (second() != previousSecond){previousSecond = second();displayRtcTime();ledTest();}}//==============================================================================// ENCODER ALARM//==============================================================================void encoder_alarm(){unsigned long count = button.getCount();// reset button count when we cycle through all digit groups and set an alarmif (count > 6) {button.resetCount();digitalWrite(LED, LOW);}//--------------------- HOURS alarm STARTS at ------------------------//if (lastReportedPos_hour_start != counter_hour_start) {if (counter_hour_start>23) {(counter_hour_start = 0);}if (counter_hour_start<0) {(counter_hour_start = 23);}tensofhours = counter_hour_start / 10;singlehours = counter_hour_start % 10;lastReportedPos_hour_start = counter_hour_start;}EEPROM.update(2,counter_hour_start);//--------------------- MINUTES alarm STARTS at ------------------------//if (lastReportedPos_minute_start != counter_minute_start) {if (counter_minute_start>59) {(counter_minute_start = 0);}if (counter_minute_start<0) {(counter_minute_start = 59);}tensofminutes = counter_minute_start / 10;singleminutes = counter_minute_start % 10;lastReportedPos_minute_start = counter_minute_start;EEPROM.update(3,counter_minute_start);}//--------------------- HOURS alarm ENDS at ------------------------//if (lastReportedPos_hour_end != counter_hour_end) {if (counter_hour_end>23) {(counter_hour_end = 0);}if (counter_hour_end<0) {(counter_hour_end = 23);}tensofhours1 = counter_hour_end / 10;singlehours1 = counter_hour_end % 10;lastReportedPos_hour_end = counter_hour_end;}EEPROM.update(4,counter_hour_end);//--------------------- MINUTES alarm ENDS at ------------------------//if (lastReportedPos_minute_end != counter_minute_end) {if (counter_minute_end>59) {(counter_minute_end = 0);}if (counter_minute_end<0) {(counter_minute_end = 59);}tensofminutes1 = counter_minute_end / 10;singleminutes1 = counter_minute_end % 10;lastReportedPos_minute_end = counter_minute_end;EEPROM.update(5,counter_minute_end);}//--------------------- 7 Segment display ------------------------//if (count == 1 || count == 6) {MaximCC.setChar(0,7,tensofhours,false);MaximCC.setChar(0,6,singlehours,true);MaximCC.setChar(0,5,tensofminutes,false);MaximCC.setChar(0,4,singleminutes,true);MaximCC.setChar(0,3,tensofhours1,false);MaximCC.setChar(0,2,singlehours1,true);MaximCC.setChar(0,1,tensofminutes1,false);MaximCC.setChar(0,0,singleminutes1,true);digitalWrite(LED, HIGH);}if (count == 2) {MaximCC.setChar(0,7,tensofhours,false);MaximCC.setChar(0,6,singlehours,true);} else if (count != 1 && count != 6){MaximCC.setChar(0,6,singlehours,false);}if (count == 3) {MaximCC.setChar(0,5,tensofminutes,false);MaximCC.setChar(0,4,singleminutes,true);} else if (count != 1 && count != 6) {MaximCC.setChar(0,4,singleminutes,false);}if (count == 4) {MaximCC.setChar(0,3,tensofhours1,false);MaximCC.setChar(0,2,singlehours1,true);} else if (count != 1 && count != 6) {MaximCC.setChar(0,2,singlehours1,false);}if (count == 5) {MaximCC.setChar(0,1,tensofminutes1,false);MaximCC.setChar(0,0,singleminutes1,true);} else if (count != 1 && count != 6) {MaximCC.setChar(0,0,singleminutes1,false);}}//==============================================================================// displayRtcTime//==============================================================================void displayRtcTime(){unsigned long count = button.getCount();if (count == 0){MaximCC.setChar(0, 7, (hour() / 10), false);MaximCC.setChar(0, 6, (hour() % 10), false);MaximCC.setChar(0, 5, '-', false);MaximCC.setChar(0, 4, (minute() / 10), false);MaximCC.setChar(0, 3, (minute() % 10), false);MaximCC.setChar(0, 2, '-', false);MaximCC.setChar(0, 1, (second() / 10), false);MaximCC.setChar(0, 0, (second() % 10), false);}}//==============================================================================// ledTest//==============================================================================void ledTest(){int midnight;int powersaving;int startTime = counter_hour_start * 100 + counter_minute_start;int endTime = counter_hour_end * 100 + counter_minute_end;int timeNow = hour() * 100 + minute();if (endTime == 0000 && startTime != 0000){endTime = 2359;}if (timeNow >= startTime && timeNow < endTime) {powersaving = true;} else {powersaving = false;}if (powersaving == true){digitalWrite(LED2, HIGH);} else {digitalWrite(LED2, LOW);}}// Interrupt on A changing statevoid doEncoderA() {unsigned long count = button.getCount();// Test transition, did things really change?if ( digitalRead(encoderPinA) != A_set ) { // debounce once moreA_set = !A_set;// adjust counter + if A leads Bif (( A_set && !B_set ) && (count == 2))counter_hour_start += 1;if (( A_set && !B_set ) && (count == 3))counter_minute_start += 1;if (( A_set && !B_set ) && (count == 4))counter_hour_end += 1;if (( A_set && !B_set ) && (count == 5))counter_minute_end += 1;}}// Interrupt on B changing state, same as A abovevoid doEncoderB() {unsigned long count = button.getCount();if ( digitalRead(encoderPinB) != B_set ) {B_set = !B_set;// adjust counter - 1 if B leads Aif (( B_set && !A_set ) && (count == 2))counter_hour_start -= 1;if (( B_set && !A_set ) && (count == 3))counter_minute_start -= 1;if (( B_set && !A_set ) && (count == 4))counter_hour_end -= 1;if (( B_set && !A_set ) && (count == 5))counter_minute_end -= 1;}}Many thanks,Alek

author: MarkF
author_rank: Super Contributor
timestamp: March 18, 2021, 08:19:56 pm
content: Quote from: elcrni on March 18, 2021, 01:59:35 pmSorry.Now you get to re-write it.To start, you DO NOT do interrupt-on-change.Remove all code related to interrupt-on-change.Your interrupt ISR() (which is missing) is called at a fixed rate (I suggest 500Hz).Inside the ISR(), it would be best the have both encoder lines on the same Mega port and to read the Port to get both bits simultaneously.Single read of the port instead of two pin reads. (i.e. read the port --> then mask the two bits --> decode gray code --> increment/decrement turn count.)To get started, view the video and setup the ISR() routine. The interrupt and setup in the video (near the end of thee video) is for an UNO and needs to be slightly modified for the Mega.Download theMega datasheetand refer to section 17.Then, test the code to insure the ISR() is called at 500Hz (or the rate you decide upon).

author: nctnico
author_rank: Super Contributor
timestamp: March 18, 2021, 08:25:24 pm
content: Quote from: elcrni on March 17, 2021, 09:41:41 amYeah, you just learned never to use interrupts on GPIO pins the hard way. Never ever use interrupts on GPIO pins because you can't predict when the interrupts will happen and thus the timing of your software is no longer predictable. There have been lengthy threads on this forum about handling a rotary encoder in software.https://www.eevblog.com/forum/microcontrollers/using-a-rotary-encoder/

author: MarkF
author_rank: Super Contributor
timestamp: March 18, 2021, 09:28:33 pm
content: Also, remove void doEncoderA() and void doEncoderB() functions.They will be replaced by an ISR(TIMER1_CMPA_vect) function.This ISR() function will both read the encoder pins and the encoder switch (if it has one) and then setting a global turn count variable and a global switch depressed/released flag.But as I said a few times now, you need to implement a timer interrupt by configuring the timer and enabling its interrupt in the setup() function and writing the ISR() function to read the encoder pins.

author: elcrni
author_rank: Regular Contributor
timestamp: March 18, 2021, 09:50:24 pm
content: DOH!Oh well, back tp the drawing board, i have a feeling this will be a long weekend.Trying to find a close example of a code to go buy, a bit out of my skill level to start from scratch, but maybe a time to learn something new.Thanks guys!Alek

author: MarkF
author_rank: Super Contributor
timestamp: March 18, 2021, 10:10:14 pm
content: Quote from: elcrni on March 18, 2021, 09:50:24 pmI gave you some pseudo code. It's 80% of the code.Just fill in the functions from the example video with small changes related to the Mega.(i.e. the register names for Timer1 are slightly different and the register bits may be different)Plus, you will need to calculate new times to get 500Hz.You're making a mountain out of a mole hill.

author: elcrni
author_rank: Regular Contributor
timestamp: March 18, 2021, 11:41:31 pm
content: Something like this, seems to work... again, uses 2 pins, not sure how i do it with one pin though.Code:#define encoderA 2#define encoderB 3volatile int8_t altAB = 0;volatile int encoderCount = 0;int8_t steps = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};ISR(TIMER1_COMPA_vect) {altAB <<= 2;altAB &= B00001100;altAB |= (digitalRead(encoderA) << 1) | digitalRead(encoderB);encoderCount += steps;}void setup() {pinMode(encoderA, INPUT);pinMode(encoderB, INPUT);noInterrupts();TIMSK1 |= (1<<OCIE1A);TCCR1A = 0;TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10);// Frequency = 16000000 / 1024 / 31 = sampling rate;OCR1A = 31;interrupts();}void loop() {}btw, i would love to test this frequency but not sure how to connect the oscilloscope, to what pins that isMany thanks,Alek

author: MarkF
author_rank: Super Contributor
timestamp: March 19, 2021, 12:42:27 am
content: You will have to check my code.I typically use Microchip PIC processors and rarely do any Arduino coding.Answer to #1:Two pins, one read.You have pins 2 and 3 for the encoder.I believe these are PortB bits 2 and 3.Let's assume the encoder switch is on pin 1 (PortB bit 1) with pull-up resistor.Answer to #2:To check the ISR() timing with a Mega pin, pick an unused pin and toggle it inside the ISR() routine.Let's pick pin 0 (PortB bit 0) for timing check.Hence:Code:// global variablesint count=0; // encoder turn count used in loop() functionuint8_t sw0=0; // encoder switch flag used in loop() functionuint8_t last_sw0=0;ISR(TIMER1_COMPA_vect) {uint8_t pins, pinA, pinB, pinS;digitalWrite(0, HIGH); // set pin 0 high when entering ISR(), check this pin with scope// Read encoderpins = PORTB; // Read both encoder inputs at once to avoid any time delay between pin samplingpinS = (pins >> 1) & 0x01; // encoder switchpinA = (pins >> 2) & 0x01; // encoder ApinB = (pins >> 3) & 0x01; // encoder B// Decode gray_code. . .// Encoder switchif (last_sw0 != 0 && pinS == 0) {sw0 = 1; // set switch flag when depressed}last_sw0 = pinS;digitalWrite(0, LOW); // set pin 0 low when leaving ISR()}void loop() {int local_count;if (count != 0) {local_count = count;count = 0; // reset encoder turn count// process encoder turns here using local_count variable. . .}if (sw0) {sw0 = 0; // reset encoder switch flag// process encoder switch depressed. . .}// Do non-encoder tasks here. . .}void setup() {pinMode(0, OUTPUT); // scope timing checkpinMode(1, INPUT); // encoder switchpinMode(2, INPUT); // encoder ApinMode(3, INPUT); // encoder B}

author: MarkF
author_rank: Super Contributor
timestamp: March 19, 2021, 12:55:31 am
content: In order to read Port B, I believe the command should bepins = PINB;and notpins = PORTB;

author: elcrni
author_rank: Regular Contributor
timestamp: March 19, 2021, 01:15:29 am
content: Many thanks MarkF.In a meantime i got the code changed:Code:#define encoderA 2#define encoderB 3volatile int encoderCount = 0;boolean A_set = false;boolean B_set = false;ISR(TIMER1_COMPA_vect) {if ( digitalRead(encoderA) != A_set ) { // debounce once moreA_set = !A_set;if ( A_set && !B_set )encoderCount ++;}if ( digitalRead(encoderB) != B_set ) {B_set = !B_set;if ( B_set && !A_set )encoderCount --;}}void setup() {pinMode(encoderA, INPUT);pinMode(encoderB, INPUT);TIMSK1 |= (1<<OCIE1A);TCCR1A = 0;TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10);// Frequency = 16000000 / 1024 / 15 = sampling rate;OCR1A = 31;sei();Serial.begin(115200);}void loop() {while(true) {Serial.println(encoderCount);delay(100);}}I added Serial just so i can check if it's working.It does work, nicely, although on restart it starts at 1 rather than at 0, for some reason, but it works.Now, will have to study your code, seems more elegant to my untrained eye :-)thanks,Alek

author: MarkF
author_rank: Super Contributor
timestamp: March 19, 2021, 04:26:58 am
content: I would set the prescaler to 256 and the OCR1A to 125. You do not want a fractional divisor.In your case the timing is not critical. Best case is to always try to have an integer divisor.16000000 / 256 / 500 = 12516000000 / 1024 / 500 = 31.25

author: elcrni
author_rank: Regular Contributor
timestamp: March 19, 2021, 12:16:22 pm
content: Alright, changed the prescaler to 256 and now reading ~496Hz on the scope port.I am very happy with this now!Code:#define encoderA 2#define encoderB 3volatile int encoderCount = 0;boolean A_set = false;boolean B_set = false;const int scope_pin =PB0;ISR(TIMER1_COMPA_vect) {if ( digitalRead(encoderA) != A_set ) { // debounce once moreA_set = !A_set;if ( A_set && !B_set )encoderCount ++;}if ( digitalRead(encoderB) != B_set ) {B_set = !B_set;if ( B_set && !A_set )encoderCount --;}PORTB ^= (1<< scope_pin);}void setup() {pinMode(encoderA, INPUT);pinMode(encoderB, INPUT);DDRB |= (1 << scope_pin);TIMSK1 |= (1<<OCIE1A);TCCR1A = 0;TCCR1B = (1<<WGM12) | (1<<CS12);OCR1A = 125;sei();}void loop() {}Many thanks for all your help MarkF!Alek

author: elcrni
author_rank: Regular Contributor
timestamp: March 19, 2021, 04:24:24 pm
content: very strange thing happens, i have just measured frequency on the scope pin again, whatever i do i get 248Hz, half of the 500Hz target... once i remove the ground connection and measure with positive scope probe i get 496Hz...either i am doing something very wrong or my math in the code is no good...Thanks,Alek

author: MarkF
author_rank: Super Contributor
timestamp: March 19, 2021, 04:49:27 pm
content: Quote from: elcrni on March 19, 2021, 04:24:24 pmThink about what your code is doing...Hint: Every time you enter the ISR() routine you toggle the scope_pin.If you set the scope_pin when you enter the ISR() and then clear the scope_pin when you exit the ISR(), you will be able to measure the time spent inside the ISR().Question: Why are you doing Port writes for the scope_pin and bit reads for the encoder lines?1) Doing the Port write for the scope_pin effects ALL bits in that port.2) I would expect the opposite. Bit write for scope_pin and port read for encoder bits.Ideally, doing a port read for the encoder will avoid time skew of encoder_A and encoder_B lines. All be it, the time delay should be very small. You never really know how much overhead is inside the digitalRead() function.

author: elcrni
author_rank: Regular Contributor
timestamp: March 19, 2021, 05:11:13 pm
content: if i do it like you suggested:Code:uint8_t pins, pinA, pinB, pinS;digitalWrite(0, HIGH); // set pin 0 high when entering ISR(), check this pin with scope-----------------digitalWrite(0, LOW); // set pin 0 low when leaving ISR()I get no readout at all on pin0 (digital pin 53 on mega2560)As for the port/pin part of it, i am still trying to get a hang of it, still a bit confusing so i am basically shooting in the dark here :-)Many thanks,Alek

author: elcrni
author_rank: Regular Contributor
timestamp: March 19, 2021, 05:22:01 pm
content: ok, just changed your pin0 to pin53 and i have a reading of ~500Hz now. this should be it.json object validatorOff to the next thing, pin/port confusion and trying to figure out your encoder code.Thanks,Alek

author: MarkF
author_rank: Super Contributor
timestamp: March 19, 2021, 06:12:51 pm
content: The short story:1) digitalRead(), digitalWrite(), pinMode() functions operate on an individual pin2) PINx, PORTx, DDRx operate on multiple pins at once.The number of pins effected depends on the width of the port (up to and including 8 pins).