Rotary Encoder-4Channel PWM

Thread Starter

stobby

Joined Jun 20, 2018
36
Hi, I have searched the forum and could not find anything similar to what i am after.

My project is to output four separately controllable 'PWM' signals from a PIC18f4550, Controlled via a rotary encoder using the push button to cycle between channels. Firstly i am very new to PIC's in general and have learned some basic functions writing in XC8 such as blinking led's, Dimming with use of the delay() function, setting config bits, Etc.

I have generated code that reads the output from the Rotary encoder and is de-bounced enough for my purposes. However i am unsure of how to implement this into software PWM. So far my Code outputs via RD2 and RD3 to light up LED's as a visual representation of the encoder, The encoder is on RB2 and RB3. As this chip only has two 'CCP' modules i assume the four channels will be software generated. I have not been able to find examples of software PWM in XC8.

Should the encoder count to a register such as INTCON and use this a function to control duty cycle? A snippet or example of software pwm in XC8 would be very helpful. Note the frequency will be low on all channels Sub 1khz. I will include my code so far.

Any help would be great, Thank you.

C:
include <xc.h>
#include <stdio.h>
#include <stdlib.h>


#define _XTAL_FREQ 4000000


void main(void)
{      
    OSCCON = 0b01110010;  
    TRISB = 0b00000110;   
    TRISD = 0x00;         

    RCONbits.IPEN = 1;    
    INTCON = 0b10000000;  
    INTCON2 = 0b10000000; 
    INTCON3 = 0b11011000; 
    INTCON2bits.RBPU = 0;

    while(1)
    {
        LATD = 0;
        LATB = 1;
        
    }
}

void __interrupt() rotaryISR(void)                   
{
    if (INTCON3bits.INT1IF > INTCON3bits.INT2IF)      
    {
        __delay_ms(100);
         PORTDbits.RD2;                                 
        INTCON2bits.INTEDG1 = ~INTCON2bits.INTEDG1;   
        INTCON2bits.INTEDG2 = ~INTCON2bits.INTEDG2;
        __delay_ms(50);
        INTCON3bits.INT1IF = 0;                       
        INTCON3bits.INT2IF = 0;                       
    }
  
    if (INTCON3bits.INT2IF > INTCON3bits.INT1IF)      
    {
        __delay_ms(100);
        PORTDbits.RD3;                                     
        INTCON2bits.INTEDG1 = ~INTCON2bits.INTEDG1;   
        INTCON2bits.INTEDG2 = ~INTCON2bits.INTEDG2;
        __delay_ms(50);
        INTCON3bits.INT1IF = 0;                       
        INTCON3bits.INT2IF = 0;                       
    }
    else   
    {}
}
Mod edit: code tags - JohnInTX
 
Last edited by a moderator:

Thread Starter

stobby

Joined Jun 20, 2018
36
Look at picmicro devices that use the QEI interface module and also AN937
Would the QEI module cut down on processor load? I have suspected that running the software decoder whilst simultaneously outputting four channels of software pwm may be too much load.

I am not Entirely restricted to the 18f4550, However i happen to have a few on hand. In mentioning other micros, Would MCC cable devices be helpful for those new to micro controllers?
 

Pushkar1

Joined Apr 5, 2021
416
Hi, I have searched the forum and could not find anything similar to what i am after.
@stobby I have a free advice for you always use the code tag when posting the code. Write good comments in your code so that it is easy for others to understand what is the problem in the code.

C:
include <xc.h>
#include <stdio.h>
#include <stdlib.h>

#define _XTAL_FREQ 4000000

void main(void)
{
         OSCCON = 0b01110010;
         TRISB = 0b00000110;
         TRISD = 0x00;

         RCONbits.IPEN = 1;
         INTCON = 0b10000000;
         INTCON2 = 0b10000000;
         INTCON3 = 0b11011000;
         INTCON2bits.RBPU = 0;

        while(1)
        {
             LATD = 0;
             LATB = 1;

        }
}

void __interrupt() rotaryISR(void)
{
       if (INTCON3bits.INT1IF > INTCON3bits.INT2IF)
{
     __delay_ms(100);
      PORTDbits.RD2;
      INTCON2bits.INTEDG1 =                
      ~ INTCON2bits.INTEDG1;
      INTCON2bits.INTEDG2 =       ~INTCON2bits.INTEDG2;
    __delay_ms(50);
    INTCON3bits.INT1IF = 0;
    INTCON3bits.INT2IF = 0;
}

if (INTCON3bits.INT2IF > INTCON3bits.INT1IF)
{
    __delay_ms(100);
      PORTDbits.RD3;
INTCON2bits.INTEDG1 = ~INTCON2bits.INTEDG1;
INTCON2bits.INTEDG2 = ~INTCON2bits.INTEDG2;
__delay_ms(50);
INTCON3bits.INT1IF = 0;
INTCON3bits.INT2IF = 0;
}
else
{}
 

BobaMosfet

Joined Jul 1, 2009
1,952
@stobby I cannot underscore how important it is that you get yourself access to an oscilloscope if you're doing anything with signal-level stuff. You will learn more by seeing reality in action, than by any thing else- particularly if you are learning both coding and electronics- you need to be able to 'see' things.

Debuggers are great, but also learn the value of 'breadcrumb'. That is, using other capabilities of the MCU to yield information to you when in specific conditions (such as toggling a pin on, or off, or outputting a binary pattery to a group fo LEDs which gives you 'state' information as your code progresses through things- Even making it serially output character data that you capture so you know what is going on and can analyze it.

I recognize this is beyond you at this time- but as an example, one of the things I did, as I develop projects commercially, is a created a means of outputting information whenever I entered a functiion, and wanted values output. Encoded so I output only what is necessary. I then wrote another program that could take that input and 'decode' it to show me exactly what was going on- which allows me to operating things in real-time while getting an exact stream of what it's doing when and where.

Debugging is as good as you are creative ;) Extra effort to learn how to debug creatively, can save you a great deal of time, when an alternative 3rd part debugger is unavailable or interferes with things.
 

Thread Starter

stobby

Joined Jun 20, 2018
36
Thank you both for the advice and patience.

I will absolutely use comment tags from now on, In my haste to begin coding it was simply overlooked, I have access to a basic oscilloscope with Sampling down to 10uS. And i have fair experience in 'PWM' and Analog power conversion circuits. However like i mentioned am totally green to Embedded devices.

As far as compilers i am pondering whether starting in Assembly maybe a better route to truly understand What is happening 'Under the Hood' so too speak. However for now i might stick too xc8.

Currently i am still trying to understand how to go about generating soft pwm, My basic idea so far is:
Result from QEI code >> Count to TIMER0 >> Trigger Interrupt to act a duty cycle based off TIMER0 value.

Am i possibly on the right track here?

Thanks again for the input

regards.
 

Pushkar1

Joined Apr 5, 2021
416
@stobby I see many delays in your interrupt routine. can you tell why you have put many delay in ISR. Interrupted routine should always be short. Use of delay should always be avoided. You are not doing anything in else statement at end of code.
 

Thread Starter

stobby

Joined Jun 20, 2018
36
@Pushkar1. Whoops, Yes i left a stray bit of code, it was a failed attempt at de-bouncing via the 'else' statement.

The delays i have used De-bounce the encoder by restricting the possible amount of inputs in a given time. However as i understand these may not be a great solotution and should be avoided?
 

JohnInTX

Joined Jun 26, 2012
4,654
This thread covers a similar project - a software PWM controlled by a mechanical quadrature encoder.
A periodic interrupt is used to sample the encoder inputs, debounce, and vary the PWM duty cycle.
The PWM is also generated by the periodic interrupt and there are a few other functions as well.
You can add more PWMs using the same method. The key is to compute the PWM port values between interrupts then copy the port image to the pins immediately on interrupt.

https://forum.allaboutcircuits.com/threads/pic-12f629-question.116963/post-918785

Maybe that will give you some ideas.
And @Pushkar1 is right, never use blocking delays in an interrupt service routine. If you do, it's wrong.

Good luck!
 

Thread Starter

stobby

Joined Jun 20, 2018
36
@JohnInTX Thanks for the great information, I think i have plenty of information too digest and most of questions are cleared up. thanks everybody.

If i happen to have subsequent questions May i direct message you guys instead of making a whole thread? It would be much appreciated.

regards.
 

JohnInTX

Joined Jun 26, 2012
4,654
Thanks for the kudos but we discourage helping via PM. The idea is to share and build a public knowledge base to help future travelers as well as today’s. Feel free to PM is you have questions or ideas to make that happen.
 

Thread Starter

stobby

Joined Jun 20, 2018
36
@JohnInTX @Pushkar1 @BobaMosfet



Hi again all, Not sure if it is appropriate to revive this thread, However my question is directly related to the thread topic.

I have made great progress on this project, So far i have implemented multiple channels of software pwm, And interfaced a rotary encoder to control the duty cycle.

I am stuck with my final part of the puzzle and what should have been the easiest part, To interface the push button to switch between controlling the various pwm channels.

currently i have the Button to RA1 (15k Pull up). When the button is pushed it increments the value of a variable. However so far it appears as though this value changes randomly without any input, Resulting in both channels being randomly controlled.

Circuit breakdown.
Rotary Encoder to RB1-RB2 (RC filtered).
Push Button on RA1 (15k Pull up)
pwm outputs on RD0 - RD1

I hope i have added an acceptable amount of comments and info. Thanks All


ROTARY ENCODER PWM:
// CONFIG1L

#pragma config PLLDIV = 2       // PLL Prescaler Selection bits (Divide by 2 (8 MHz oscillator input))

#pragma config CPUDIV = OSC1_PLL2// System Clock Postscaler Selection bits ([Primary Oscillator Src: /1][96 MHz PLL Src: /2])

#pragma config USBDIV = 1       // USB Clock Selection bit (used in Full-Speed USB mode only; UCFG:FSEN = 1) (USB clock source comes directly from the primary oscillator block with no postscale)


// CONFIG1H

#pragma config FOSC = INTOSC_HS // Oscillator Selection bits (Internal oscillator, HS oscillator used by USB (INTHS))

#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)

#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)


// CONFIG2L

#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)

#pragma config BOR = OFF        // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)

#pragma config BORV = 3         // Brown-out Reset Voltage bits (Minimum setting 2.05V)

#pragma config VREGEN = OFF     // USB Voltage Regulator Enable bit (USB voltage regulator disabled)


// CONFIG2H

#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))

#pragma config WDTPS = 16384    // Watchdog Timer Postscale Select bits (1:32768)


// CONFIG3H

#pragma config CCP2MX = OFF     // CCP2 MUX bit (CCP2 input/output is multiplexed with RB3)

#pragma config PBADEN = OFF     // PORTB A/D Enable bit (PORTB<4:0> pins are configured as digital I/O on Reset)

#pragma config LPT1OSC = OFF    // Low-Power Timer 1 Oscillator Enable bit (Timer1 configured for higher power operation)

#pragma config MCLRE = OFF      // MCLR Pin Enable bit (RE3 input pin enabled; MCLR pin disabled)


// CONFIG4L

#pragma config STVREN = OFF     // Stack Full/Underflow Reset Enable bit (Stack full/underflow will not cause Reset)

#pragma config LVP = OFF        // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)

#pragma config ICPRT = OFF      // Dedicated In-Circuit Debug/Programming Port (ICPORT) Enable bit (ICPORT disabled)

#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))


// CONFIG5L

#pragma config CP0 = OFF        // Code Protection bit (Block 0 (000800-001FFFh) is not code-protected)

#pragma config CP1 = OFF        // Code Protection bit (Block 1 (002000-003FFFh) is not code-protected)

#pragma config CP2 = OFF        // Code Protection bit (Block 2 (004000-005FFFh) is not code-protected)

#pragma config CP3 = OFF        // Code Protection bit (Block 3 (006000-007FFFh) is not code-protected)


// CONFIG5H

#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) is not code-protected)

#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM is not code-protected)


// CONFIG6L

#pragma config WRT0 = OFF       // Write Protection bit (Block 0 (000800-001FFFh) is not write-protected)

#pragma config WRT1 = OFF       // Write Protection bit (Block 1 (002000-003FFFh) is not write-protected)

#pragma config WRT2 = OFF       // Write Protection bit (Block 2 (004000-005FFFh) is not write-protected)

#pragma config WRT3 = OFF       // Write Protection bit (Block 3 (006000-007FFFh) is not write-protected)


// CONFIG6H

#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) are not write-protected)

#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot block (000000-0007FFh) is not write-protected)

#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM is not write-protected)


// CONFIG7L

#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 (000800-001FFFh) is not protected from table reads executed in other blocks)

#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 (002000-003FFFh) is not protected from table reads executed in other blocks)

#pragma config EBTR2 = OFF      // Table Read Protection bit (Block 2 (004000-005FFFh) is not protected from table reads executed in other blocks)

#pragma config EBTR3 = OFF      // Table Read Protection bit (Block 3 (006000-007FFFh) is not protected from table reads executed in other blocks)


// CONFIG7H

#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot block (000000-0007FFh) is not protected from table reads executed in other blocks)




#include <xc.h>

#include <stdint.h>

#define _XTAL_FREQ 8000000

#define PWM1_D TRISD0

#define PWM1 PORTDbits.RD0

#define PWM2_D TRISD1

#define PWM2 PORTDbits.RD1



uint16_t TMR1_C;

uint8_t PWM1DC = 0, PWM2DC = 0;

unsigned int BUTTON = 1;



void TMR1_Init(void);


void main(void) {

  

 

  

  PWM1_D = 0; // Set As Output Pin PWM1

  PWM2_D = 0; // Set As Output Pin PWM2

  TMR1_Init(); // Setup Timer1

  OSCCON = 0b01111100; //8MHZ CLOCK

   TRISB = 0b00000110;

   RCONbits.IPEN = 1;     

    INTCON = 0b10000000;   

    INTCON2 = 0b10000000;   //SETUP INTERRUPTS FOR ENCODER

    INTCON3 = 0b11011000; 

    INTCON2bits.RBPU = 0;

     PORTAbits.RA1 = 1;  //INITIALIZE BUTTON HIGH

    

    

    

      

  while(1)

  {

        LATB = 1; //SET LATB AS INPUT FOR ENCODER

        TRISAbits.TRISA1 = 1;          //RAO BUTTON INPUT

      

        if (!PORTAbits.RA1){          //WAS BUTTON PUSHED (RA0 LOW)

            __delay_ms(50);           //DEBOUNCE

            if (!PORTAbits.RA1){      //CHECK AGAIN

                BUTTON = BUTTON+1;             //INCREMENT VALUE OF 'BUTTON'

            }

        }

      

        if (BUTTON == 3){             //BUTTON COUNT LOOP

            BUTTON = 1;

        }

      

}

 

  }

//--------------------------------------------------------


void setup (){

  

    TMR0ON = 1; //SETUP TIMER0 FOR ROTARY ENCODER

    T08BIT = 1;

    T0CS = 1;

    PSA = 0;

  

}


void __interrupt() ISR()

{

  if(TMR1IF)

  {

    TMR1_C++;

    if(TMR1_C>=PWM1DC)

    {

      PWM1 = 0; // Drive PWM Output LOW

    }

    if(TMR1_C>=PWM2DC)

    {

      PWM2 = 0; // Drive PWM Output LOW

    }

    if(TMR1_C==100)

    {

      PWM1 = 1; // Drive PWM Output HIGH

      PWM2 = 1;

      TMR1_C = 0; // Reset Counter

    }

    TMR1IF = 0;

    TMR1 = 65520; // After Compensation

    //TMR1 = 65520; // Theoretical Value From Calculations

  

    if (INTCON3bits.INT1IF > INTCON3bits.INT2IF)       

    {

        if (BUTTON == 1){PWM1DC++;}//ON BUTTON VALUE '1' INCREMENT PWM1

        if (BUTTON == 2){PWM2DC++;}//ON BUTTTON VALUE '2' INCREMENT PWM2

      

      

        INTCON2bits.INTEDG1 = ~INTCON2bits.INTEDG1;    //ROTARY ENCODER IF, CW INCREMENT VALUE

        INTCON2bits.INTEDG2 = ~INTCON2bits.INTEDG2;

        INTCON3bits.INT1IF = 0;                       

        INTCON3bits.INT2IF = 0;                       

    }

  

    if (INTCON3bits.INT2IF > INTCON3bits.INT1IF)       

    {

        if (BUTTON == 1){PWM1DC--;}//ON BUTTON VALUE '1' DECREMENT PWM1

        if (BUTTON == 2){PWM2DC--;}//ON BUTTTON VALUE '2' DECREMENT PWM2

      

                                        

        INTCON2bits.INTEDG1 = ~INTCON2bits.INTEDG1;     //ROTARY ENCODER, IF CCW DECREMENT VALUE

        INTCON2bits.INTEDG2 = ~INTCON2bits.INTEDG2;

        INTCON3bits.INT1IF = 0;                       

        INTCON3bits.INT2IF = 0;                       

    }

  

 

}

}

//--------------------------------------------------------

void TMR1_Init(void)

{

  // -- [[ Configure Timer1 To Operate In Timer Mode ]] --

  // Clear The Timer1 Register. To start counting from 0

  TMR1 = 65536;

  // Choose the local clock source (timer mode)

  TMR1CS = 0;

  // Choose the desired  ratio (1:1)

  T1CONbits.T1CKPS1 = 1;

  T1CONbits.T1CKPS0 = 1;

  // Switch ON Timer1 Module!

  TMR1ON = 1;

  T1CONbits.RD16 = 0; //8 BIT MODE

  T1RUN = 1;

  // -- [[ Interrupts Configurations ]] --

  TMR1IE = 1; // Timer1 Interrupt Enable Bit

 

  TMR1IF = 0; // Clear The Interrupt Flag Bit

  PEIE = 1; // Peripherals Interrupts Enable Bit

  GIE = 1; //ENABLE GLOBAL INTERUPTS

}
 
Last edited:

Thread Starter

stobby

Joined Jun 20, 2018
36
Hello again, I have found the source of my inability to read button inputs on PORTA. And i am sure this will help many beginners like myself. I did not have my ADCON and CMCON registers set correctly.

To read digital inputs they must be disabled.
ADCON1 = 0xff; //Disable PORTA ADC
CMCON = 0xff; //Disable PORTA Comparators

I hope this can help others, Would anyone be interested in the complete code?
 

atferrari

Joined Jan 6, 2004
4,579
@stobby I cannot underscore how important it is that you get yourself access to an oscilloscope if you're doing anything with signal-level stuff. You will learn more by seeing reality in action, than by any thing else- particularly if you are learning both coding and electronics- you need to be able to 'see' things.

Debuggers are great, but also learn the value of 'breadcrumb'. That is, using other capabilities of the MCU to yield information to you when in specific conditions (such as toggling a pin on, or off, or outputting a binary pattery to a group fo LEDs which gives you 'state' information as your code progresses through things- Even making it serially output character data that you capture so you know what is going on and can analyze it.

I recognize this is beyond you at this time- but as an example, one of the things I did, as I develop projects commercially, is a created a means of outputting information whenever I entered a functiion, and wanted values output. Encoded so I output only what is necessary. I then wrote another program that could take that input and 'decode' it to show me exactly what was going on- which allows me to operating things in real-time while getting an exact stream of what it's doing when and where.

Debugging is as good as you are creative ;) Extra effort to learn how to debug creatively, can save you a great deal of time, when an alternative 3rd part debugger is unavailable or interferes with things.
I recently started debugging with a Pickit4.
For many years I used:

Green LED for good results
Red LED for undesired ones
Flashing LEDs so I could distinguish different situations
An LCD display for massive complex data.
One IO pin, toggling (18F family does in just one instruction) or pulsed briefly. Mine, whose creative name for almost 20 years is PIN_DEBUG, does it
at the very beginning/end of the ISR acting as a ticking for all my inner soft toned routines.

Pickit4, I like it to peek in memory.

/Edit to add
Once a certain module was passed as OK by your test, do not forget to eliminate all debugging code. You could have it becoming your new unexpected bug/s
Edit/
 

Thread Starter

stobby

Joined Jun 20, 2018
36
@atferrari
Thanks for the tip i will keep this in mind, yes i have been meaning to get my hands on a PicKit4. This has been a great initial project albeit probably not the best for a newbie to tackle.

Here is the complete XC8 code, I have tried to Annotate as much as possible and make each function clear.

QUAD CHANNEL SOFTWARE PWM, ROTARY ENCODER:
/*
 * File:   4 CHANNEL PWM, CONTROLLED VIA ROTARY ENCODER
 * Author: STOBBY
 * PIC 18F4550
 * PWM CHN 1 = RD0
 * PWM CHN 2 = RD1
 * PWM CHN 3 = RD2
 * PWM CHN 4 = RD3
 * PWM FREQ 100HZ
 * ENOCDER INPUT ON = RB1, RB2 (RC FILTERED)
 * BUTTON INPUT ON = RA1 (RC FILTERED + 10K PULLUP)
 * 8MHZ CLOCK
 */

// CONFIG1L
#pragma config PLLDIV = 2       // PLL Prescaler Selection bits (Divide by 2 (8 MHz oscillator input))
#pragma config CPUDIV = OSC1_PLL2// System Clock Postscaler Selection bits ([Primary Oscillator Src: /1][96 MHz PLL Src: /2])
#pragma config USBDIV = 1       // USB Clock Selection bit (used in Full-Speed USB mode only; UCFG:FSEN = 1) (USB clock source comes directly from the primary oscillator block with no postscale)

// CONFIG1H
#pragma config FOSC = INTOSC_HS // Oscillator Selection bits (Internal oscillator, HS oscillator used by USB (INTHS))
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOR = OFF        // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)
#pragma config BORV = 3         // Brown-out Reset Voltage bits (Minimum setting 2.05V)
#pragma config VREGEN = OFF     // USB Voltage Regulator Enable bit (USB voltage regulator disabled)

// CONFIG2H
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
#pragma config WDTPS = 16384    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = OFF     // CCP2 MUX bit (CCP2 input/output is multiplexed with RB3)
#pragma config PBADEN = OFF     // PORTB A/D Enable bit (PORTB<4:0> pins are configured as digital I/O on Reset)
#pragma config LPT1OSC = OFF    // Low-Power Timer 1 Oscillator Enable bit (Timer1 configured for higher power operation)
#pragma config MCLRE = OFF      // MCLR Pin Enable bit (RE3 input pin enabled; MCLR pin disabled)

// CONFIG4L
#pragma config STVREN = OFF     // Stack Full/Underflow Reset Enable bit (Stack full/underflow will not cause Reset)
#pragma config LVP = OFF        // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
#pragma config ICPRT = OFF      // Dedicated In-Circuit Debug/Programming Port (ICPORT) Enable bit (ICPORT disabled)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection bit (Block 0 (000800-001FFFh) is not code-protected)
#pragma config CP1 = OFF        // Code Protection bit (Block 1 (002000-003FFFh) is not code-protected)
#pragma config CP2 = OFF        // Code Protection bit (Block 2 (004000-005FFFh) is not code-protected)
#pragma config CP3 = OFF        // Code Protection bit (Block 3 (006000-007FFFh) is not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) is not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM is not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 (000800-001FFFh) is not write-protected)
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 (002000-003FFFh) is not write-protected)
#pragma config WRT2 = OFF       // Write Protection bit (Block 2 (004000-005FFFh) is not write-protected)
#pragma config WRT3 = OFF       // Write Protection bit (Block 3 (006000-007FFFh) is not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) are not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot block (000000-0007FFh) is not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM is not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 (000800-001FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 (002000-003FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF      // Table Read Protection bit (Block 2 (004000-005FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF      // Table Read Protection bit (Block 3 (006000-007FFFh) is not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot block (000000-0007FFh) is not protected from table reads executed in other blocks)



#include <xc.h>
#include <stdint.h>
#define _XTAL_FREQ 8000000
#define PWM1_D TRISD0
#define PWM1 PORTDbits.RD0   //PWM CHANNEL 1

#define PWM2_D TRISD1
#define PWM2 PORTDbits.RD1   //PWM CHANNEL 2

#define PWM3_D TRISD2
#define PWM3 PORTDbits.RD2   //PWM CHANNEL 3

#define PWM4_D TRISD3
#define PWM4 PORTDbits.RD3   //PWM CHANNEL 4


uint16_t TMR1_C;  //COUNT TMR1
uint8_t PWM1DC = 0, PWM2DC = 0, PWM3DC = 0, PWM4DC;   // ASSIGN VALUES
char BUTTON = 1;  //IDENTIFY 'BUTTON'

 
void TMR1_Init(void);
 
void main(void) {
    
  
    
  PWM1_D = 0; // Set As Output Pin PWM1
  PWM2_D = 0; // Set As Output Pin PWM2
  PWM3_D = 0; // Set As Output Pin PWM3
  PWM4_D = 0; // Set As Output Pin PWM4
  TMR1_Init(); // Setup Timer1
  OSCCON = 0b01111100; //8MHZ CLOCK
   TRISB = 0b00000110; //ENCODER INPUT
   RCONbits.IPEN = 1;     
    INTCON = 0b10000000;   
    INTCON2 = 0b10000000;   //SETUP INTERRUPTS FOR ENCODER
    INTCON3 = 0b11011000;   
    INTCON2bits.RBPU = 0;
     PORTAbits.RA1 = 1;  //INITIALIZE BUTTON HIGH
     TRISA1 = 1;          //RAO BUTTON INPUT
     ADCON1 = 0xff;         //Disable PORTA ADC
     CMCON = 0xff;          //Disable PORTA Comparators
    
    
        
  while(1)
  {
        LATB = 1; //SET LATB AS INPUT FOR ENCODER
        
        
        if (PORTAbits.RA1 == 0){          //WAS BUTTON PUSHED (RA0 LOW)
            __delay_ms(50);           //DEBOUNCE
            if (PORTAbits.RA1 == 0){      //CHECK AGAIN
                BUTTON = BUTTON+1;             //INCREMENT VALUE OF 'BUTTON'
            }
        }
        
        if (BUTTON > 4){             //BUTTON COUNT LOOP AND VALUE LIMIT
            BUTTON = 1;
        }
        
}
 
  }
//--------------------------------------------------------

void setup (){
    
    TMR0ON = 1; //SETUP TIMER0 FOR ROTARY ENCODER
    T08BIT = 1;
    T0CS = 1;
    PSA = 0;
    
}

void __interrupt() ISR()
{
  if(TMR1IF)
  {
    TMR1_C++;
    if(TMR1_C>=PWM1DC)
    {
      PWM1 = 0; // Drive PWM Output LOW
    }
    if(TMR1_C>=PWM2DC)
    {
      PWM2 = 0; // Drive PWM Output LOW
    }
    if(TMR1_C>=PWM3DC)
    {
      PWM3 = 0; // Drive PWM Output LOW
    }
    if(TMR1_C>=PWM4DC)
    {
      PWM4 = 0; // Drive PWM Output LOW
    }
    
    if(TMR1_C==100)
    {
      PWM1 = 1; // Drive PWM Output HIGH
      PWM2 = 1;
      PWM3 = 1;
      PWM4 = 1;
      TMR1_C = 0; // Reset Counter
    }
    TMR1IF = 0;
    TMR1 = 65520; // After Compensation
    //TMR1 = 65530; // Theoretical Value From Calculations
    
    if (INTCON3bits.INT1IF > INTCON3bits.INT2IF)       
    {
        if (BUTTON == 1){PWM1DC++;}//ON BUTTON VALUE '1' INCREMENT PWM1
        if (BUTTON == 2){PWM2DC++;}//ON BUTTTON VALUE '2' INCREMENT PWM2
         if (BUTTON == 3){PWM3DC++;}//ON BUTTTON VALUE '3' INCREMENT PWM3
         if (BUTTON == 4){PWM4DC++;}//ON BUTTTON VALUE '4' INCREMENT PWM4
      
        INTCON2bits.INTEDG1 = ~INTCON2bits.INTEDG1;    //ROTARY ENCODER IF, CW INCREMENT VALUE
        INTCON2bits.INTEDG2 = ~INTCON2bits.INTEDG2;
        INTCON3bits.INT1IF = 0;                         
        INTCON3bits.INT2IF = 0;                         
    }
    
    if (INTCON3bits.INT2IF > INTCON3bits.INT1IF)       
    {
        if (BUTTON == 1){PWM1DC--;}//ON BUTTON VALUE '1' DECREMENT PWM1
        if (BUTTON == 2){PWM2DC--;}//ON BUTTTON VALUE '2' DECREMENT PWM2
        if (BUTTON == 3){PWM3DC--;}//ON BUTTTON VALUE '3' DECREMENT PWM3
        if (BUTTON == 4){PWM4DC--;}//ON BUTTTON VALUE '4' DECREMENT PWM4
                                        
        INTCON2bits.INTEDG1 = ~INTCON2bits.INTEDG1;     //ROTARY ENCODER, IF CCW DECREMENT VALUE
        INTCON2bits.INTEDG2 = ~INTCON2bits.INTEDG2;
        INTCON3bits.INT1IF = 0;                         
        INTCON3bits.INT2IF = 0;                         
    }
    
 
}
}
//--------------------------------------------------------
void TMR1_Init(void)
{
  // -- [[ Configure Timer1 To Operate In Timer Mode ]] --
  // Clear The Timer1 Register. To start counting from 0
  TMR1 = 65536;
  // Choose the local clock source (timer mode)
  TMR1CS = 0;
  // Choose the desired  ratio (1:1)
  T1CONbits.T1CKPS1 = 1;
  T1CONbits.T1CKPS0 = 1;
  // Switch ON Timer1 Module!
  TMR1ON = 1;
  T1CONbits.RD16 = 0; //8 BIT MODE
  T1RUN = 1;
  // -- [[ Interrupts Configurations ]] --
  TMR1IE = 1; // Timer1 Interrupt Enable Bit
 
  TMR1IF = 0; // Clear The Interrupt Flag Bit
  PEIE = 1; // Peripherals Interrupts Enable Bit
  GIE = 1; //ENABLE GLOBAL INTERUPTS
}
 

BobaMosfet

Joined Jul 1, 2009
1,952
Thank you both for the advice and patience.

I will absolutely use comment tags from now on, In my haste to begin coding it was simply overlooked, I have access to a basic oscilloscope with Sampling down to 10uS. And i have fair experience in 'PWM' and Analog power conversion circuits. However like i mentioned am totally green to Embedded devices.

As far as compilers i am pondering whether starting in Assembly maybe a better route to truly understand What is happening 'Under the Hood' so too speak. However for now i might stick too xc8.

Currently i am still trying to understand how to go about generating soft pwm, My basic idea so far is:
Result from QEI code >> Count to TIMER0 >> Trigger Interrupt to act a duty cycle based off TIMER0 value.

Am i possibly on the right track here?

Thanks again for the input

regards.
Start with assembly language. I cannot underscore how much better you will be as a coder if you learn things at this 'bare-metal' level. Learning assembly language on *any* processor will make you a better coder at any higher level programming language because you understand what the machine is actually doing- and you will code 'with it' rather than 'against it'.
 

Thread Starter

stobby

Joined Jun 20, 2018
36
Start with assembly language. I cannot underscore how much better you will be as a coder if you learn things at this 'bare-metal' level. Learning assembly language on *any* processor will make you a better coder at any higher level programming language because you understand what the machine is actually doing- and you will code 'with it' rather than 'against it'.
Very true, i Think for my next project i will start fresh with ASM, As much as i dread the idea.
 
Top