PIC 12F683 - LED driving - code is not working at all

Thread Starter

MartinFALCON

Joined Feb 16, 2024
1
Hello I am new to this world of microcontrollers and programming. I am not a programmer at all. I just want it to write code for my fun and needs, but
I have the issues to get this code running and I do not know how to properly debug. I am using PICKIT3,5 with MPLAB X IDE 6.15 and XC8 compiler.
For simulation I am using SimuliDE and I can use real components on breadbord.

Just the brief description, what this code should do. Turn the LED on/off, by the long press as it´s written in the code. With the short press to cycle thrue POWER levels. It´s also checking the temperature of the LED from the analog sensor LM35, and when the temperature reach set limits it will deploy temperature derating. Checking battery supply voltage for battery protection. LED is controlled by the PWM. PWM output pin is connected to the mosfet, which will control the LED by the switching current. And that´s it.

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

// Function prototypes
void initialize();
void setPWM(uint8_t dutyCycle);
uint16_t readADC(uint8_t channel);
uint16_t readTemperature();
uint16_t readBatteryVoltage();
void handleTemperature();
void handleLowBattery();

// Configuration settings for the PIC12F683
#pragma config FOSC = INTOSCIO, WDTE = OFF, PWRTE = ON, MCLRE = OFF, CP = OFF, BOREN = OFF

#define _XTAL_FREQ 2000000 // Oscillator frequency for delay calculations

// Pin definitions
#define LED_PIN     0x04 // LED connected to GP2
#define BUTTON_PIN  0x08 // Button connected to GP5
#define TEMP_CHANNEL GP0 // ADC channel for temperature sensor, connected to GP0

// State definitions
#define STATE_OFF    0
#define STATE_ON     1
#define STATE_LOW_VOLTAGE 2

// Timing definitions
#define LONG_PRESS_TIME     1500 // ms, for turning LED on/off
#define SHORT_PRESS_TIME    1000 // ms, for changing LED power level
#define DEBOUNCE_TIME       100  // ms, debounce time for button press
#define BLINK_INTERVAL      500  // ms, not used in this version

// Button states
#define BUTTON_RELEASED 1
#define BUTTON_PRESSED 0

// LED power levels (PWM duty cycle as a percentage of 255)
#define POWER_OFF   0
#define POWER_10    26
#define POWER_20    51
#define POWER_25    64
#define POWER_50    128
#define POWER_75    192
#define POWER_90    230
#define POWER_100   255

// Adjusted temperature limits (ADC values)
#define TEMP_50     122
#define TEMP_60     146
#define TEMP_70     171
#define TEMP_80     195

// Global variables
volatile uint16_t timer = 0; // Incremented by ISR, used for timing
uint8_t state = STATE_OFF; // Current state of the system
uint8_t powerLevel = POWER_25; // Current power level of LED
uint8_t lowVoltageMode = 0; // Flag for low voltage mode
uint16_t lowVoltageTimer = 0; // Timer for low voltage mode, incremented by ISR

// Interrupt Service Routine for Timer0
void __interrupt() ISR() {
    if (T0IF) { // Check Timer0 overflow flag
        T0IF = 0; // Clear Timer0 interrupt flag
        TMR0 = 6; // Reset Timer0 for next interrupt
        timer++; // Increment global timer
        if (lowVoltageMode && state == STATE_LOW_VOLTAGE) {
            lowVoltageTimer++; // Increment low voltage timer if in low voltage mode
        }
    }
}

// Set PWM duty cycle for LED brightness
void setPWM(uint8_t dutyCycle) {
    CCPR1L = dutyCycle; // Set upper 8 bits of duty cycle
}

// Initialize system configuration
void initialize() {
    // Initialize I/O
    TRISIO = 0b00100001; // Set GP2 as output, GP5 and GP0 as input
    GPIO = 0; // Turn off all pins initially
    
    // Initialize ADC
    ANSEL = 0b00000001; // Set GP0 as analog input
    ADCON0 = 0b00000001; // Enable ADC, select channel AN0, use Fosc/8
    
    // Initialize Timer0 for timing
    OPTION_REG = 0b00000111; // Prescaler 1:256 for Timer0
    TMR0 = 6; // Set Timer0 for 1ms interrupts
    T0IE = 1; // Enable Timer0 interrupts

    // Initialize PWM for LED control
    CCP1CON = 0b00001100; // PWM mode
    PR2 = 255; // PWM period
    T2CON = 0b00000101; // Enable Timer2, prescaler 1:4
    
    // Set initial PWM duty cycle to 0 (LED off)
    CCPR1L = 0;
    setPWM(POWER_OFF); // Set PWM duty cycle to 0%
    
    // Enable global interrupts
    GIE = 1;
    PEIE = 1;
}

// Read ADC value from specified channel
uint16_t readADC(uint8_t channel) {
    ADCON0 = ((uint8_t)(channel << 3) | 1); // Select ADC channel
    __delay_us(10); // Delay for ADC settling
    GO_DONE = 1; // Start conversion
    while (GO_DONE); // Wait for conversion to complete
    return (uint16_t)((ADRESH << 8) + ADRESL); // Return ADC result
}

// Read temperature from sensor
uint16_t readTemperature() {
    return readADC(TEMP_CHANNEL); // Read ADC value from temperature channel
}

// Read battery voltage
uint16_t readBatteryVoltage() {
    uint16_t adcValue = readADC(1); // Assume channel AN1 for battery voltage
    return (uint16_t)(((uint32_t)adcValue * 4200) / 1023); // Convert ADC value to voltage
}

// Handle button presses to change LED power level or toggle state
void checkButton() {
    static uint16_t lastButtonPress = 0;
    static uint8_t buttonState = BUTTON_RELEASED;

    uint8_t currentButtonState = BUTTON_PIN;

    if (currentButtonState == BUTTON_PRESSED && buttonState == BUTTON_RELEASED) {
        lastButtonPress = timer;
        buttonState = BUTTON_PRESSED;
    } else if (currentButtonState == BUTTON_RELEASED && buttonState == BUTTON_PRESSED) {
        uint16_t buttonPressDuration = timer - lastButtonPress;

        if (buttonPressDuration >= LONG_PRESS_TIME) {
            // Toggle system state on long press
            state = (state == STATE_OFF) ? STATE_ON : STATE_OFF;
            powerLevel = (state == STATE_ON) ? POWER_25 : POWER_OFF;
        } else if (buttonPressDuration >= SHORT_PRESS_TIME && state == STATE_ON) {
            // Cycle power levels on short press, only if system is on
            switch (powerLevel) {
                case POWER_25: powerLevel = POWER_50; break;
                case POWER_50: powerLevel = POWER_75; break;
                case POWER_75: powerLevel = POWER_90; break;
                case POWER_90: powerLevel = POWER_100; break;
                case POWER_100: powerLevel = POWER_25; break;
                default: powerLevel = POWER_25; break;
            }
        }

        buttonState = BUTTON_RELEASED;
    }
}

// Adjust LED power level based on temperature to prevent overheating
void applyTemperatureDerating(uint16_t temperature) {
    if (temperature > TEMP_80) powerLevel = POWER_50;
    else if (temperature > TEMP_70) powerLevel = POWER_75;
    else if (temperature > TEMP_60) powerLevel = POWER_90;
}

// Handle low voltage condition by reducing LED power level
void handleLowVoltage(uint16_t voltage) {
    if (voltage < 3300) {
        state = STATE_LOW_VOLTAGE;
        lowVoltageMode = 1;
        lowVoltageTimer = 0;
    } else if (voltage < 3400) powerLevel = POWER_10;
    else if (voltage < 3500) powerLevel = POWER_20;
}

// Main loop
void main() {
    initialize();

    while(1) {
        checkButton(); // Independently check button state

        if (state == STATE_ON) {
            uint16_t temperature = readTemperature();
            uint16_t batteryVoltage = readBatteryVoltage();

            applyTemperatureDerating(temperature);
            handleLowVoltage(batteryVoltage);

            setPWM(powerLevel); // Set LED power level
        } else {
            setPWM(POWER_OFF); // Turn off LED if not in STATE_ON
        }
    }
}

Thank you for any help
 

ronsimpson

Joined Oct 7, 2019
3,037
You wrote all the code and did not try any of it?
Have you tried just blinking a single LED?

It is very common for new programmers to not set the output pins to output before writing to them.
 
Last edited:

AlbertHall

Joined Jun 4, 2014
12,346
If you are not using the comparator module you need to configure CMCON0 to disable it otherwise the omparator pins are by default set as analog inputs.
 
Top