Search code examples
microcontrollerpicmicrochip

PIC ports behaves randomly


I am trying for hours now to light up a simple LED on a button press with no luck. The pins seem to randomly get low and high for some reason.

I am using MPLAB X IDE v5.54 and PIC12F1822. Can anyone spot my mistake?

Here is the code:

// CONFIG1
#pragma config FOSC = ECH       // Oscillator Selection (ECH, External Clock, High Power Mode (4-32 MHz): device clock supplied to CLKIN pin)
#pragma config WDTE = ON        // Watchdog Timer Enable (WDT enabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)

#include <xc.h>

#define _XTAL_FREQ 8000000      // SET frequency as 8000000 bit because of the 8 MHz clock

int main(int argc, char** argv) {
    OSCCON = 0xF0; // Internal oscillator 8MHz and PLL enabled
    ADCON0 = 0; // Disable ADC module

    ANSELAbits.ANSA2 = 0; // SET LED PIN to Digital
    ANSELAbits.ANSA4 = 0; // SET Button pin to Digital

    TRISAbits.TRISA2 = 0; // LED bit is output
    TRISAbits.TRISA4 = 1; // Button bit is input

    PORTAbits.RA4 = 0; // Button bit low initial
    PORTAbits.RA2 = 0; // LED bit low initial

    while (1) {
        while (PORTAbits.RA4 == 0); // Wait until button is pressed

        __delay_ms(100); // wait 100ms
        PORTAbits.RA2 = 1; // Light up the LED
        __delay_ms(100); // wait 100ms

        while (PORTAbits.RA4 == 1); // Wait until button release

        __delay_ms(100); // wait 100ms
        PORTAbits.RA2 = 0; // Turn off the LED
        __delay_ms(100); // wait 100ms
    }
    return (EXIT_SUCCESS);
}

I tried so many things like:

  • Tweaking the configs for god knows how many times
  • Making all pins one by one input, output, digital, analog etc.

I cannot think for anything else.

Any help will be greatly appreciated.

Edit 1

Here is the latest code that I have. This one turns on and off LED randomly. I have also disabled the Watchdog Timer:

// CONFIG1
#pragma config FOSC = ECH       // Oscillator Selection (ECH, External Clock, High Power Mode (4-32 MHz): device clock supplied to CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)


#include <xc.h> // include standard header file

// Definitions
#define _XTAL_FREQ  16000000        // this is used by the __delay_ms(xx) and __delay_us(xx) functions

void main() {
    // set up oscillator control register
    OSCCONbits.SPLLEN = 0; // PLL is disabled
    OSCCONbits.IRCF = 0x0F; //set OSCCON IRCF bits to select OSC frequency=16Mhz
    OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block

    ANSELAbits.ANSA0 = 0; // Set to digital
    ANSELAbits.ANSA1 = 0; // Set to digital
    ANSELAbits.ANSA2 = 0; // Set to digital
    ANSELAbits.ANSA4 = 0; // Set to digital

    // Set up I/O pins
    // PORT A Assignments
    TRISAbits.TRISA0 = 0; // RA0 = Digital Voltage out
    TRISAbits.TRISA1 = 0; // RA1 = Digital Voltage out
    TRISAbits.TRISA2 = 0; // RA2 = Digital Voltage out
    TRISAbits.TRISA3 = 0; // RA3 = Digital Voltage out
    TRISAbits.TRISA4 = 1; // RA4 = Digital Voltage in
    TRISAbits.TRISA5 = 0; // RA0 = Digital Voltage out

    PORTAbits.RA0 = 0;
    PORTAbits.RA1 = 0;
    PORTAbits.RA2 = 0;
    PORTAbits.RA3 = 0;
    PORTAbits.RA4 = 0;
    PORTAbits.RA5 = 0;

    // Set up ADC
    ADCON0 = 0; // ADC is off

    for (;;) {
        __delay_ms(100); // wait 100ms
        if (PORTAbits.RA4 == 0) {
            LATAbits.LATA2 = 0;
        } else {
            LATAbits.LATA2 = 1;
        }
    }
}

Ok, here is what I observed. When the button is pressed the LED never turns off, but when the button is not pressed it randomly turns on or off.


Solution

  • The problem of random values observed on inputs are generally caused by floating inputs. An input is considered in floating state if it's not driven by an output or a some kind of source. In this case, a floating input behaves like an antenna, it can get any voltage level randomly and the logic connected to it switches between 1 & 0 randomly.

    It seems most of the time, electronics and uC beginners learn about them for the first time when they experiment with buttons. My experience was similar. The input was triggering when I get my finger close to the button, but before I actually press or even touch it. Later, I observed that they can also be triggered by the changes of adjacent output pins of the uC.

    Fortunately, the solution is simple. Just add a pull-up or pull-down resistor, so that the output is driven by VDD or GND even when the button is not pressed. Pull-ups seem to be more popular (I'm not sure why), so you generally connect the button in such a way that it pulls the input pin to the ground when the button is pressed and provide a pull-up resistor between the pin and VDD. In this case, your program logic reads the input as 0 when the button is pressed.

    BTW, you could connect the pin directly to the VDD to prevent it from floating. But in this case, a button press would short circuit VDD to GND, collapse the VDD and reset the uC. This can also damage the power regulator (actually most of them have thermal shutdown to protect themselves), the button itself or the wires (or traces on the PCB). This is why you need a resistor.

    Most uCs also provide internal weak pull-ups which can be activated by software. Here, weak means that these resistors have high values (like 100k ohms) and they don't pull the pin strongly. In cases where you need stronger ones (like pulling up communication lines) you may need to use external ones.

    Some more recent uCs may provide internal weak pull-down resistors too. STM32 uCs have this capability. Your PIC12F1822 has only pull-up resistors, but fortunately they can be controlled individually. In some older PIC models, you have single switch to activate pull-ups on all pins of PORTB.

    To activate internal weak pull-up on RA4:

    OPTION_REGbits.nWPUEN = 0; // Global pull-up enable bit (inverted)
    WPUAbits.WPUA4 = 1; // Individual pin pull-up enable bit