NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » help with keypad/display project code

December 18, 2012
by rkarrick
rkarrick's Avatar

Thanks in advance for your help.I want to read the voltage input on PC0 from my keypad and display it as a 2,3 or 4 digit number (depending on keypad input)on the display with a fixed decimal point between the second and third digit. I also need to store this number for use in the next phase of the project. Would like to dedicate one key as the enter key to signal the end of input.I have the keypad wired with resistors and one input to PC0 (found out how on this forum). I was able to read and record (on paper)the key values using the tempsensor program. Now i am attempting to write the program. I have spent a great deal of time completing all of the projects included with my Nerdkit,reading the tutorials and blogs and have a pretty good understanding of all of the parts and pieces but can't seem to put it all together. I know this code is incomplete and jumbled but i'm stuck and very frustrated!

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- keypad analog input

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)

  uint16_t result = ADCL;
  uint16_t result2 = ADCH;

  result = result + (result2<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

   { if ((ADC_16bit > 333) && (ADC_16bit < 339)) { // 1. ADC = 336

    //delay_ms(50);

    a = 1;

    }

    if ((ADC_16bit > 361) && (ADC_16bit < 367)) { // 2. ADC = 364

    //delay_ms(50);

    a = 2;

    }

    if ((ADC_16bit > 393) && (ADC_16bit < 399)) { // 3. ADC = 397

    //delay_ms(50);

    a = 3;

    }

    if ((ADC_16bit > 240) && (ADC_16bit < 246)) { // 4. ADC = 243

    //delay_ms(50);

    a = 4;

    }

    if ((ADC_16bit > 254) && (ADC_16bit < 260)) { // 5. ADC = 258

    //delay_ms(50);

    a = 5;

    }

    if ((ADC_16bit > 270) && (ADC_16bit < 276)) { // 6. ADC = 273

    //delay_ms(50);

    a = 6;

    }

    if ((ADC_16bit > 187) && (ADC_16bit < 193)) { // 7. ADC = 190

    //delay_ms(50);

    a = 7;

    }

    if ((ADC_16bit > 195) && (ADC_16bit < 201)) { // 8. ADC = 199

    //delay_ms(50);

    a = 8;

    }

    if ((ADC_16bit > 205) && (ADC_16bit < 211)) { // 9. ADC = 208

    //delay_ms(50);

    a = 9;

    }

    if ((ADC_16bit > 147) && (ADC_16bit < 153)) { // 0. ADC = 151

    //delay_ms(50);

    a = 0;

    }

    if ((ADC_16bit > 159) && (ADC_16bit < 164)) { // Enter. ADC = 162

    //delay_ms(50);

    a = 0;// want this to represent "enter" or end of input?

    }.

return 0;
}

sample (uint16_t sample) {
return sample;
}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();

  // holder variables for results
  uint16_t last_sample = 0;

  // not sure what this line does, assigns i to uint8_t ?
  uint8_t i;

// Stuck on this next part, I want to monitor or scan the voltage inputs on PCO as keys 
// are pressed and display the result on the LCD and store it in (ADCL/ADCH registers) 
// for the next phase of this project. I also need a fixed decimal in the second position 
// on the display and in memory?. Example: if I key in 2756 it would be displayed and stored
// as 27.56

  while(1) {

    keypad_entry = 0.0;

    for(i=0; i<1000; i+++) {    //1 + 4 places?

      last_sample = adc_read();

      keypad_entry= (last_sample);

      keypad_entry= keypad_entry + last_sample;

    }

    // write message to LCD
    lcd_home();
    lcd_write_string(PSTR("ADC: "));
    lcd_write_int16(last_sample);
   }

    return 0;
}

I haven't figured out how to embed a picture yet so i'm not including the list of errors at this time. I know that variables and assinging them is one of my bigger challanges. I used to think i was a pretty smart guy :) Thanks, Bob

December 18, 2012
by Noter
Noter's Avatar

What function do all the statements that assign values to the variable 'a' belong to?

Start small and work your way up. See if you can just read the acd and show the value then add only a few statements at time then run and test. It's easier to build a program in small incremental steps where you can verify each is working along the way.

December 18, 2012
by rkarrick
rkarrick's Avatar

Thanks for the reply. The temp sensor reads the voltages and displays them as temperature with each key press. Honestly i don't know the answer to your question (borrowed code). I will start with tempsensor.c and see if i can get rid of the parts i don't need and then try to add some statements as you suggested. Bob p.s all homework assignments are welcomed.

December 18, 2012
by pcbolt
pcbolt's Avatar

Hi Bob,

There are a couple of things to fix right off the bat and one of them is summed up in a comment line in your code (line 164 above)...

// not sure what this line does, assigns i to uint8_t ?
uint8_t i;

This is a pretty important concept to understand. Every variable you use needs to be declared in this fashion. Basically it tells the compiler "I need a variable called 'i' and I would like it to be unsigned, an integer and 8-bits long". I see in your code above, 'a', 'keypad_entry', and 'ADC_16bit' do not have declarations and the compiler will throw errors because of this. Also, it is important to understand the scope of each variable, which means where in your code you can use it. If a variable is declared outside all code execution blocks you can use it anywhere in your code. If it is declared inside main() you can only use it inside the main() execution block (i.e. between the braces). If you declare a variable inside a subroutine, you can only use it there.

Another thing I noticed was all the 'if' statements (from line 56 to 143) are not inside any subroutine block and will never get compiled, much less executed. (The dot at the end of line 143 will cause a syntax error as well). You can see the basic structure for subroutines in the "adc_init()" and "adc_read()" functions up above.

December 18, 2012
by JimFrederickson
JimFrederickson's Avatar

In addition to the above observations you also need to consider "when a key is released" and it would be best to change the "adc_read()" function so that it doesn't wait for an ADC Result to be ready, but instead returns "the ADC Result if there is one ready or some value the you see and a no result if there is not an ADC Result ready".

Technically, for this particular code changing the "adc_read()" function won't make a difference, but if this code expands in the future, which it seems to me is the intent, it is best to stop wasting those cycles.

As far as a "key release event".

What you have right now is just looping around reading the ADC as fast as it can. So when you press a key you have to remember that the Microcontroller is executing a max of 16,000,000+ instructions per second. WELL BEFORE YOU CAN POSSIBLY RELEASE YOUR KEY" your little Microcontroller will have read your key press many more times than the one key press you are hoping for!

If you look up "debouncing" in these forums you will see alot of discussions about "debouncing and validation". Using resistance to determine which keys are pressed, and since you are using ranges, "debouncing" is likely not as significant. (Although I ALWAYS over sample any ADC Values). "validation" though is still a big issue.

Using a resistor network can make reading keyboards possible with alot less I/O Pins. If you choose your resister values very carefully it is even able to possibly account for key press overlapping as well.

December 18, 2012
by rkarrick
rkarrick's Avatar

pcbolt, Thanks for your input. i'm going to start fresh at line 55. One of the many things i can't wrap my brain around is how to accommodate up to 4 sequential key presses that will generate one number? All of the if statements are intended to convert a voltage reading into a number representing the key pressed. I'll figure out where these statements belong at some point. I will make sense of all this if i live long enough! Thanks again, Bob

December 19, 2012
by Ralphxyz
Ralphxyz's Avatar

Bob, what you need to do is to accumulate the key presses in a separate area and then read the one number. In other words don't try to read the number from the key presses.

Ralph

December 19, 2012
by pcbolt
pcbolt's Avatar

Bob -

Just curious if you came across This Thread. Looks like some of the variable names are the same :-)

December 19, 2012
by rkarrick
rkarrick's Avatar

pcbolt, You would be correct. I also used the wiring diagram in that thread to hook up my matrix keypad. I'm back reviewing what I thought I had learned to fill in the blanks. Thanks for all of the help and suggestions.

I'll be back!

Bob

January 02, 2013
by rkarrick
rkarrick's Avatar

My first goal was to have the number of the key pressed display on the LCD...Success! Now I'm back stuck in the mud. I have tried dozens of different ways to accumulate the key presses into one number all of which have failed. The following code actually works and I am asking for some help or clues in figuring out the next step(s). Some of the previous suggestions are a bit advanced for me right now but I will keep them in mind as I move forward.

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- keypad analog input

uint16_t numberpressed = 0;
uint16_t key = 0;

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {

  while(ADCSRA & (1<<ADSC)) {

  }

  uint16_t numberpressed = ADCL;
  uint16_t numberpressed2 = ADCH;
  numberpressed = numberpressed + (numberpressed2<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return numberpressed;
}

int main() {
  // start up the LCD
  lcd_init();
  lcd_home();
  // start up the Analog to Digital Converter
  adc_init();

  while(1) 
  {

     { numberpressed = adc_read();

    if ((numberpressed > 694) && (numberpressed < 704)) {
    key = 1;
    }
    if ((numberpressed > 752) && (numberpressed < 762)) {
    key = 2;
    }
    if ((numberpressed > 822) && (numberpressed < 832)) { 
    key = 3;
    }
    if ((numberpressed > 498) && (numberpressed < 508)) {
    key = 4;
    }
    if ((numberpressed > 528) && (numberpressed < 538)) {
    key = 5;
    }
    if ((numberpressed > 561) && (numberpressed < 571)) {
    key = 6;
    }
    if ((numberpressed > 388) && (numberpressed < 398)) {
    key = 7;
    }
    if ((numberpressed > 405) && (numberpressed < 415)) {
    key = 8;
    }
    if ((numberpressed > 425) && (numberpressed < 435)) {
    key = 9;
    }
    if ((numberpressed > 329) && (numberpressed < 339)) {
    key = 0;
    }
    if ((numberpressed > 305) && (numberpressed < 315)) {
    key = 10; 
    }

    }
    // write message to LCD
    lcd_home();

    lcd_write_int16(key);
    }
     return 0;
}

Thanks for all of the help and HAPPY NEW YEAR! Bob

January 02, 2013
by pcbolt
pcbolt's Avatar

Hi Bob,

I'm a little unsure of your next goal. In the first post you said you wanted a 2, 3 or 4 digit number and a decimal point between the 2nd and 3rd digit. Where would the decimal point go for a 2 digit number? If you enter 1,2,3,4 the final would be 12.34. If you enter 5,6,7 the final would be 56.7. (Or 5.67?) So a 2 digit input like 8,9 would be 89? Or 8.9? Or 0.89?

In either case you need some more variables. Maybe insert these lines somewhere between lines 54 and 59...

uint8_t count = 0, ok_to_write = 0;
uint16_t tally = 0;
float final = 0.0;

In each of your "if" blocks, you can increase the count and modify the tally using something like this...

if ((numberpressed > 405) && (numberpressed < 415)) {
key = 8;
count++;
tally = (tally * 10) + key;
}

If you assume the '10' key is "Enter", you can do the final calculations there and signal the LCD to output your answer...

if ((numberpressed > 305) && (numberpressed < 315)) {
key = 10;
ok_to_write = 1;
final = tally / 100.0;    // if you always want 2 decimal places
}

Then you can output to the LCD with this code...

if (ok_to_write){
  lcd_write_int16(tally);
  ok_to_write = 0;
  tally = 0;
}

I know this won't be in the format you want just yet, but until I know more about your final format...this should get you going. If you want to display 2 decimal points use the NK function "lcd_write_int16_centi(tally)" instead of "lcd_write_int16(tally)"

January 03, 2013
by JimFrederickson
JimFrederickson's Avatar

I really don't know why you want to encode your 1-4 multiple digit entries into a single value.

Yes, it is more efficient, it is also more cumbersome as well. It is also not for adaptable in the future.

If I am understanding correctly you have:

0-9 digit entries, you will also need to account for a not-entered conditioned too.
What happens if too many characters are entered?

My second another assumption is you would like your Entry Display to read something like:

No entry:
LCD Display Shows: _ _ . _ _

First entry of 1:

LCD Display Shows: _ _ . _ 1

Second entry of 5:

LCD Display Shows: _ _ . 1 5

Third entry of 7:

LCD Display Shows: _ 1 . 5 7

Last entry of 3:

LCD Display Shows: 1 5 . 7 3

Or to the digits fill in some other direction?
Perhaps you are just putting an "*" so no-one can read the password/code?

January 03, 2013
by rkarrick
rkarrick's Avatar

Jim, Let me jump ahead with an overview of what I am attempting to do. I have invented a system that will eliminate the exchange of coins in cash transactions. This system is Patent Pending and currently sitting at the USPTO waiting for the Utility application review. I have a working prototype I paid to have built using a laptop and external reader/writer that works perfectly but it is too bulky. Retail counter space is at a premium so I need to have a compact device with a small footprint.

The hardware: 1 – Nerdkit, 1 – 13.56MHz RFID reader/writer

Here is an overview of the work this device needs to do:

a.Receive an amount ($) between 0 and 99.99 via the key pad input by a cashier

b.Read a value from a consumers RFID key fob

c.Process an algorithm using (a and b) resulting in two new output values (d and e)

d.Display a new value to the LCD (this will be an even dollar amount)

e.Write a new value to the key fob that will overwrite the previous value

f.Enter sleep mode and wait for the next transaction

Being a novice in the programming world I am trying to build this and learn one small step at a time. I will be figuring out how to implement what pcbolt has posted and then I’ll be back!

Thanks for all the help, Bob

January 03, 2013
by Noter
Noter's Avatar

Why stop with coins? Why not put all your cash on the key fob? Although I don't see how you could call it a cash transaction after that.

Depending on you skill set it can take a long time and a lot of work to get up to speed on applications using AVR controllers. Perhaps you should consider paying someone to develop it for you like you did the laptop version.

January 03, 2013
by rkarrick
rkarrick's Avatar

Noter, Funny you should mention hiring somebody. I didn't feel this forum was the place to solicit for that but since you brought it up, I did email the staff asking for names of individuals or companies that might be interested. I have not heard back at this time. I have also spent a great deal of time searching the internet for the same with no luck!

In the meantime, I figured I might as well give it a shot.

p.s. is that really you in the picture? I did some competitive cow sorting out west a few years back..... much easier than programming :)

January 03, 2013
by Noter
Noter's Avatar

You will probably have better luck finding someone using Atmel - Atmel and Third Party contacts

Yep, that's really me and my bull in the photo.

January 03, 2013
by pcbolt
pcbolt's Avatar

Bob -

If I were in your shoes, I'd hire Noter.

January 03, 2013
by Noter
Noter's Avatar

Thanks for the vote of confidence PCBOLT :-)

Bob, If you're just looking for a smaller footprint maybe your existing program can ported to the Raspberry Pi easier than starting from scratch with an AVR.

January 03, 2013
by BobaMosfet
BobaMosfet's Avatar

rkarrick-

I'd like to look at your problem in depth if you don't mind. I don't really think it's very difficult at all. Coding is trivial, and the circuit side is not all that hard either. If you like, ask the NK guys to provide you with my email address, so we can discuss this. I need to understand more specifics than you can list here, and your timetable.

We can discuss something equitable and fair for both of us.

BM

January 04, 2013
by BobaMosfet
BobaMosfet's Avatar

Or you can see if you can reach me on the #nerdkit IRC channels, I'm 'bobamosfet'

BM

January 04, 2013
by rkarrick
rkarrick's Avatar

bobamosfet, I have set up a temporary email address: temp123@bex.net send me an email.

January 11, 2013
by BobaMosfet
BobaMosfet's Avatar

All-

I'm working this issue with rkarrick, so thread is essentially closed.

BM

January 11, 2013
by pcbolt
pcbolt's Avatar

BM/Bob -

Best of luck getting everything off the ground, hope there is a way you can let us know of your progress.

Post a Reply

Please log in to post a reply.

Did you know that a microcontroller can measure an RC time constant? Learn more...