cpicmicrochip

Wrong LED turning on using microcontroller PIC16F1829


For this assignment I'm using the PicKit3 microchip PIC16F1829, lesson 8 from the PicKit starter guide.

I wrote the following code for my assignment that turns DS3 on (but for some reason it turns on and off every second in a loop). I've tried to figure out what the problem is and I think it has something to do with the configuration of the PWM module, but I can't figure what exactly is going wrong.

#include <xc.h>
#include <stdint.h>

#define _XTAL_FREQ 500000  // CPU clock at 500 kHz

// Define button and LED pin
#define BUTTON_PIN PORTAbits.RA2  // Button on RA2
#define LED_PIN LATCbits.LATC2    // DS3 on LATC2

// Look-up table for brightness according to Steven's Power Law (gamma = 2.2)
const uint16_t brightness_table[256] __attribute__((space(prog))) = {
    0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18,
    19, 21, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 41, 43, 44,
    46, 48, 50, 52, 54, 56, 58, 61, 63, 65, 67, 69, 72, 74, 76, 78,
    81, 83, 86, 88, 91, 93, 96, 98, 101, 103, 106, 109, 111, 114, 117, 120,
    122, 125, 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 161, 164, 168,
    171, 174, 177, 181, 184, 187, 191, 194, 198, 201, 205, 208, 212, 215, 219, 222,
    226, 230, 233, 237, 241, 244, 248, 252, 256, 260, 263, 267, 271, 275, 279, 283,
    287, 291, 295, 299, 303, 307, 312, 316, 320, 324, 328, 333, 337, 341, 345, 350,
    354, 358, 363, 367, 372, 376, 381, 385, 390, 394, 399, 403, 408, 412, 417, 422,
    426, 431, 436, 440, 445, 450, 455, 459, 464, 469, 474, 479, 484, 489, 493, 498,
    503, 508, 513, 518, 523, 528, 533, 538, 543, 548, 554, 559, 564, 569, 574, 579,
    584, 590, 595, 600, 605, 610, 616, 621, 626, 632, 637, 642, 647, 653, 658, 664,
    669, 674, 680, 685, 690, 696, 701, 707, 712, 718, 723, 729, 734, 740, 745, 751,
    756, 762, 767, 773, 778, 784, 790, 795, 801, 806, 812, 818, 823, 829, 834, 840,
    846, 851, 857, 863, 868, 874, 880, 885, 891, 897, 902, 908, 914, 920, 925, 931,
    937, 942, 948, 954, 960, 965, 971, 977, 982, 988, 994, 1000, 1005, 1011, 1017, 1023
};

// Variables for brightness and button status
uint8_t brightness_index = 0;  // Start at 0% brightness
uint8_t led_state = 0;         // 0 = off, 1 = on
uint8_t last_button_state = 1; // Button not pressed
uint8_t stable_button_state = 1;

// Debounce delay
#define DEBOUNCE_DELAY 50  

void update_pwm_duty_cycle(uint16_t duty_cycle);

void setup_pwm(void) {
    PR2 = 255;               // PWM period
    T2CON = 0b00000100;      // Enable Timer2, Prescaler 1:1
    CCP2CON = 0b00001100;    // Enable PWM mode
    CCPR2L = brightness_table[brightness_index] >> 2;
    CCP2CONbits.DC2B = brightness_table[brightness_index] & 0x03;
    TRISCbits.TRISC2 = 0;    // Set LATC2 as output (PWM)
}

void update_pwm_duty_cycle(uint16_t duty_cycle) {
    CCPR2L = (unsigned char)((duty_cycle >> 2) & 0xFF);
    CCP2CONbits.DC2B = duty_cycle & 0x03;
}

void main(void) {
    OSCCON = 0b00111000;  // Internal oscillator at 500 kHz
    TRISC = 0b11111011;   // Only RC2 as output (PWM)
    LATC = 0x00;          // All outputs low
    TRISA = 0xFF;         // Port A as input
    ANSELA &= ~(1 << 2);  // RA2 as digital input (no ADC)

    setup_pwm();
    update_pwm_duty_cycle(brightness_table[brightness_index]);

    // Debug: Turn on the LED at startup
    LATCbits.LATC2 = 1;
    __delay_ms(1000);
    LATCbits.LATC2 = 0;
    __delay_ms(1000);

    while (1) {
        uint8_t current_button_state = BUTTON_PIN;

        // Debounce logic
        if (current_button_state != stable_button_state) {
            __delay_ms(DEBOUNCE_DELAY);
            stable_button_state = current_button_state;
        }

        // Button pressed?
        if (last_button_state == 1 && stable_button_state == 0) {
            if (led_state == 0) {
                led_state = 1;
                brightness_index = 255;  // Maximum brightness
            } else {
                led_state = 0;
                brightness_index = 0;    // Off
            }
            update_pwm_duty_cycle(brightness_table[brightness_index]);
            __delay_ms(DEBOUNCE_DELAY);
        }
        last_button_state = stable_button_state;
    }
}

If anyone can help me try to solve this problem I would very much appreciate it!


Solution

  • The answer that Reinderien provided covers the major issues in the code posted for this question.

    This is the solution for your homework that I implemented:

    /*
     *
     * File:     main.c
     * Target:   PIC16F1829 
     * Author:   dan1138
     * Compiler: XC8 v2.45
     * IDE: MPLABX v6.20
     *
     * Target hardware: DM164130-9, PICkit 3 Low Pin Count Demo Board
     *
     * Notes:
     *  See: https://stackoverflow.com/questions/79523471/wrong-led-turning-on-using-microcontroller-pic16f1829
     *
     * Description:
     *  "Write your code in C. Correctly configure the PWM module in combination with Timer2. 
     *  When the button is pressed, LED DS3 should light up brighter according to 
     *  Steven's Power Law. If the button is pressed again, LED DS3 should also dim 
     *  according to Steven's Power Law. LED DS3 must not exhibit any signs of hysteresis."
     * 
     *                                PIC16F1829
     *                 +-----------------:_:---------------+
     *       5v0 ->  1 : VDD                           VSS : 20 <- GND
     *           <>  2 : RA5/CPP2/P2A          PGD/AN0/RA0 : 19 <> ICD_PGD
     *       POT <>  3 : RA4/AN3               PGC/AN1/RA1 : 18 <> ICD_PGC
     *   ICD_VPP ->  4 : RA3/MCLR             CCP3/AN2/RA2 : 17 <> SW1
     *           <>  5 : RC5/CPP1/P1A              AN4/RC0 : 16 <> LED_DS1
     *           <>  6 : RC4                       AN5/RC1 : 15 <> LED_DS2
     *   LED_DS4 <>  7 : RC3/AN7/P1C       P1C/P2B/AN6/RC2 : 14 <> LED_DS3
     *           <>  8 : RC6/AN8          P1B/SDI/AN10/RB4 : 13 <> 
     *           <>  9 : RC7/AN9/SDO           RX/AN11/RB5 : 12 <>
     *           <> 10 : RB7/TX                    SCK/RB6 : 11 <>
     *                 +-----------------------------------+
     *                                 DIP-20
     * 
     * Created on March 24, 2025, 12:38 PM
     */
    /* define how this application will setup the system oscillator frequency */
    #define _XTAL_FREQ 500000ul
    
    // PIC16F1829 Configuration Bit Settings
    
    // 'C' source line config statements
    
    // CONFIG1
    #pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on 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 = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
    #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 = OFF      // PLL Enable (4x PLL disabled)
    #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 DEBUG = OFF      // In-Circuit Debugger Mode (In-Circuit Debugger disabled, ICSPCLK and ICSPDAT are general purpose I/O pins)
    #pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)
    
    // #pragma config statements should precede project file includes.
    // Use project enums instead of #define for ON and OFF.
    
    #include <xc.h>
    
    // Look-up table for brightness according to Steven's Power Law (gamma = 2.2)
    const uint16_t brightness_table[256] = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18,
        19, 21, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 41, 43, 44,
        46, 48, 50, 52, 54, 56, 58, 61, 63, 65, 67, 69, 72, 74, 76, 78,
        81, 83, 86, 88, 91, 93, 96, 98, 101, 103, 106, 109, 111, 114, 117, 120,
        122, 125, 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 161, 164, 168,
        171, 174, 177, 181, 184, 187, 191, 194, 198, 201, 205, 208, 212, 215, 219, 222,
        226, 230, 233, 237, 241, 244, 248, 252, 256, 260, 263, 267, 271, 275, 279, 283,
        287, 291, 295, 299, 303, 307, 312, 316, 320, 324, 328, 333, 337, 341, 345, 350,
        354, 358, 363, 367, 372, 376, 381, 385, 390, 394, 399, 403, 408, 412, 417, 422,
        426, 431, 436, 440, 445, 450, 455, 459, 464, 469, 474, 479, 484, 489, 493, 498,
        503, 508, 513, 518, 523, 528, 533, 538, 543, 548, 554, 559, 564, 569, 574, 579,
        584, 590, 595, 600, 605, 610, 616, 621, 626, 632, 637, 642, 647, 653, 658, 664,
        669, 674, 680, 685, 690, 696, 701, 707, 712, 718, 723, 729, 734, 740, 745, 751,
        756, 762, 767, 773, 778, 784, 790, 795, 801, 806, 812, 818, 823, 829, 834, 840,
        846, 851, 857, 863, 868, 874, 880, 885, 891, 897, 902, 908, 914, 920, 925, 931,
        937, 942, 948, 954, 960, 965, 971, 977, 982, 988, 994, 1000, 1004, 1008, 1012, 1020
    };
    // Variables for brightness and button status
    uint8_t brightness_index = 0; // Start at 0% brightness
    uint8_t led_state = 0; // 0 = increase brightness, 1 = decrease brightness
    uint16_t led_brightness_step_rate = 0; // 0 = stopped, 1 to 63353 is application loops per step. 
    uint8_t button_sample;
    uint8_t button_bouncing;
    uint8_t button_stable;
    uint8_t button_change;
    
    // define number if application loop per LED brightness change, bigger is slower
    #define LED_BRIGHTNESS_RATE (4)
    #define LED_BRIGHTNESS_INDEX_MAX ((sizeof(brightness_table)/sizeof(*brightness_table))-1)
    #define LED_BRIGHTNESS_INDEX_MIN ((0))
    
    // Define button input pin
    #define BUTTON_PIN PORTAbits.RA2  // Button on RA2
    #define BUTTON_BIT_MASK (_PORTA_RA2_MASK)
    #define BUTTON_PORT (PORTA)
    
    // Debounce delay
    #define DEBOUNCE_DELAY 50  
    
    void set_brightness(uint8_t brightness_index) {
        CCPR2L = (uint8_t) (brightness_table[brightness_index] >> 2);
        brightness_index = (uint8_t) (brightness_table[brightness_index]);
        CCP2CONbits.DC2B1 = 0;
        CCP2CONbits.DC2B0 = 0;
        if (brightness_index & 2) CCP2CONbits.DC2B1 = 1;
        if (brightness_index & 1) CCP2CONbits.DC2B0 = 1;
    }
    
    void setup_pwm(void) {
        PR2 = 254; // PWM period
        T2CON = 0b00000000; // Disable Timer2, Prescaler 1:1, Postscaler 1:1
        CCP2CON = 0b00001100; // Enable PWM mode
        PSTR2CON = 0b00000010; // PWM output on RC2
        T2CONbits.TMR2ON = 1;
    
        set_brightness(brightness_index);
        TRISCbits.TRISC2 = 0; // Set LATC2 as output (PWM)
        ANSELCbits.ANSC2 = 0;
    }
    
    int main(void) {
        OSCCON = 0b00111000; // Internal oscillator at 500 kHz
        TRISC = 0b11111011; // Only RC2 as output (PWM)
        LATC = 0x00; // All outputs low
        ANSELC = 0;
    
        /* setup button input */
        TRISA = 0xFF; // Port A as input
        ANSELA = 0;
    
        button_sample = BUTTON_PORT & BUTTON_BIT_MASK;
        button_stable = button_sample;
        button_change = 0;
        button_bouncing = 0;
    
        // Debug: Turn on the LED at startup
        LATCbits.LATC2 = 1;
        __delay_ms(1000);
        LATCbits.LATC2 = 0;
        __delay_ms(1000);
    
        /* setup PWM */
        setup_pwm();
    
        /* Application process loop */
        while (1) {
            /* poll button task */
            if (button_sample != (BUTTON_PORT & BUTTON_BIT_MASK)) {
                button_sample = (BUTTON_PORT & BUTTON_BIT_MASK);
                button_bouncing = DEBOUNCE_DELAY;
            } else if (button_bouncing) {
                button_bouncing--;
            } else if (button_stable != button_sample) {
                button_change |= (button_stable ^ button_sample);
                button_stable = button_sample;
            }
    
            /* check if button changed */
            if (button_change) {
                if (button_stable) {
                    /* button changed to released */
                } else {
                    /* button changed to asserted */
                    if (led_brightness_step_rate) {
                        if (led_state == 0) {
                            led_state = 1;
                        } else {
                            led_state = 0;
                        }
                    } else {
                        set_brightness(brightness_index);
                        led_brightness_step_rate = LED_BRIGHTNESS_RATE;
                    }
                }
                button_change = 0;
            }
    
            /* change LED brightness task */
            if (led_brightness_step_rate) {
                if (--led_brightness_step_rate == 0) {
                    set_brightness(brightness_index);
                    led_brightness_step_rate = LED_BRIGHTNESS_RATE;
                    switch (led_state) {
                        case 0:
                            if (brightness_index >= LED_BRIGHTNESS_INDEX_MAX) {
                                brightness_index = LED_BRIGHTNESS_INDEX_MAX - 1;
                                led_state = 1;
                            } else {
                                brightness_index++;
                            }
                            break;
                        case 1:
                            if (brightness_index <= LED_BRIGHTNESS_INDEX_MIN) {
                                brightness_index = LED_BRIGHTNESS_INDEX_MIN + 1;
                                led_state = 0;
                            } else {
                                brightness_index--;
                            }
                            break;
                        default:
                            break;
                    }
                }
            }
            __delay_us(1000);
        }
    }