How could I deal with an 8bit rollover from an external counter? I have a quad decoder that im gonna use in 8bit parallel mode and use PIND to read it,
Im planning on using the difference in one reading to the next to add or subract to a number but im stumped on rollover, if the counter is either at its max or min and I cause it to rollover in either direction how will I get an accurate count on the difference?
So like if its @3 and I turn it backwards to 255 the difference will be -252 instead of -4, which would cause alot of jumping around and undesirable effects
any ideas?
It it has only overflowed once you can test for the new value < old value and add 256.
After the second rollover you're screwed though unless you can detect the events and accumulate them.
Rob
This might not be possible depending on how many pins and interrupts you have free, which pins are taken, etc. but I would try to trigger a "both edge" interrupt with the LSB of you parallel interface. In the interrupt routine you would increment variable x. Then when you decide to read the value of the parallel interface, you would do some simple calculations to find out whch way you turned the knob.
Example: knob was at 10, which is stored in variable oldInput, turned 11 click backwards to 255, this is stored in newInput. This means the interrupt would have fired 11 times and you would have 11 in x. Then all you do is 2 if statements. Code:(not tested)
if (oldInput - x == newInput){//255, correct
dir = backwards;
}else if(oldInput + x == newInput){//21, incorrect
dir = forewards;
}
oldInput = newInput;
x = 0;//reset it for a new calculation
oldInput and newInput should be unsigned bytes, x can be a volatile int. There's probably an easier way to do it but this is the first thing that came to mind.
EDIT: You didn't say which arduino you're using, so I'm assuming it's an Uno. The only interrupts are on PD2 and PD3 so you won't be able to hook up the parallel interface with pin 0 to PD0, pin 1 to PD1 etc., as the LSB (pin 0) would have to be on PD2 or PD3. This could be easily fixed in software though so the idea is still feasible.
Oops, I didn't read the both directions part ![]()
It's a quad encoder right, so there are 16 possibilities on the reading. Just a thought, have an array of 16 bytes
byte myArray [16] = {
0x1, // if 0th element == the new reading count up else count dn
0x2, // 1
0x3, // 2
0x4, // 3
...
0x0 // F
};
new_reading = getNewReading();
if (myArray [last_reading] == new_reading) // assumes the count can never skip a number
count_up();
else
count_dn();
last_reading = new_reading;
Rob
It is a quad encoder im using but I have an ic to decode that for me and save me the software load of counting it, I just need to figure out how to solve this rollover event(s)
Well first of all, a change from 4 to 255 would be ambiguous. It could be going +252 or -4. But if you read sufficiently fast than you could do something like this:
thisValue = readNewValue(PIND);
difference = thisValue - lastValue;
if (difference > 128) {
difference -= 256;
} else if (difference < -128) {
difference += 256;
}
lastValue = thisValue;
// Difference should be corrected
// Loop to get the next difference
This is untested and somewhat of pseudocode, but hopefully it shows the idea. Also, this would require you to read faster than the encoder can change by 128 to avoid ambiguity.
Im gonna try out that pseudocode, I still have to get the hardware all setup to debug so idk how fast yet ill be able to count or how fast it counts, its optical so I can't feel the steps nor could I find the datasheet from the 20yo encoder im using so I don't know how fast it will be counting if I were to spin it at different speeds
luckily all the mcu will be doing is monitoring this port, modifying a timer, and displaying the number on the lcd
hopefully it will be fast enough, I wont relly know till the time comes tho
I got code successfully reading the parallel output and modifing a number, tho it can overflow like 70 times a second
Which is good in certain aspects since it allows me to change my number by 9000 every second without scaling, but im afraid as my program grows it will start to miss the overflow in which ill start to have problems
What do you think would be the fastest way to find the difference from old value to new?
I know the current idea works, I just want it to work efficiently as possible,
im still going to streamline the rest of my code, but it couldn't hurt to have more headroom
Can you post the current version of your code?
Rob
Ok so here's my complete code so far just taking the input from the decoder, modyfieng a number while checking for boundries, then applying that number to timer 1
I clocked the loop at about 250k cycles per second with this code but when I add a single analog read it drops to 8k cycles per second, and that's just one analog reead
im planning on taking analog reading of 2-3 pins, and I usually throw in a few after changing pins, then average a few
ill probably only do that once per second or so but that's gonna eat up alot of time and may cause undesirable hangs
if I can refine / eliminate any of my code plz let me know
#include <avr/io.h>
#include <avr/interrupt.h>
#define CLK 11//PB3
#define OE 8 //PB0
uint8_t quadpos =0;
uint8_t oldquadpos =0;
unsigned long frequency = 600 ;
void setup(){
DDRD =0;
PORTD =0;
Serial.begin(57600);
pinMode(13,1);//clock loop test PB5
pinMode(9,1); //for hbridge
pinMode(10,1);//for hbridge
pinMode(A0,0);
pinMode(CLK,1);//for hctl2000 clk
pinMode(OE,1);//hctl2000
digitalWrite(OE,1);//hctl2000
DFG(frequency);
clksrc();//1Mhzclock
}
void loop(){
PORTB ^= _BV(5);
quadpos = quadread();
if(quadpos != oldquadpos){
int dif = quadpos - oldquadpos;
if(dif > 128) dif -= 256; else
if(dif < -128) dif += 256;
if((frequency + dif) < 1000000) frequency += dif;
DFG(frequency);
oldquadpos = quadpos;
Serial.println(frequency);//temporary with loss of 2 LSB of portD but easier upload
}
}
void DFG(unsigned long tempfreq){
cli();
TCCR1A = 0;
TCCR1B = 0;
TCNT1=0;
TCCR1A |= _BV(COM1A0) + _BV(COM1B0);
TCCR1B |=_BV(WGM12);
TCCR1C = _BV(FOC1A);
if(tempfreq > 122 && tempfreq < 1000001){
OCR1A = (8000000/tempfreq)-1;
TCCR1B |= _BV(CS10);
}
else if(tempfreq <= 122 && tempfreq > 15){
OCR1A = (1000000/tempfreq)-1;
TCCR1B |= _BV(CS11);
}
else if(tempfreq <= 15 && tempfreq > 4){
OCR1A = (125000/tempfreq)-1;
TCCR1B |= _BV(CS10) + _BV(CS11);
}
sei();
}
void clksrc(){
cli();
TCCR2A = 0;
TCCR2B = 0;
TCNT2=0;
TCCR2A |= _BV(COM1A0) + _BV(WGM21);
OCR2A = 0;
TCCR2B |= _BV(CS21);
sei();
}
uint8_t quadread(){
PORTB ^= 1;
uint16_t portD = PIND;
PORTB ^= 1;
portD &= B11111100;//temporary to allow serial
return portD;
}
I hope the code is self explainatory but if needed here's a quick explanation
clksrc() sets up a 1Mhz clock for the hctl2000 quad decoder
dfg() takes the frequency I desire and calculates and changes the timer settings(and I know higher frequencies will skip around due to resolution, im still debating taking the calculations and displaying the true frequency as well as incorporate that into the increments made)
Quadread() sets the OE pin of the hctl as it needs to to read the byte and returns it(the only improvement here I could see is to use pointers to directly modify quadpos)
winner10920:
How could I deal with an 8bit rollover from an external counter? I have a quad decoder that im gonna use in 8bit parallel mode and use PIND to read it,
Im planning on using the difference in one reading to the next to add or subract to a number but im stumped on rollover, if the counter is either at its max or min and I cause it to rollover in either direction how will I get an accurate count on the difference?
So like if its @3 and I turn it backwards to 255 the difference will be -252 instead of -4, which would cause alot of jumping around and undesirable effects
any ideas?
Is it counting so fast that the Arduino can't see every single step?
How about always reading less than 128 counts apart?
With 8 bit unsigned and knowing that from start to end is always less than 128 clicks:
end - start = difference
3 - 255 = 4, ~~~ 4 is < 128, direction is forward
200 - 255 = 201, 201 > 128, direction is backward
Yeah tht part was figured out, used basially the same example someone put out
now im just trying to optimize it, if possible
Well according the the language reference an analogRead takes about 100 microseconds (http://arduino.cc/en/Reference/AnalogRead). So if all you had in loop() was a single analogRead the best possible performance you could hope for (but of course never quite achieve) is 10k cycles per second. That said, 8k isn't bad. How fast do you need it to be?
Lol as fast as possible, id rather streamline it as I go rather than make a Big mess and go back and fix it since I know speed will be an issue
sped it up by 6kcycles just by not reiniating a variable to read the port, realized its not. Library or anything I cn just alter the global variable
Altering the timer1 (constantly rotating the quad) only slows me down 5k cycles so that's good
I think I may have to find a way to speed up analogread or maybe only read when the quad isn't being turned
Found out my quad counts 1000 per turn, which is nice and quick
Oh wow, removed one line of code and the cycles per second doubled, im getting 500k cycles per second
just removed quadpos &= B11111100;
perhaps im streamlined enough XD so my main loop takes 32 clock cycles, and the timer change takes just a few more
Now if only I could get my analog reads in there, and write to an lcd screen and still not skip a turn
winner10920:
Yeah tht part was figured out, used basially the same example someone put out
now im just trying to optimize it, if possible
I don't need to add or subtract 256
uint8_t dif = quadpos - oldquadpos;
if (dif > 128)
{
if (frequency - (unsigned long) dif < lowLimit ) // what happens if frequency goes below 0?
{
frequency = lowLimit;
}
else frequency -= (unsigned long) dif;
}
else
{
if (frequency + (unsigned long) dif > hiLimit )
{
frequency = hiLimit; // I mean since you want to keep frequency <= 1,000,000?
}
else frequency += (unsigned long) dif;
}
So why is the variable frequency unsigned?
if(tempfreq > 122) { // you already limit frequency to 1000000 or less earlier
OCR1A = (8000000/tempfreq)-1;
TCCR1B |= _BV(CS10);
}
else if(tempfreq > 15){
OCR1A = (1000000/tempfreq)-1;
TCCR1B |= _BV(CS11);
}
else if(tempfreq > 4){
OCR1A = (125000/tempfreq)-1;
TCCR1B |= _BV(CS10) + _BV(CS11);
}
// so what happens if tempfreq < 4? maybe that's your lowLimit?
In the function below, what is portD? I don't see it declared anywhere.
uint8_t quadread(){
PORTB ^= 1;
uint16_t portD = PIND;
PORTB ^= 1;
portD &= B11111100;//temporary to allow serial
return portD;
}
As to the analog reads, there is a way to make 'fast' 8 bit analog reads with Arduino.
I just used unsigned since it'll never be negative anyway, and I declared it uint16_t which is a leftover when I used the decoder in 12bit mode but decided to forgo that to save a pin and just read it faster
I revised it and eliminated portD variable and just altered the global variable instead, which saved a little time
Im gonna try your code for the increment/decrement and see if its faster, does the same thing I hope
speed is the goal tho so ill have to try it
Is there a faster way to get the full 10bit resolution? Unfortunately im going to need every last drop of resolution out of the adc, wish it was 12 bit actually
In your opinion what would the fastest way to get ang accurate reading from 3 different anlog pins?
usually I would read the pin 2x nd throw that out then take 10 and average it, then repeat,
how much do u think is neccesary?
I don't know of any way to speed up 10 bit A/D short of using an external chip and then why not at least 12 bits? Some of the more expensive A/D chips are extremely fast too. What I'm not sure of is if a DSP (sound chip) could be used, they handle 44.1 kHz at 16 bits (doesn't have to 44.1) after all. There's probably a few good reasons why not like data width/rate.
You could connect a resistance ladder to parallel to serial shift chips. The read is instant but the data has to come in at SPI speed (fastest way I know) unless you have 10+ pins per analog channel.
I have used an RC circuit fed by analog signal into a digital pin and timed how long that took to bleed to LOW. It can take a long time but the act of measuring doesn't have to block other code.
However, unblocked is going to take up to resolution usecs = up to over 1 msec to get 10 bits.
Using a blocking loop counter and smaller RC, how long while digitalRead, count++? 1023 passes doesn't take long but that's all the code will be doing.
I use digital/analog when low resolution will do but have mucked with long read times for high resolution. I can't say I'm happy with the results but some of that is because of my third rate electronics skills.
If I was really good at electronics, I'd make a resonating circuit that would output a frequency dependent on input voltage. There might be a cheap TTL chip that does that or a PLA/PAL/FPGA setup, maybe even an op-amp circuit?
I. Just wish the adc was faster, its not even the fact of a fast changing signal just im measuring 3 separate low level dc voltages but need as much precision as I can get
one is a high voltge capable voltage divider that will be also used for lower voltages where 1v accuracy means alot more, another is the output of am opamp that also needs a wide resolution to get the full swing but he lower section still needs precision , the third will be a potentiometer that will need the low end precision also
im borderline using nother 328 just for analog reads and displaying info but I have enough pins to do it all on one, and if I added the other ill have two half used chips, plus the latency of communications between the two is undesirable
just single byte serial every couple loops to my computer at 115600 bogs things down to where I don't think it viable
Using a 2nd Arduino is certainly an over kill I think and you wouldn't be any better off resolution wise, but why not an external 12/14/16-bit ADC?
Rob