0
\$\begingroup\$

My current knowledge of PICs is currently limited to simple LED blinking with and without the use of interrupts and timers in C code with XC8, MPLAB IDE, and Pickit 3. I have trouble implementing this software pwm on a PIC16F628A based on instructions for other pics of the PIC18F family at this page: software PWM for PIC18F family and whose original implementation did not make complete sense either.

Here's my version:

#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

#define _XTAL_FREQ 4000000

unsigned char pwmCounter = 0;
unsigned char ledActualBrightness = 20;
unsigned char ledTargetBrightness = 0;
unsigned char fadeCounter = 0;
void initialize (){
    // Tri-state registers. Sets all pins to outputs
    TRISA = 0; 
    TRISB = 0;
    // Turn off all comparators
    CMCON = 7;

    PORTA = 0;
    PORTB = 0;
    GIE = 1;
    T0IE = 1;
    T0CS = 0;
    PSA = 0;
    PS0 = 0;
    PS1 = 1;
    PS2 = 0;
    TMR0 = 255 - 250;
}


void main(void) {

    initialize(); 

    while (1){
    }
}

void interrupt isr(void){
    if (T0IF){
        GIE = 0;
        T0IE = 0;
        T0IF = 0;
        // Perform the PWM brightness control
        if (ledActualBrightness > pwmCounter)
            RB0 = 1; else RB0 = 0;

        pwmCounter++;
        if (pwmCounter > 19) pwmCounter = 0;

        // Perform fading control
        if (ledActualBrightness <= ledTargetBrightness)
            ledActualBrightness = ledTargetBrightness;
        else
        {
            fadeCounter++;
            if (fadeCounter >= 24)
            {
                ledActualBrightness--;
                fadeCounter = 0;
            }
        }

        TMR0 = 255 - 250;
        GIE = 1;
        T0IE = 1;
    }
}

This code only makes the LED fade completely only once, after which it stays completely dimmed (or OFF) contrary to what I was aiming at: having the LED repeatedly fade off. In addition, I can see a high frequency oscillation of the brightness by eye while it slowly fades (fading occurs over about 0.5 second). With respect to the original version in the link provided, in addition to the PIC setup, I changed my prescaler to have a ratio of 1:8, thinking it gives me 250 timer ticks to time 1000 microseconds with the internal oscillator of 4Mhz. I also changed the ledTargetBrightness from 0 to 20. Having it at zero like in the original code made no sense (and it indeed didn't work at all, the LED stayed OFF all the time).

I'd like to understand why my version is only fading the LED once and not periodically every ~0.5 second, and how it can be fixed while keeping the same strategy (doing it within this interrupt).

At some point I thought it could be a mishandling of the interrupt flags, not set or cleared in the right sequence or at the right place but reading the datasheet showed it seems to be ok the way I use them: T0IF is cleared before re-enabling the interrupt through TOIE = 1 (and GIE = 1 although it's not obvious whether it's useful to do that in the interrupt code block).

[EDIT] Following the advice given in the comment, the LED not lighting up again is due to the improper reset in the if statement. Instead of resetting to the starting brightness value of 20, I left it as it was in the original code, where it is in fact just set to 0 which forces the LED to stay OFF. If I change the if statement to:

if (ledActualBrightness <= ledTargetBrightness)
            ledActualBrightness = 20;

The fading off repeats itself. However, I still see high frequency on/off oscillation while it fades, which I still don't understand. The timing should be fast enough to not even notice this.

\$\endgroup\$
  • \$\begingroup\$ Welcome to EE.SE. My first guess is you do not have code to reset the brightness level. I see a couple of countdown timers, but you need to reset brightness before your code exits. \$\endgroup\$ – Sparky256 Mar 27 '18 at 3:26
  • \$\begingroup\$ Thank you @Sparky256. Regarding the brightness reset, isn't it what if (ledActualBrightness <= ledTargetBrightness) ledActualBrightness = ledTargetBrightness; is meant to do? \$\endgroup\$ – Wall-E Mar 27 '18 at 3:45
  • \$\begingroup\$ I see, you're right @Sparky256, I didn't adapt this properly from the original code. That if statement does not reset brightness at all and I don't even understand what it was for originally. \$\endgroup\$ – Wall-E Mar 27 '18 at 3:54
0
\$\begingroup\$

Your code is good, but the Timer0 input on Microchip parts is the instruction cycle time, which is the oscillator frequency/4. So with your present setup, you are running with an input frequency of 1 Mhz instead of 4Mhz. After going through your prescaler, you are down to 125 KHz, and since you are counting up from 250, you are getting an interrupt at a rate of 500 Hz. Since you are turning the LED on and off every 20 interrupts, you are down to 25 Hz, which is what you can see. Try a prescaler of one. This will speed up the pwm to 200 Hz, but will also reduce the time to fade out. You can increase the fadeCounter >= number to 192 (24*8) to compensate and make it fade at the same rate. If it's still fading too fast, change fadeCounter from an unsigned char to a 16-bit integer so you can use a larger number than 255. Have fun!

\$\endgroup\$
0
\$\begingroup\$

After looking at your code the if (ledActualBrightness <= ledTargetBrightness) ledActualBrightness = ledTargetBrightness; should be ledActualBrightness = 20; (or use the alias for that value).

The code as you had it written simply kept it locked at zero brightness. At the end of your code you need to reset it back to full brightness so it can fade again.

Duplicate that code (another loop) but reverse your count ups and downs and you get a light that goes up then down, then up, then down, etc.

As for the noise, maybe it is a break in the loop that makes an LED flicker. An LED can flash on and off thousands of times per second. An alternative is to use an RC network to drive the LED with cleaned up pulses.

Code has no mercy on the programmer. Tiny boo-boos can have steam coming out of your ears. Good luck.

\$\endgroup\$
  • \$\begingroup\$ thank you very much for looking at the code. I hope I can eventually fix that flickering/oscillating issue with the code. I'm trying to make a pcb with that kind of pic with 7 leds controlled with software pwm and i want to minimize the number of components to solder. Adding resistors and capacitors could defeit the purpose. I guess I may need to change the model of the pic for this though if it's too slow or too inaccurate to fix the visible pwm oscillation. \$\endgroup\$ – Wall-E Mar 27 '18 at 12:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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