|
|
View previous topic :: View next topic |
Author |
Message |
strsh
Joined: 16 Jul 2013 Posts: 37
|
About PWM and pause between |
Posted: Wed May 15, 2019 3:58 pm |
|
|
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: 21708
|
|
Posted: Wed May 15, 2019 10:24 pm |
|
|
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: 19520
|
|
Posted: Wed May 15, 2019 11:53 pm |
|
|
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: 37
|
|
Posted: Thu May 16, 2019 3:38 am |
|
|
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: 19520
|
|
Posted: Thu May 16, 2019 7:47 am |
|
|
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: 37
|
|
Posted: Fri May 17, 2019 2:26 am |
|
|
Thanks for the explanation. I used your code, but I wonder how I can extend the break time? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri May 17, 2019 7:00 am |
|
|
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: 37
|
|
Posted: Mon May 20, 2019 1:48 am |
|
|
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: 19520
|
|
Posted: Mon May 20, 2019 2:05 am |
|
|
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: 37
|
|
Posted: Mon May 20, 2019 4:40 am |
|
|
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: 9229 Location: Greensville,Ontario
|
|
Posted: Mon May 20, 2019 5:14 am |
|
|
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 |
|
|
|
|
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
|