0
\$\begingroup\$

I am trying to program with the PIC18F4550 a PWM with which I can regulate the duty-cycle with a variable resistance.

I attach my code to explain the problem I have.

#pragma config CPUDIV = OSC1_PLL2
#pragma config USBDIV = 1 
#pragma config FOSC = HS        
#pragma config FCMEN = OFF      
#pragma config IESO = OFF   
#pragma config PWRT = OFF       
#pragma config BOR = OFF        
#pragma config BORV = 3         
#pragma config VREGEN = OFF  
#pragma config WDT = OFF        
#pragma config WDTPS = 32768    
#pragma config CCP2MX = ON      
#pragma config PBADEN = ON      
#pragma config LPT1OSC = OFF    
#pragma config MCLRE = ON       
#pragma config STVREN = ON      
#pragma config LVP = ON         
#pragma config ICPRT = OFF      
#pragma config XINST = OFF      
#pragma config CP0 = OFF        
#pragma config CP1 = OFF       
#pragma config CP2 = OFF        
#pragma config CP3 = OFF    
#pragma config CPB = OFF        
#pragma config CPD = OFF        
#pragma config WRT0 = OFF       
#pragma config WRT1 = OFF       
#pragma config WRT2 = OFF       
#pragma config WRT3 = OFF   
#pragma config WRTC = OFF       
#pragma config WRTB = OFF       
#pragma config WRTD = OFF       
#pragma config EBTR0 = OFF      
#pragma config EBTR1 = OFF      
#pragma config EBTR2 = OFF      
#pragma config EBTR3 = OFF     
#pragma config EBTRB = OFF

#include <xc.h>

#define _XTAL_FREQ 20000000L

void ADC_Initialize()
{
    ADCON1bits.PCFG = 0;
    ADCON1bits.VCFG = 0;
    ADCON0 = 0;
    ADCON2bits.ACQT = 3;
    ADCON2bits.ADCS = 5;
    ADCON2bits.ADFM = 1;
    ADCON0bits.ADON = 1;
}

unsigned int ADC_Read(unsigned char channel)
{
  ADCON0 &= 0x11000101;
  ADCON0 |= channel<<3;
  __delay_ms(2);
  GO_nDONE = 1;
  while(GO_nDONE);
  return ((ADRESH<<8)+ADRESL);
}

#define TMR2PRESCALE 16
long PWM_freq = 8000;

void PWM_Initialize()
{
    PR2 = 0x26;
    CCPR1L = 1;
    TRISCbits.RC1 = 0;
    TRISCbits.RC2 = 0;
    T2CON = 0x03;   
    CCP1CON = 0x0C;
    TMR2 = 0;
    T2CONbits.TMR2ON = 1; 
}

void PWM_Duty(unsigned int duty)
{
  if(duty<1023)
  {
    duty = ((float)duty/1023)*(_XTAL_FREQ/(PWM_freq*TMR2PRESCALE));

    CCPR1L = duty>>2;
  }
}

void main()
{
  int adc_value;  
  TRISC = 0; 
  TRISA = 1; 
  TRISD = 0; 
  ADC_Initialize(); 
  PWM_Initialize(); 

  do
  {
    adc_value = ADC_Read(4);
    PWM_Duty(adc_value);    
    __delay_ms(50);       
  }while(1);
}

If I execute the above code, what I have at the output of the PWM are 0v and a frequency 0. As shown in the following image

enter image description here

If in the fixed code the value of the PWM Duty (580), for example.

Then I stop running the analog input.

So it seems that the PWM signal is regulated and I have a voltage at the output.

As I show in the following image.

enter image description here

What I do not understand is that in neither of the 2 cases the frequency is 0, when it should be approximately 8000Hz.

On the other hand I see that if I set the duty_cycle it seems that it regulates and gives voltage to the output.

So there must be a fault in the part of the analog input that I have not been able to find.

Finally, I leave a picture of the circuit.

enter image description here

\$\endgroup\$
6
  • 1
    \$\begingroup\$ PR = 0x26 that don't make any sense for me. Another case: Why the cast to float? \$\endgroup\$
    – Mike
    Apr 3 '20 at 9:44
  • \$\begingroup\$ Is the result of the calculation: PWM periodo = (PR2 + 1)*4*Tosc*Prescaler -> 1/8KHz = (PR2 + 1)*4*(1/20MHZ)*16 -> PR2 + 1 = 39.06 -> PR2 = 38.06 = 38 -> 26 hex -> PR = 0x26 \$\endgroup\$
    – Geo
    Apr 3 '20 at 9:49
  • \$\begingroup\$ I have tried with the parameter PR2 = 0x3FF and the same thing happens \$\endgroup\$
    – Geo
    Apr 3 '20 at 20:25
  • 1
    \$\begingroup\$ duty = ((float)duty/1023)*(_XTAL_FREQ/(PWM_freq*TMR2PRESCALE)); This part is also wrong. Just CCPR1L = duty>>2;.Have a look into the datasheet. \$\endgroup\$
    – Mike
    Apr 6 '20 at 5:14
  • \$\begingroup\$ Thanks for the help. I have already got it working. Apart from the errors mentioned above I also found this ADC_Read (0). \$\endgroup\$
    – Geo
    Apr 6 '20 at 18:36
0
\$\begingroup\$

With the following code I could make it work

#define TMR2PRESCALE 16
long PWM_freq = 8000;

void PWM_Initialize() 
{
   PR2 = ("%#x", (_XTAL_FREQ/(PWM_freq*4*TMR2PRESCALE)) - 1); //Setting the PR2 formulae using Datasheet 
   CCPR1L = 0x1F;
   TRISCbits.RC1 = 0; 
   TRISCbits.RC2 = 0;
   T2CON = 0x03;   
   CCP1CON = 0x0C;
   TMR2 = 0;
   T2CONbits.TMR2ON = 1; 
}

void PWM_Duty(unsigned int duty)
{
  if(duty < 1023)
  {
      duty = ((float)duty/1023)*(_XTAL_FREQ/(PWM_freq*TMR2PRESCALE));
      CCPR1L = duty>>2;
  }
}

void ADC_Initialize()
{    
   ADCON1bits.PCFG = 0;
   ADCON1bits.VCFG = 0;
   ADCON0 = 0;
   ADCON2bits.ACQT = 3;
   ADCON2bits.ADCS = 5;
   ADCON2bits.ADFM = 1;
   ADCON0bits.ADON = 1;
}

unsigned int ADC_Read(unsigned char ch)
{
   if(ch > 13)return 0;
   ADCON0 = 0;
   ADCON0 = (ch<<2);
   ADCON0bits.ADON = 1;
   ADCON0bits.GO_DONE = 1;
   while(ADCON0bits.GO_DONE == 1);
   ADCON0bits.ADON = 0;
   return ADRES;
}

void main()
{    
   int adc_value;  
   TRISC = 0;
   TRISA = 1;
   TRISD = 0; 
   ADC_Initialize();
   PWM_Initialize();

   do
   {
       adc_value = ADC_Read(0);
       PWM_Duty(adc_value);    
       __delay_ms(50);       
   }
   while(1); //Infinite Loop  
}
\$\endgroup\$

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.