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!
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);
}
}