A Listening Calculator

Welcome back to the Digilent Blog!


When working with microcontrollers, it’s pretty straightforward to have your system board “listen” for an input that you would give it and have it do some sort of action to show that it noticed your input, such as pressing a button to light up an LED. Listening to a set of inputs and then comparing them to a predetermined set, like in the Simon Says game, is a little more involved but definitely doable. But what if we did not compare to any internal values and the system board has no idea how many inputs we might provide? This is a common situation in things like four function calculators; it has no idea how big the numbers will be, nor when we might press the = sign or + sign. The microcontroller can compensate for this, at least in my design, by performing what is known as active listening.


So what is active listening (in terms of an electronic system)? This is where the system board continually checks if a certain condition has been met, or in the case of a four function calculator, continually checks to see if a button is being pressed. This continual checking is known as polling and is a pretty efficient listening method if you are not concerned with potentially not receiving the input for a few microseconds (over a 1000 times faster than a chameleons tongue catching a bug) or the larger power draw as your system board is continually working to check the status of the inputs. In terms of a calculator, I’m not concerned about noticing a button being pressed immediately or about the power draw since I have my chipKIT™ uC32 powered from my computer’s USB port.


Essentially, this boils down to that it’s pretty straightforward to receive a “random” input by polling (however, please leave a comment if you have any questions). But after we get this input, which will translate to a ‘logic high’ voltage signal, how do we then merge this signal into both a number that we are typing and a single character to display?


How do we take a logic '1' and get both a char and an int?
How do we take a logic ‘1’ and get both a char and an int?


What I ended up doing for my calculator was creating a switch-case statement that looks at which button on the PmodKYPD was pressed and gives another function the character that represents that particular button. The received character can then be immediately printed to the PmodOLED screen in the appropriate location depending on if it is one of the ‘operator’ characters such as the ‘+’ or ‘=’ signs or if it is a number character. When the pressed button is a number character, its decimal value is added to the number we were originally typing out. However, this raises an important question. How would the microcontroller ‘know’ what the decimal value of a character is? One way to do this is through typecasting. This is a technique where you can designate how you want the system board to interpret a particular data type, such as interpreting a character as an integer.


For example, if we wanted to type the number ’37’ we would first press the ‘3’ button on the keypad to get our ‘3’ character. According to the ASCII Table, the character ‘3’ corresponds to the decimal number 51, meaning if we subtracted a decimal value of 48 from our ‘3’ character, we would get the character corresponding to the decimal value of 3 (‘ETX’). We can then typecast this character as an integer (to get our decimal value of 3) and add it to ten times our pre-existing number value, which in this case is a zero, giving us a value of 3.


Typecasting to turn a character 3 into a number 3
Typecasting to turn a character 3 into a number 3 (click on the picture to enlarge)


Then we would press the ‘7’ button to get the ‘7’ character and subtract a decimal value of 48 to get the character with the decimal value of 7. We then typecast this new character as an integer and add it to ten times our pre-existing number value, a 3 turned into a 30, to give us a new value of 37.


Typecasting to turn a character 7 into a number 7 and add it to an existing number
Typecasting to turn a character 7 into a number 7 and add it to an existing number (click on the picture to enlarge)


Naturally, calculators (even four-function calculators) do more than just allow you to type numbers onto a screen; they also allow you to do some basic math without forcing you to remember how to do long division. In terms of our calculator, the system board just needs to keep track of the first value that we put in and the second value that we entered and perform the appropriate operation, like adding or dividing, based on another switch-case statement. The tricky bit comes when writing a function to print out our answer on the screen since we have to individually convert every number (and the decimal point if there is one) into the correct character. Otherwise the number 78 would print as the character ‘N’, which isn’t terribly helpful in terms of math.


N - 50 is rather hard to compute when you don't know what N is
N – 50 is rather hard to compute when you don’t know what N is


One way to accomplish this is to slowly divide your number by larger and larger increments of 10 (starting from 1) and wait to see how many of these increments it takes to get your calculated number to a single digit. This lets you know how many digits long your number is. You can then start at that largest division value to get your first (and largest) digit and turn it into the appropriate ASCII char value by adding 0x30 (decimal value of 48 in hexadecimal) and then displaying it on the screen. You can then find your second largest digit by dividing by the next smallest factor of 10 and then subtracting anything “tens place” and larger from it to be left with just the single digit number.


Calculating and placing main number places
Calculating and placing main number places (click on the picture to enlarge)


Once you have gone through and displayed your digits you could also, if you so desired, check for any decimal points. Here, we would need our initially calculated value to be a double so that any decimal points would exist in the first place. We can then subtract from our calculated value its “typecasted” integer version to leave us with just the decimal points. With just the decimal points, we can then multiply them by factors of 10 to get our single digit values, remembering to subtract off the higher numbers after we start multiplying by 100 or greater.


Calculating and placing decimal places
Calculating and placing decimal places (click on the picture to enlarge)


In terms of practicality, you might be wondering how you could make sure that you keep everything looking neat and pretty on the display since there is a lot of background manipulation involved. The answer to this is to place limits on how big the numbers can be and how much of them will be shown on the display. A signed integer (can be positive or negative) on a chipKIT processor is only able to be as large as +- 2,147,483,647. With this in mind, it would be a good idea to keep the input numbers within the range of +- 1,000,000,000 where anything larger produces (as per your screen output) an overflow error forcing the user to work with smaller numbers if they want the calculator to keep working correctly.


An example of an overflow error
An example of an overflow error


The code that I used to create this setup can be found here.

What cool projects would you want to try out with a combination of Pmods?

Be the 1st to vote.

About James Colvin

The biggest thing that I enjoy is learning new things. Especially things involving some type of technology; computer components, fun gadgets, games, coding techniques, etc. I love spending time with my wife and our two sons and hanging out with our friends. During my normal work day, I manage the Digilent Forum and the North American Support team.

View all posts by James Colvin →

Leave a Reply

Your email address will not be published. Required fields are marked *