AGlass0fMilk 5 Posted October 8, 2014 Share Posted October 8, 2014 Hey there guys. First post in a long time. I find myself developing once again on an MSP430G2553 chip on a Rev 1.4 Launchpad and I am trying to take samples from the on-chip ADCs using Energia. It's not secret that the on-chip ADCs are quite unstable (very accurate perhaps?), I don't get anywhere near the kind of jitter I see when using the MCP3008 (10-bit, 8-channel ADC converter IC). Regardless, I am very frustrated with the section of my code that deals with smoothing the signal from analog controls. I am sampling 4 ADC values and averaging it, no problem. What really frustrates me is that when I try to check if the new value is within a certain sensitivity (range) of the last recorded value, I have to do a strange work-around or else I get constant resends as if I'm not even checking if the value is in the certain range. In other words, I cannot use the compare operators greater than, less than, greater than/equal to, and less than/equal to, in order to check if the new ADC value is within a certain range of the old recorded value. To illustrate this, I will include the code I use to do this and show you what I would like to do (which doesn't work) and what I do currently (which still results in some glitches) Just for reference, this is what the Control class looks like: class Control { public: //Identification information int pin; //Sets the pin to be used by the control int ID; //Identification number (0-31) //Data processing variables int value; //Stores the current value int oldValue; //Stores the old value (for preventing constant resends) int counter; //Counter used for averaging int average; //Value use for calculating average int sensitivity; //Value used for setting sensitivity (a variable in smoothing algorithm) @todo //Constructor Control(int nID, int nPin, int nSensitivity); //Methods int Read(void); //Reads the value of the control boolean isNew(void); //Returns true if the value has updated (within a reasonable range, the sensitivity) void Send(void); //Transmits the control change to the computer for processing }; And this is what the Control.Read() function does (it includes the average code) int Control::Read(void) { int pinVal = analogRead(pin); if(pinVal == 1023 || pinVal == 0) //it is max/min { return pinVal; //I do this so that mins/maxs can be sent anyway. It's important that absolute mins/maxs are sent. } if(counter != 3) //Averaging counter that's stored on the class { average += analogRead(pin); counter++; //Increment Counter return 1025; //Not ready value (outside ADC range) } else { average /= 4; //Divide by 4 to average counter = 0; //Reset counter return average; } } } Code that I would like to use: boolean Control::isNew(void) { value = Read(); //Set value of control if(value == 1025) //Not ready return false; //First check for maxs/mins (to prevent resending) if(value == 1023 || value == 0) //it is max/min { if(value != oldValue) //max/min was not sent already { oldValue = value; return true; } } //Check if its within sensitivity setting if(value >= (oldValue-sensitivity) || value <= (oldValue+sensitivity)) //This does nothing to smooth input (constantly returns true, even when it shouldn't) return false; //Still within sensitivity, report false else { oldValue = value; //It's changed more than the sensitivity, update values and report true return true; } } return false; //If it hasn't returned true by now, return false } Code that I use right now... boolean Control::isNew(void) { value = Read(); //Set value of control if(value == 1025) //Not ready return false; //First check for maxs/mins (to prevent resending) if(value == 1023 || value == 0) //it is max/min { if(value != oldValue) //max/min was not sent already { oldValue = value; return true; } } boolean within5 = false; //Please ignore the irrelevant name of this variable int checkVal = 0; //Then check if it's within 15 of previous value for(int i=0;i<15;i++) { checkVal = value + i; if(checkVal == oldValue) { within5 = true; break; } checkVal = value - i; if(checkVal == oldValue) { within5 = true; break; } } if(!within5) //Not within 15 { oldValue = value; return true; } return false; //If it hasn't returned true by now, return false } The main loop does something like this: if(control_1.isNew()) { control_1.Send(); //Simply prints the value to the serial line } Similarly, I have tried to see if the new ADC value is above a certain threshhold as to count it as a maximum value. Above a certain value (like 1015) the sensitivity check becomes erratic, it sends new values even if they're within (for example) 2 of the last value. The code I used was: if(value > 1015) //If it's above a certain threshhold value = 1023; //Count it as a maximum The kind of output I would get is still like: 1020 1022 1018 1023 1023 1020 The sensitivity is normally set to within 10-20 for the on-board ADC. For controls connected to the MCP3008, the sensitivity is about 5, it is as stable as a rock. What is going on? Why can't I compare these values? Is it related to the type of variable I store the value in? I have tried changing the related variables to unsigned integers and it has no effect. It has had me pulling my hair out. I hate inefficient code on MCUs and using for loops simply to check if a value lies within some range seems pretty inefficient to me. I'll post the finished project when I'm done. Thanks guys! Quote Link to post Share on other sites
abecedarian 330 Posted October 9, 2014 Share Posted October 9, 2014 I don't think you want an "OR" in the comparison. No matter what "value" is, it will return true. I.e. if 'value' is 512, 'oldValue' is 508, and the sensitivity is 10, 508-10 is 498, so the >= is true; and 508+10 is 518, so the <= is true as well. But, if 'value' is 19, 508+10 is 518, so the <= comparison is true and therefore the IF statement is true when using "||" / OR. Likewise, if 'value' is 988, 508-10 is 498, so the >= comparison is similarly true. So maybe: if((value >= (oldValue-sensitivity)) && (value <= (oldValue+sensitivity)))? Also, you should constrain / bound "oldValue" with the sensitivity calcs so that it doesn't over / under flow the 0-1023 limits, no? tripwire and energia 2 Quote Link to post Share on other sites
bobnova 59 Posted October 9, 2014 Share Posted October 9, 2014 if(value >= (oldValue-sensitivity) || value <= (oldValue+sensitivity)) This will always return true. This method will do what you want if you switch things around a little bit. You want to check to see if the new number is less than X - sensitivity or more than X + sensitivity, and if it is then you run your code. If it isn't then you return false as the number is still within the sensitivity range. [code]if(value <= (oldValue-sensitivity) || value >= (oldValue+sensitivity)){ doStuff(); return true; } else{ return false; } Just as an example. Please do note that the above has not actually been compiled/run. Or do what abec. suggested, the end result is the same. I'm not sure you need to check for 0 and 1023 specifically. If the last read was 1023 and the next read is also 1023 that will fall within the sensitivity range and be ignored. Maybe I'm missing something there. If you have time, do a few more samples with a bit of time between them. If I can I like to sample decently often over a ~17ms time period to get rid of the 60Hz background EMI. A running average can be a nice tool for this sort of thing too. Take value and add the last-read value then divide by 2. Very low memory usage, with decent long term averaging. A small cap between the ADC input pin and GND or VCC can go a long way too, especially if it's a high resistance slow changing (relatively) input. abecedarian and energia 2 Quote Link to post Share on other sites
AGlass0fMilk 5 Posted October 9, 2014 Author Share Posted October 9, 2014 Wow guys... I've been working with this code for so long and never even realized it was the OR operator in the if statement... I feel so embarrassed. I've been programming for a few years now, even on MSP430. Hahaha, I guess it really takes a few fresh eyes to pick out the little mistakes like that. I will recompile it with the && operator when I have the change and see how much it improves. I may try the running average idea. I could also try upping the sample count and balance the latency with the stableness of the readings. Also, the purpose of the check for absolute mins/maxs (0/1023) is so that if I have a previous sent value, for example 1018, it's in the sensitivity range so 1023 will never be sent. I will never register totally 0 or totally 1023, which is critical for my project. Therefore, I have to explicitly check to see if it's a max/min value, and then send it if it hasn't been sent before. I'll check back with you guys again if I have anymore problems. Thanks again for catching my dumb mistakes! abecedarian 1 Quote Link to post Share on other sites
bobnova 59 Posted October 10, 2014 Share Posted October 10, 2014 That makes sense. Probably worth testing to make sure you can get 0 and 1023. It can be trickier than you'd think, the difference between 0 and 1 at the 3.6v the launchpads typically run at is only 0.0035 volts. I noticed the OR largely because I've been in that trap in almost exactly that situation and driven myself nuts trying to work out what was wrong. It can be very helpful to take out a calculator and notepad and manually test a program. Takes a bit, but it turns things like that up. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.