My PIC18F2525-based variable duty LFO Project

Started by mattthegamer463, May 04, 2011, 09:49:09 PM

Previous topic - Next topic

mattthegamer463

Hey guys,

I've undertaken a project to get me into PIC programming, microcontrollers and programming in general, since I've always avoided it like the plague.  After nearly failing a microcontrollers class this last semester I've put my foot downand decided to get some learnin' done. I thought I would post my work from the last few days, I've been designing and coding an LFO with variable uptime/downtime potentiometer controls.  I haven't prototyped but I have simulated it with code and it works great.  I'll be prototyping tomorrow after I code squarewave mode into it.

If anyone is wondering, I'm using the Proteus suite of design tools and CCS C for coding/compiling.  Proteus allows me to spice sim hardware and code within a PIC and a simultaneously, so that is my prototyping thus far.

The schematic:


The code:


#include "18f2525.h"
#use delay(internal)

long value, an1, an2, math1, math2;

//Matt Bechberger May 4th 2011
//Functioning LFO from 62Hz to 0.25Hz, triangle wave, variable uptime/downtime
//not to be saved over!

void main()
{

setup_adc( ADC_CLOCK_INTERNAL);  //set up the ADC to use internal clock speeds
setup_adc_ports( ALL_ANALOG); //set all ADC ports to analog inputs
set_adc_channel(0);  //switch adc to chan 0

value = 0;
an2 = an1 = 0;

upcount:

set_adc_channel(0);  //switch adc to chan 0
math2 = read_adc();  //read uptime value from chan 0

if(math2 == 0)
{math2 = 1;}

an2 = math2 * 30;


while (value<=255)
{
output_c(value);  // output count val to port C
value++;  // increment count value
delay_us(an2); //delay for uptime delay time
if(value == 255) // when it finishes goto down count
{goto downcount;} //jump to down counting code

}


downcount:

set_adc_channel(1);  //switch adc to chan 1
math1 = read_adc();  //read uptime value from chan 1

if(math1 == 0)
{math1 = 1;}

an1 = math1 * 30;

while(value<=255)
{
  output_c(value); //output value to port C
  value--; //decrement value
  delay_us(an1);  //delay for downtime delay time

  if (value == 0)
  {goto upcount;}
}

}


The result is a triangle output with perfectly variable uptime/downtime, and max and minimum frequencies of 62Hz and 0.25Hz respectively. I'll have to see how well it performs in real life.

Please don't critique it too hard, I've been doing PIC programming for about two days, and C for about a week.  Before that, I've scraped a couple languages in high school, and the aforementioned microcontrollers class was assembly.  It nearly killed me. 

One thing I am wondering though, does anyone know if there is a way to make a pseudo-sine wave out of a triangle/square wave?  3rd order filter on a square wave?  Waveshape tables?  Algorithm? I'd be interested what the community has come up as a novel way to do so.  In the case like this, where it is a control signal, purity of form is not required since it is not an audible signal, and the imperfections won't be noticeable.

Anyway, I'd be interested in hearing what anyone might have to say, and some discussion on techniques for accomplishing D/A, etc.

cloudscapes

I use wavetables for this sort of thing.

the advantage over algorithms is that you have the guarantee that a sine wave takes exactly as many cpu cycles as a square.
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

mattthegamer463

Quote from: cloudscapes on May 04, 2011, 11:13:49 PM
I use wavetables for this sort of thing.

the advantage over algorithms is that you have the guarantee that a sine wave takes exactly as many cpu cycles as a square.

Is that because the algorithm takes time to process?  Is there really a good way to estimate how many cycles you're using with a given section of code?  I know in assembly you can just look up what each operation takes in a table, but C is so much more removed from the final product that I can't imagine it's simple.  Unless the program can do it for you.

mattthegamer463

Been doing more work on this, and I didn't like the way it looked so I took a look at how tap-tempo tremolo units do it, and it made infinity more sense.  I re-tooled mine to match, copy-pasting the buffer portion of the circuit from them.



The PWM code is my design though.  Not sure if its the best way to do it, but it seems to work.  Need to prototype soon.

Code:

#include "18F2525.h"
#use delay(internal)

int uprate, downrate;
long value;


void main()
{

value = 0;

   setup_adc( ADC_CLOCK_INTERNAL);  //set up the ADC to use internal clock speeds
   setup_adc_ports( ALL_ANALOG); //set all ADC ports to analog inputs

   setup_timer_2(T2_div_by_16,0xFF,16); // pre-scaler,period,post-scaler, respectively
   setup_ccp1(ccp_pwm); // capture compair pwm on ccp1 (C1)



while(1)
{
//uptime
   set_adc_channel(0);  //switch adc to chan 0
   delay_us(10);
   uprate = read_adc();  //read uptime value from chan 0
//  uprate = uprate * 2;

   value = 0;  //reset value to 0 each waveform output period

   if(uprate == 0) //zero wrecks the math
      {uprate = 1;}

      while (value<=1023, value !=1023)
      {
      set_pwm1_duty(value);
      value++;  // increment count value
      delay_us(uprate); //delay for uptime delay time
      delay_us(uprate);
      delay_us(uprate);
      delay_us(uprate);
      }

//Downtime
   set_adc_channel(1);  //switch adc to chan 1
   delay_us(10);
   downrate = read_adc();  //read uptime value from chan 1
//  downrate = downrate * 2;

   if(downrate == 0) //zero wrecks the math
      {downrate = 1;}

   while(value<=1023, value!=0)
   {
     set_pwm1_duty(value);
     value--; //decrement value
     delay_us(downrate);  //delay for downtime delay time
     delay_us(downrate);
     delay_us(downrate);
     delay_us(downrate);
   }

}
}


potul

Quote from: mattthegamer463 on May 05, 2011, 07:13:52 AM
Is that because the algorithm takes time to process?  Is there really a good way to estimate how many cycles you're using with a given section of code?  I know in assembly you can just look up what each operation takes in a table, but C is so much more removed from the final product that I can't imagine it's simple.  Unless the program can do it for you.

The issue with altorithms, is that it takes different amount of cycles depending on the wave shape you use.... while if you use lookup tables, it always takes the same.
You can use MPLAB SIM to see how much time/cycles it takes. Just use the StopWatch function.