Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC16F628A PWM for LED Chaser

Status
Not open for further replies.
As long as you have the other general components, voltage regulators, caps, etc. and some pin header connectors for the pickit connection you should be fine.

You could go for the 16F18326 / 16F18346 as larger pin count ones, for maximum compatibility with the 18313, but it should not really matter.

I set the CPU speed right down on the example to be sure it would work OK on an older, slower device..
It's set a 4MHz, but with the 16F18 series devices it will run at up to 32 MHz - so would stand a lot more LED output sections and still need a higher sub-step divider to give a reasonable output cycle speed.

The step table values for brightness through the cycle probably need tweaking as well, it's just quick guesswork.
 
Cool. Yeah, I have a good bit of stuff here. I'll let you know how it goes once the parts get here. Thank you again.
 
Here's a Charlieplexed example from long ago with something like 32 PWM steps (below). More recent software uses an 8-bit (256 level) BAM (Bit Angle Modulation) driver to provide 64 linear brightness levels and several hundred Hertz refresh rate...

 
I finally got my PICKit-4 and have MPLAB X and the compilers installed. Before, I was using MPLAB with asm files to compile them into hex and that was easy, but I am lost in the sauce on MPLAB X. I created a standalone project and added a new header and created a new main.c and tried to put the code that rjenkinsgb generously created for me, but I'm getting a lot of errors, the first of which is that it doesn't recognize what "int8" is. I pulled up the datasheet for the PIC16F18313 and can't find a reference to "int8" either. Did I do that right by doing what I did? Should I have somehow imported the code instead of creating a new project? I should probably have not started asking before I did my research, but there is a lot of informwation to take in and I just didn't know where to start. Sorry to bug you guys with newbie stuff. Can you point me in the right direction? Thank you.
 
I'm using a different compiler in MPLAB X; the CCS one (which is a lot more versatile) rather than the microchip one.

I thought the only possibly non-standard thing I had used was the line to output the data to the port, sorry...

It's a C language variable type and nothing directly relating to any specific CPU or MPU.

Try adding this near the start:

typedef char int8;

Or you could add
#include <stdint.h>

and change all the int8 to int8_t, which is the more formal type and the properly "portable" one.

Or just change them all to "char".
I normally avoid that purely for clarity, when it's numeric rather than character data - but it compiles to the exact same code & makes no difference at all..
 
Modified file; using stdint.h and uint8_t also requires the "unsigned" prefix to be removed as well; this will hopefully now have minimal problems...

The one line that will likely need changing is

output_a(outimg);

which writes "outimg" to the PORTA register.

edit:
Looking again, I have no idea on whether the first few lines (DEVICE and USE) that configure the device type are compatible with XC8

If you configure the device type in the project, you may just be able to omit those..

edit 2 - It appears you need "#pragma" lines to configure the oscillator etc. in XC8..

Some "light reading" here:
See section 4.3.5

Hopefully someone else on here that uses XC8 can provide more info on the configuration part..

Or this looks a bit clearer - see half way down the page, where is shows a block of #pragma directives, then just after that shows how to get MPLAB to create them for you.



C:
#include <16F18313.h>
#zero_ram

#DEVICE PIC16F18313 ICD=1

#USE DELAY(internal=4MHz)

#USE FAST_IO (A)

#include <stdint.h>


// Do all pin selects
// 01 = VDD                 +5V
// 02 = RA5
// 03 = RA4
// 04 = RA3 / MCLR          Debug VPP
// 05 = RA2
// 06 = RA1 / ICSP CLK
// 07 = RA0 / ICSP DAT
// 08 = VSS                 0V


//#include <stdio.h>
//#include <stdlib.h>

uint8_t pwmreg;        //address configuration
uint8_t stepcount;     //
uint8_t substep;       // PWM cycles per step increment; speed setting


uint8_t outimg;


// PWM range 0-x; a (power of 2) - 1 value
// Using 0 - 31
#define PWM_MAX     0x1f

// Brightness steps for a full rotation sequence of one light;
// eg. 4 or 8 times the number of different-brightness lights, -1
#define STEP_MAX    64

// Output phase, equal offsets through the "step" cycle.
#define LED_P0      0
#define LED_P1      16
#define LED_P2      32
#define LED_P3      48

// Look-up table for brightness sequence for one light
// Brightness range 0 = PWM_MAX

uint8_t  ltable[STEP_MAX] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 5, 7, 8, 9, 10, 12, 15, 17, 20, 22, 25, 28, 32,
32, 28, 25, 22, 20, 17, 15, 12, 10, 9, 8, 7, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};


// Function definition
uint8_t calc_pwm(uint8_t);



/*
*
*/
int main(int argc, char** argv) {
 
    int8_t ledout;
 
    outimg = 0;
    pwmreg = 0;
    stepcount = 0;
    substep = 0;
 
 
    // I/O Pin directions:
    // All out: TRIS = 0x0000 0000
    set_tris_a(0x00);
 
    // OSC = 32 MHz.
    // Timer source = osc / 4; 8 MHz.
 
 
    for(;;)
    {
        restart_wdt();
        pwmreg = pwmreg + 1;
        pwmreg = pwmreg & PWM_MAX;
     
     
        // Do one LED
        // Get the on/off state and set the appropriate output pin
        // Outputs high for on; LED & resistor to 0V.
     
        ledout = calc_pwm(LED_P0);
     
        if(ledout == 1) {
            outimg = outimg | 0x04;
        }
        else {
            outimg = outimg & ~0x04;
        }
 
        // The same sequence for the next:
        ledout = calc_pwm(LED_P1);
     
        if(ledout == 1) {
            outimg = outimg | 0x08;
        }
        else {
            outimg = outimg & ~0x08;
        }
     
        // And again for each pin, passing the phase offset
        // then setting the appropriate pin
        ledout = calc_pwm(LED_P2);
     
        if(ledout == 1) {
            outimg = outimg | 0x10;
        }
        else {
            outimg = outimg & ~0x10;
        }


        ledout = calc_pwm(LED_P3);
     
        if(ledout == 1) {
            outimg = outimg | 0x20;
        }
        else {
            outimg = outimg & ~0x20;
        }

     
        output_a(outimg);

        // At each 0 of the PWM reg, count the substep delay
        // and if that overflows, move to the next sequence step.

        if(pwmreg == 0) {
         
            //
            // Substep limit sets the overall cycle speed
            substep = substep + 1;
            if(substep > 4) {
         
                substep = 0;
             
                stepcount = stepcount + 1;
                                 
                if(stepcount >= STEP_MAX)
                {
                    stepcount = 0;
                }
            }
        }
     



     
    }

    return (1);
}


uint8_t calc_pwm (uint8_t lp) {
    uint8_t x, y;
 
    // Work out the cycle stage for the lamp, then get the table brightness
    // and compare to the PWM count to determine on/off
 
    x = stepcount + lp;
 
    // Wrap the result back to the start of the table;
    // if it is beyond the end.
 
    if(x >= STEP_MAX) {
        x = x - STEP_MAX;
    }
 
    // Get the brightness value from the table
    y = ltable[x];
 
    // Compare to present PWM value to determine on or off
 
    if(y > pwmreg) {
        return 1;
    }
    return 0;
}
 
Last edited:
I could not resist.. This is modified to compile with XC8...

Change #pragma config DEBUG = ON to OFF for it to run stand-alone, without the debug tool connected; or use the "Production" clean & build option, which clears the debug bit, instead of Debug > Debug project.

C:
/*
* File:   Rotating strobe simulator.c
* Author: Robert Jenkins
*/

// PIC16F18313 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FEXTOSC = OFF    // FEXTOSC External Oscillator mode Selection bits (Oscillator not enabled)
#pragma config RSTOSC = HFINT32 // Power-up default value for COSC bits (HFINTOSC with 2x PLL (32MHz))
#pragma config CLKOUTEN = OFF   // Clock Out Enable bit (CLKOUT function is disabled; I/O or oscillator function on OSC2)
#pragma config CSWEN = ON       // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config MCLRE = OFF      // Master Clear Enable bit (MCLR/VPP pin function is digital input; MCLR internally disabled; Weak pull-up under control of port pin's WPU control bit.)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config WDTE = OFF       // Watchdog Timer Enable bits (WDT enabled, SWDTEN is ignored)
#pragma config LPBOREN = OFF    // Low-power BOR enable bit (ULPBOR disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable bits (Brown-out Reset enabled, SBOREN bit ignored)
#pragma config BORV = LOW       // Brown-out Reset Voltage selection bit (Brown-out voltage (Vbor) set to 2.45V)
#pragma config PPS1WAY = ON     // PPSLOCK bit One-Way Set Enable bit (The PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a Reset)
#pragma config DEBUG = ON       // Debugger enable bit (Background debugger enabled)

// CONFIG3
#pragma config WRT = OFF        // User NVM self-write protection bits (Write protection off)
#pragma config LVP = OFF        // Low Voltage Programming Enable bit (HV on MCLR/VPP must be used for programming.)

// CONFIG4
#pragma config CP = OFF         // User NVM Program Memory Code Protection bit (User NVM code protection disabled)
#pragma config CPD = OFF        // Data NVM Memory Code Protection bit (Data NVM code protection disabled)

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

#include <xc.h>

#include <stdint.h>


// Do all pin selects
// 01 = VDD                 +5V
// 02 = RA5
// 03 = RA4
// 04 = RA3 / MCLR          Debug VPP
// 05 = RA2
// 06 = RA1 / ICSP CLK
// 07 = RA0 / ICSP DAT
// 08 = VSS                 0V


uint8_t pwmreg;        //address configuration
uint8_t stepcount;     //
uint8_t substep;       // PWM cycles per step increment; speed setting

uint8_t outimg;


// PWM range 0-x; a (power of 2) - 1 value
// Using 0 - 31
#define PWM_MAX     0x1f

// Brightness steps for a full rotation sequence of one light;
// eg. 4 or 8 times the number of different-brightness lights, -1
#define STEP_MAX    64

// Output phase, equal offsets through the "step" cycle.
#define LED_P0      0
#define LED_P1      16
#define LED_P2      32
#define LED_P3      48

// Look-up table for brightness sequence for one light
// Brightness range 0 = PWM_MAX

uint8_t  ltable[STEP_MAX] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 5, 7, 8, 9, 10, 12, 15, 17, 20, 22, 25, 28, 32,
32, 28, 25, 22, 20, 17, 15, 12, 10, 9, 8, 7, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};


// Function definition
uint8_t calc_pwm(uint8_t);



/*
*
*/
int main(int argc, char** argv) {
   
    int8_t ledout;
   
    outimg = 0;
    pwmreg = 0;
    stepcount = 0;
    substep = 0;
   
   
    // I/O Pin directions:
    // All out: TRIS = 0x0000 0000
    TRISA = 0x00;
   
    // OSC = 32 MHz.
    // Timer source = osc / 4; 8 MHz.
   
   
    for(;;)
    {
        CLRWDT();
       
        pwmreg = pwmreg + 1;
        pwmreg = pwmreg & PWM_MAX;
       
       
        // Do one LED
        // Get the on/off state and set the appropriate output pin
        // Outputs high for on; LED & resistor to 0V.
       
        ledout = calc_pwm(LED_P0);
       
        if(ledout == 1) {
            outimg = outimg | 0x04;
        }
        else {
            outimg = outimg & ~0x04;
        }
   
        // The same sequence for the next:
        ledout = calc_pwm(LED_P1);
       
        if(ledout == 1) {
            outimg = outimg | 0x08;
        }
        else {
            outimg = outimg & ~0x08;
        }
       
        // And again for each pin, passing the phase offset
        // then setting the appropriate pin
        ledout = calc_pwm(LED_P2);
       
        if(ledout == 1) {
            outimg = outimg | 0x10;
        }
        else {
            outimg = outimg & ~0x10;
        }


        ledout = calc_pwm(LED_P3);
       
        if(ledout == 1) {
            outimg = outimg | 0x20;
        }
        else {
            outimg = outimg & ~0x20;
        }

       
        PORTA = outimg;

        // At each 0 of the PWM reg, count the substep delay
        // and if that overflows, move to the next sequence step.

        if(pwmreg == 0) {
           
            //
            // Substep limit sets the overall cycle speed
            substep = substep + 1;
            if(substep > 18) {
           
                substep = 0;
               
                stepcount = stepcount + 1;
                                   
                if(stepcount >= STEP_MAX)
                {
                    stepcount = 0;
                }
            }
        }
       
    }

    return (1);
}


uint8_t calc_pwm (uint8_t lp) {
    uint8_t x, y;
   
    // Work out the cycle stage for the lamp, then get the table brightness
    // and compare to the PWM count to determine on/off
   
    x = stepcount + lp;
   
    // Wrap the result back to the start of the table;
    // if it is beyond the end.
   
    if(x >= STEP_MAX) {
        x = x - STEP_MAX;
    }
   
    // Get the brightness value from the table
    y = ltable[x];
   
    // Compare to present PWM value to determine on or off
   
    if(y > pwmreg) {
        return 1;
    }
    return 0;
}
 
Wow! I just tried it and it compiled! Thank you, thank you, thank you. When I get home, I'll try to program.

Also, I'm going to study more and use Nigel's tutorials before I ask for anything else. Things are starting to click, but I want to be able to understand more than I do. I feel horrible when asking questions. Thank you guys for helping.
 
Last edited:
If you're going to stick with C then Ian Rogers converted Nigel's tutorials to C. Link in his signature.

Mike.
 
I would like to have a working knowledge of both. I'm starting to understand the assembly language a bit. I'll look for that too.
 
Re. my program, a modification I was intending to do but never did..

Change the step table size to 60, rather than 64. That makes it suitable for 3, 4, 5 or 6 outputs using equal integer divisions, with no other changes.
Plus it's then an exact six degrees rotation per step so easier to transfer from intensity graphs.

And on that - also, adjusting the step brightness values to better real world ones rather than my rough guesses.
eg. The graphs below are actual angle vs intensity plot for a narrow beam light source and a wide beam source, which could be used to create better scaled sets of step intensities.

I don't know if you noticed, but as I now know what PICs you will be using, I set the clock to the full 32 MHz and the sub-step divider to 18, to give a reasonable speed.
It could easily take a 120 step table and/or double the PWM resolution, and more outputs, by using a lower divider; and still run at any speed you wished - it just depends how smooth a range of transitions you want.

If you do change it so it needs a low substep value, you can then make fine speed changes (slowing it down fractionally) by adding some dummy instructions or a few microseconds delay, immediately after the PORTA = outimg; line.

Just change things around as you like!


!Intensity_Curve.png




!light_intensity.jpg
 
I got through reading the asm tutorials and I'm on to the C ones. The asm made a lot more sense, mostly because it referenced what I was modifying in the original UFO chaser circuit. There were some things in the UFO circuit that the tutorial did not cover, but I'll get there.
 
Alright. I was finally able to write the code to the PIC 16F18313. It scared me initially as it took longer than expected. I was happy to see some LEDs lit, but it is performing like your video where only three (RA2, RA4 & RA5) are fading and none of the others are lit at all.
 
Alright. I was finally able to write the code to the PIC 16F18313. It scared me initially as it took longer than expected. I was happy to see some LEDs lit, but it is performing like your video where only three (RA2, RA4 & RA5) are fading and none of the others are lit at all.

According to:

// Do all pin selects
// 01 = VDD +5V
// 02 = RA5
// 03 = RA4
// 04 = RA3 / MCLR Debug VPP
// 05 = RA2
// 06 = RA1 / ICSP CLK
// 07 = RA0 / ICSP DAT
// 08 = VSS 0V

Only those three pins can be used, RA3 is usually input only (MCLR pin) and RA0 and RA1 are used for programming and debugging.

If you're not using debugging (I never do) RA0 and RA1 should be available if you unplug the PICKIT from the board.
 
At the top of his code it says:

#pragma config DEBUG = ON // Debugger enable bit (Background debugger enabled)

Try setting that to OFF instead.

However, having just looked at his code, it appears to be trying to use RA3 as an output? - this is usually input only. The code only uses RA2, RA3, RA4, and RA5.

Might be better to move to PortB and avoid RA3.
 
Yes, I did set the debug to off. Should I use a different uP?

No need to, MCLR is input only on most (all?) PIC's - either use PortB (I presume you have a PortB?, or move the RA3 LED to a different pin such as RA6 or RA7.

Change:

C:
// The same sequence for the next:
        ledout = calc_pwm(LED_P1);
      
        if(ledout == 1) {
            outimg = outimg | 0x08;
        }
        else {
            outimg = outimg & ~0x08;
        }

To:

C:
// The same sequence for the next:
        ledout = calc_pwm(LED_P1);
      
        if(ledout == 1) {
            outimg = outimg | 0x80;
        }
        else {
            outimg = outimg & ~0x80;
        }

To use RA7 instead of RA3.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top