CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug Reports on this forum. Send them to support@ccsinfo.com

About PWM and pause between

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
strsh



Joined: 16 Jul 2013
Posts: 20

View user's profile Send private message

About PWM and pause between
PostPosted: Wed May 15, 2019 3:58 pm     Reply with quote

Greeting.
I am trying to do the following process:
1. Turn on PWM to run at 23KHz for 1 second
2. Turn off PWM for 1 second
3. Turn on PWM to run at 23KHz for 1 second
4. Turn off PWM for 1 second
and so on...

All this is fine if it is in the main loop, but I need to run this process in the background while the controller needs to process other data (responds to the keys, monitors the heat sensor-ds18b20, prints the data on the display ...)

It's not a problem for me to do PWM to work in the background, but I'm not able to make a break of 1 second in the background.
The MCU is 16f690.

Thanks in advance.
PCM programmer



Joined: 06 Sep 2003
Posts: 20603

View user's profile Send private message

PostPosted: Wed May 15, 2019 10:24 pm     Reply with quote

strsh wrote:

It's not a problem for me to do PWM to work in the background, but I'm
not able to make a break of 1 second in the background.

1. Count PWM cycles in the #int_timer2 interrupt. When you have done
the required number of PWM cycles, disable PWM.
2. At the same time, start counting #int_timer2 cycles, until you reach
one second. Then start the PWM again.
Ttelmah



Joined: 11 Mar 2010
Posts: 14321

View user's profile Send private message

PostPosted: Wed May 15, 2019 11:53 pm     Reply with quote

Remember too, that you can stop the PWM output, by just setting the duty
cycle to 0 (or one greater than your timer2 value*4, to leave it 'on'), you
don't have to actually 'stop' the PWM itself. Also Timer2, supports an
interrupt every n cycles (n=1 to 16), so even at 23KHz, the interrupt rate
can be kept down to just 1.4KHz. So, don't 'stop the PWM', just stop the
output, and (using/16) 1 second will be a count of 1437 in the timer interrupt.
As a demo of this (using the 8MHz internal clock):
Code:

#include <16F690.h>
#device ADC=10
#use delay(internal=8000000)

#define COUNTS_PER_SECOND (1437) //interrupt ticks for one second
#define PWM_HALF (174L) //PWM value for 50% duty
#define PWM_STOP (0L) //set to 348 if you want PWM high when stopped
#INT_TIMER2
void tick(void)
{
   static int1 toggle=FALSE;
   static int16 ctr=COUNTS_PER_SECOND;
   
   if (--ctr==0)
   {
      toggle=!toggle;
      if (toggle)
         set_pwm1_duty(PWM_HALF); //50% duty
      else
         set_pwm1_duty(PWM_STOP); //off
      ctr=COUNTS_PER_SECOND; //reload counter
   }
}

void main()
{
   setup_timer_2(T2_DIV_BY_1, 86, 16); //setup timer2 to run at 23KHz & int at 1.4K
   setup_ccp1(CCP_PWM);
   set_pwm1_duty(0); //disable output
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_TIMER2);
   
   while(TRUE)
   {
      //User Code
      //PWM will merrily be doing 1 second running, one second stopped
      delay_ms(10); //just do something.
   }
}
strsh



Joined: 16 Jul 2013
Posts: 20

View user's profile Send private message

PostPosted: Thu May 16, 2019 3:38 am     Reply with quote

Thank you for the answer. I checked the code with an oscilloscope. The values are correct.
I tried this (experimental method):
Code:

#include <16F690.h>

#FUSES HS
#USE delay(clock=20MHz)
#use fast_io(B)

long long i,time,timeToPulse=100*23.14,timeToPause=100*95;
#INT_TIMER2
void timer2_isr(void)
{
   clear_interrupt(INT_TIMER2);
   output_toggle(PIN_C5);
   i++;
   time= timeToPause+timeToPulse;
   while (i >= timeToPulse && i <= time)
   {
      output_low(PIN_C5);
      i++;
   }
   if (i > time) i=0;
}
void main()
{
   setup_timer_2 (T2_DIV_BY_1, 0x6b, 1 );   // 23.15KHz
   enable_interrupts(INT_TIMER2);
   enable_interrupts(GLOBAL);
   output_low(PIN_C5);
 
   while(TRUE) ;
}


but I do not have the exact values.

Once again, thank you.
Ttelmah



Joined: 11 Mar 2010
Posts: 14321

View user's profile Send private message

PostPosted: Thu May 16, 2019 7:47 am     Reply with quote

23KHz, from a 20MHz master clock is going to need:

(20000000/4)/23000 = 217 counts.

So a value of 216 for the timer2 setup.

Then don't try interrupting on every timer cycle. You are trying to interrupt
every half cycle, so 46KHz. This implies an interrupt every 108 processor
cycles. It takes about _60_ cycles just to handle the interrupt without any
code. Trying to interrupt this fast is just going to result in running out
of time, especially handling maths in the interrupt as well.
This is just too fast for the interrupt. You will be spending most of your
processor time handling the interrupt. Use the /16 as I do, and the PWM.
Doing the pulse 'manually', is wasting too much processor time.

Using the /16, your timer interrupt will then be at the same 23000/16
interval as I use.
strsh



Joined: 16 Jul 2013
Posts: 20

View user's profile Send private message

PostPosted: Fri May 17, 2019 2:26 am     Reply with quote

Thanks for the explanation. I used your code, but I wonder how I can extend the break time?
Ttelmah



Joined: 11 Mar 2010
Posts: 14321

View user's profile Send private message

PostPosted: Fri May 17, 2019 7:00 am     Reply with quote

You said you wanted 1sec and 1sec.

To do different times, use:
Code:

#INT_TIMER2
void tick(void)
{
   static int1 toggle=FALSE;
   static int16 ctr=COUNTS_PER_SECOND;
   
   if (--ctr==0)
   {
      toggle=!toggle;
      if (toggle)
      {
         set_pwm1_duty(PWM_HALF); //50% duty
         ctr=ON_COUNTS;
      }
      else
      {
         set_pwm1_duty(PWM_STOP); //off
         ctr=OFF_COUNTS; //reload counter
      }
   }
}


Then just choose suitable values for the OFF and ON count numbers.
strsh



Joined: 16 Jul 2013
Posts: 20

View user's profile Send private message

PostPosted: Mon May 20, 2019 1:48 am     Reply with quote

Thank you. I asked, but I find it interesting how I can change the time between two 23KHz packages.

I'll ask one more question, and if you do not want to answer, I'll understand.

Since I'm watching the temperature with the DS1820, I noticed that it takes a lot of time to process his code, which is a problem, because the keys (switching on and off) do not respond quickly.

Is there a faster way to process this information?

The code is as follows:
Code:

#include <16F690.h>
//#device ADC=10
#use delay(internal=8MHz)

#define LCD_RS_PIN PIN_C0
#define LCD_RW_PIN PIN_A0
#define LCD_ENABLE_PIN PIN_C1
#define LCD_DATA4 PIN_C2
#define LCD_DATA5 PIN_C3
#define LCD_DATA6 PIN_C4
#define LCD_DATA7 PIN_C7

#define COUNTS_PER_SECOND (1400) //1473interrupt ticks for one second
#define PWM_HALF (174L) //PWM value for 50% duty
#define PWM_STOP (348) //set to 348/0L if you want PWM high/low when stopped

#include<1wire.c>
#include <lcd.c>
#include<ds1820.c>

float temperature=0;
short display_OnOff = 0;
long ON_COUNTS = 1;
long OFF_COUNTS = 1;

#INT_RB
void interrupt()
{
   switch (input_b()){
      case 0xe0:
    {
       setup_ccp1(CCP_OFF);
       output_bit( PIN_C5, 1);
       display_OnOff = 0;
       printf(lcd_putc,"\fstop");
       break;
    }
      case 0xb0:
    {
       setup_ccp1(CCP_PWM);
       lcd_gotoxy(1, 1);
       printf(lcd_putc,"start");
       display_OnOff = 1;
    }
   }
}

#INT_TIMER2
void tick(void)
{
   static int1 toggle=FALSE;
   static int16 ctr=COUNTS_PER_SECOND;
   
   if (--ctr==0)
   {
      toggle=!toggle;
      if (toggle)
    {
         set_pwm1_duty(PWM_HALF); //50% duty
    ctr=ON_COUNTS;
    }
      else
    {
         set_pwm1_duty(PWM_STOP);
    ctr=OFF_COUNTS;
    }
       
      if (ctr==OFF_COUNTS && display_OnOff == 1)
    {
       temperature = ds1820_read();
       lcd_gotoxy(12, 2);
       printf(lcd_putc,"%2.0f",temperature/10);
       lcd_putc(223);
       lcd_putc("C");
    }
   }
}

void main()
{   
   lcd_init();
   port_b_pullups(0b11110000);
   setup_timer_2(T2_DIV_BY_1, 86, 16); //setup timer2 to run at 23KHz & int at 1.4K
   //setup_ccp1(CCP_PWM);
   set_pwm1_duty(0);
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_TIMER2);
   enable_interrupts(INT_RB4|INT_RB5|INT_RB6|INT_RB7);
   output_bit( PIN_C5, 1);
   while(TRUE);
}
Ttelmah



Joined: 11 Mar 2010
Posts: 14321

View user's profile Send private message

PostPosted: Mon May 20, 2019 2:05 am     Reply with quote

Quote:

Thank you. I asked, but I find it interesting how I can change the time between two 23KHz packages.


I have answered this.

In my last post, just set 'ON_COUNTS' to how long you want the pulse on for,
and 'OFF_COUNTS' for how long you want it to pause.

Now seriously, your issue is trying to do too much inside the interrupt.
Repeat ten times.
Interrupt handlers should always be as short/quick as possible.

Never. Do things like floating point arithmetic, or printing inside
an interrupt handler.

You main code should be doing this. If you want the sampling to be
'synchronous' to the interrupt event, then set a flag inside the interrupt
handler and do this when the flag sets (and then clear the flag of course).
strsh



Joined: 16 Jul 2013
Posts: 20

View user's profile Send private message

PostPosted: Mon May 20, 2019 4:40 am     Reply with quote

OK.

I looked at the code for 1wire.c and I see that it takes 1 second to reset. There is a problem that the keys do not respond to me.

I'll try to modify the code to avoid this reset time.

But if anyone has any idea how to overcome this to save my time...
temtronic



Joined: 01 Jul 2010
Posts: 6664
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Mon May 20, 2019 5:14 am     Reply with quote

What I do for my remote greenhouse monitoring is rather easy.
I have an RTC module configured to send an interrupt to the PIC at 1Hz. You can use the 'software RTC' from the code library, I've used it too. The key is to have a 1Hz interrupt.
The ONLY code inside the RTC ISR is simply setting a 'flag( flag1Hz)' to say an interrupt has occoured.
In MAIN() the PIC checks to see IF the flag 'flag1Hz' is set.
If set... I reset the 'flag1Hz', read the DS18B20 sensor data, command it to take another reading, update the LCD, check config switches,control the watering system, send info to PC,and 'other stuff'.
The temp. sensor data is actually the PREVIOUS reading from 1 second ago NOT the current one so the PIC doesn't have to wait .9 seconds for the sensor to work. This method has worked very well for years. It's unlikely that temperature will dramatically change in one second. If it does, we ALL have a bigger problem !!

If you want fast KPD reaction, simply code an ISR for the KPD. There's code in the library for that,or this forum. I know it works as I've modified and added to my 'library' of functions.

Jay
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group