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.
if (ledActualBrightness <= ledTargetBrightness) ledActualBrightness = ledTargetBrightness;
is meant to do? \$\endgroup\$