1
\$\begingroup\$

Basically, I'm looking to program a PIC16F917, so that a particular int will increment by one every time I press a button.

Now I got the ADC up and running, I got loops to count, but I seem to be lacking the knowledge and experience to put it altogether, and latch the input back to zero before the int increases by a number greater than one.

The following code is what I have written so far. The problem with it is that it increments multiple times per push (as many delay cycles that the button was held for), and I'd love to make it only count by one. I was thinking of creating a new function and int but I just can't seem to put it together - much help needed.

Thanks for helping, Ezra

#include <xc.h>
#include "config-bits.h"

#define _XTAL_FREQ 4000000

int result;
int output;

void delay()
{
    int i;

    for(i=0; i<100; i++)
    {
        /*Timer Stuff*/
    }
}

int main()
{
    TRISA=1;                    //Set all pins to input
    TRISB=1;
    TRISC=1;
    TRISD=1;
    TRISE=1;

    TRISDbits.TRISD0 = 0;       //LED output
    ANSELbits.ANS0 = 1;         //Select ADC input

    ADCON0bits.ADFM = 1;        //ADC result is right justified
    ADCON0bits.VCFG = 0;        //Vdd is the +ve reference
    ADCON1bits.ADCS = 0b001;    //Fosc/8 is the conversion clock
                                //This is selected because the conversion
                                //clock period (Tad) must be greater than 1.5us.
                                //With a Fosc of 4MHz, Fosc/8 results in a Tad
                                //of 2us.
    ADCON0bits.CHS =  0;        //select analog input, AN2
    ADCON0bits.ADON = 1;        //Turn on the ADC

    while(1)
    {
        delay();                        //Wait the acquisition time (about 5us).

        ADCON0bits.GO = 1;              //start the conversion
        while(ADCON0bits.GO==1){};      //wait for the conversion to end

        result = (ADRESH<<8)+ADRESL;    //combine the 10 bits of the conversion

        if(result > 512)
        {
            PORTD=output++;
            delay();
        }
        else
        {
            NOP();
        }
    }
}
\$\endgroup\$
7
  • \$\begingroup\$ Maybe I've misunderstood something, but can you explain why you're using ADC to read a button press? \$\endgroup\$ Commented Sep 7, 2015 at 12:08
  • \$\begingroup\$ I thought that was the way to get the PIC to recognise external input, besides using the ISR. no? how do you do it/what's the best, most efficient way? \$\endgroup\$
    – ezra_vdj
    Commented Sep 7, 2015 at 12:11
  • \$\begingroup\$ @ezra_vdj Where is the button connected on the other side of the PIC? \$\endgroup\$
    – Golaž
    Commented Sep 7, 2015 at 12:12
  • \$\begingroup\$ Maybe do a quick search of the site and see what pops up. You might also search for debouncing techniques because I suspect that's at the root of your problem. \$\endgroup\$ Commented Sep 7, 2015 at 12:13
  • \$\begingroup\$ @Golaž The button is connected to VDD on one side, pulled low whilst being connected to AN0 \$\endgroup\$
    – ezra_vdj
    Commented Sep 7, 2015 at 12:14

2 Answers 2

1
\$\begingroup\$

Use a variable to remember whether the last result was < than 512. Prior to incrementing the output variable, check if the previous result was < 512. Could you have used a digital input rather than an analogue one?

\$\endgroup\$
4
  • \$\begingroup\$ So store the previous result in a variable? Would digital input make things simpler? (because I have no idea) I guess because its already digital, no need for ADC? \$\endgroup\$
    – ezra_vdj
    Commented Sep 7, 2015 at 12:22
  • \$\begingroup\$ Did you really create this code? I would have thought you would have understood what I said if you had written it yourself? \$\endgroup\$
    – HandyHowie
    Commented Sep 7, 2015 at 12:31
  • \$\begingroup\$ Yeah I wrote it, and understand what's happening. Since I'm fairly new though, not sure if what I'm doing is the most effective way. From what I understand in your answer, you're saying to create a new variable that stores whether the output variable is <512, and before counting check if the previous result is <512. This helps because if it's >512, it halts the counting I think? can you clarify please? Thanks for helping btw \$\endgroup\$
    – ezra_vdj
    Commented Sep 7, 2015 at 12:44
  • \$\begingroup\$ You have a variable called 'result' which is updated each time you read the ADC. You need to store that in another variable so that next time around the loop you can check the previous 'result' to see if it is different to the current 'result' i.e. the button has been released and pressed again. Because you are using the ADC the value will be constantly changing slightly, so rather than storing a value, you will need to store whether 'result' is greater or less than 512. i.e. a boolean value. (I am trying to describe this without writing the code for you). \$\endgroup\$
    – HandyHowie
    Commented Sep 7, 2015 at 13:25
4
\$\begingroup\$

Ok, first a few things. TRISA=1 doesn't put all the PORTA as input as written in the comment, it is equivalent to 0b00000001 so to put all the port as input you have to do TRISA=255 or TRISA=0xFF which equals to 0b11111111.

I don't know how you connected the button to the micro controller but looking to your program it should be something like this:

schematic

simulate this circuit – Schematic created using CircuitLab

On this way it should work, you get 5V when the button is pressed and 0V when it is open. However you still can get some inestable voltage at the moment the button is pressed (called bouncing), it looks like this:

enter image description here

So it may seems like the button is pressed multiple times due to this bouncing effect, to solve this you have to take more samples and average them, then use the average result to decide what to do.

But there is another way, which is easier in this case, you just want to read if the switch is pressed ('1') or not ('0') so you don't need and ADC, you can do it just with a digital input, you don't need an analog one. The schematic would be same but the C program would be something like this (let's suppose the switch is connected to RA0):

int main() {
    TRISA=1;                    // Set RA0 as input
    TRISD=0x00;
    PORTD=0x00;

    while(1){
        if(TRISAbits.RA0) {
            PORTD += 1;
            delay();     // We use the delay to avoid the bouncing effect (some milliseconds)
            while(TRISAbits.RA0);
        }
    }
}

Analog inputs are sometimes useful when you have to read a lot of buttons and you want to save some MCU inputs:

enter image description here

In this circuit each button produces a different voltage in the ADC input. So with the analog input you read that voltage and determine which button was pressed.

\$\endgroup\$
5
  • \$\begingroup\$ Sweet thanks heaps! I did not know that it was only effecting one pin, wow. With your sample code what is happening with the if statement? I would have thought you need some operation, or comparators or something? Instead of if(TRISAbits.RA0)? \$\endgroup\$
    – ezra_vdj
    Commented Sep 7, 2015 at 20:48
  • \$\begingroup\$ if(TRISAbits.RA0) it's the same as if(TRISAbits.RA0 == 1) in the same way if(!TRISAbits.RA0) it's the same that if(TRISAbits.RA0 == 0). That's just some C stuff :) \$\endgroup\$
    – Andres
    Commented Sep 7, 2015 at 20:54
  • \$\begingroup\$ Fantastic! Using digital inputs would mean that anything not VDD or VSS would be considered floating hence the pull down resistor? \$\endgroup\$
    – ezra_vdj
    Commented Sep 7, 2015 at 21:20
  • 1
    \$\begingroup\$ It's not strictly Vdd and Vss. With Vdd as 5V a logic 1 is roughly anything above 2.7V and logic 0 anything below 1.5V (check the microcontroller datasheet, I'm just putting some numbers) so there is a threshold between a logic 1 and logic 0, if the voltage is there in can be read as 0 or 1, it's undefined behavior so you need the pull down to ensure that you have a logic 0. You should read some basics about digital circuits, that will help you a lot. \$\endgroup\$
    – Andres
    Commented Sep 7, 2015 at 21:24
  • \$\begingroup\$ When filtering spikes, never use the average. Average is used for reducing constant noise, equivalent to an RC filter. \$\endgroup\$
    – Lundin
    Commented Sep 23, 2015 at 11:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.