![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
bdemir
Joined: 08 Jan 2009 Posts: 3
|
Multi PWM getting data problem |
Posted: Mon Apr 05, 2010 7:48 am |
|
|
Hi, I wrote a PWM code using PIC18F4520. I have two algorithms using timer interrupts. One algorithm produces synchronous multi PWM and the other non-synchronous.
My algorithm for non-synchronous multi PWM :
I am getting the angle value from the serial port and for that value I calculate the time that the PWM pin should be high. Then I set the timer interrupt for every 20 ms (50Hz). In every interrupt I am pulling the PWM pins high for the calculated time and then set them low. By this way I produce PWM signal around 50 Hz.
But I have problem while receiving the data from the serial rs232 port. For one RC servo my code works fine. I only get 1Byte data with no problem by using getc() command. On the other hand, for multi PWM I need to get at least 4 angle values (for 4 PWM). For that I am using "for loop" and put each byte (using getc()) in a array. But when I use "for loop" my timer interrupt is obtained in every 100 ms. Because while getting data I disable the timer interrupt and then enable it after getting the data. Do you have any suggestions about the code or getting data??
PWM producing algorithm may be improved, this method is very straight forward. But I think I should first find a solution to get a data from serial port.
My Code :
Code: |
#include <18F4520.h>
#fuses HS,NOWDT,NOMCLR,PROTECT,NOLVP,NODEBUG
#use delay(clock=20000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7)
#org 0x3F00,0x3FFF {}
#opt 9
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <input.c>
#define timer_value 65536
#byte PORTB=0xF81
#bit PWM1=PORTB.0
#bit PWM2=PORTB.1
#bit PWM3=PORTB.2
#bit PWM4=PORTB.3
void Get_Data();
int data[5];
int msj_ok;
int16 d1,d2,d3,d4;
int i;
int16 delay(int16 angle)
{
int16 d;
d= (angle*10)+1050;
return d;
}
#INT_TIMER1
void isr() {
set_timer1(15536);
if(msj_ok){
d1= delay(data[0]);
d2= delay(data[1]);
d3= delay(data[2]);
d4= delay(data[3]);
PWM1=1; // 1
delay_us(d1);
PWM1=0;
PWM2=1; // 2
delay_us(d2);
PWM2=0;
PWM3=1; // 3
delay_us(d3);
PWM3=0;
PWM4=1; // 4
delay_us(d4);
PWM4=0;
}
}
void main() {
setup_timer_1(T1_INTERNAL | T1_DIV_BY_2);
set_timer1(15536);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
set_tris_d(0xFB);
set_tris_b(0xF0);
output_d(0);
output_b(0);
msj_ok=0;
while(TRUE){
disable_interrupts(INT_TIMER1);
Get_Data();
enable_interrupts(INT_TIMER1);
}
}
void Get_Data(){
for(i=0;i<5;i++){
data[i] = getc();
if(data[4] == 254) msj_ok = 1; //Checking for stop bit, if stop bit is received then it will execute the interrupts command
}
}
|
_________________ Batu Demir |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19368
|
|
Posted: Mon Apr 05, 2010 9:31 am |
|
|
I'd suggest approaching it very differently.
First, don't use the timer1 interrupt, but just use the interrupt _flag_.
Have a main program loop, which tests this flag, and when it is set, clears it, and does your current pulse train, then carries on, and loops round. However as soon as you have finished your delays, and while it is waiting for the flag to trigger, it loops testing both this, and 'kbhit' (which is really the received character interrupt flag). If this goes true, is transfers the character into a buffer.
When the buffer reaches the required five characters, then your maths is performed, before looping back and once more waiting for the timer interrupt flag.
Slow your serial rate to 19200bps. This way, the worst case delay (4*255uSec), is potentially _just_ shorter than the total time for two characters (1.044mSec), allowing the internal hardware buffer to cope if characters arrive as the pulses are being sent.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Mon Apr 05, 2010 7:30 pm |
|
|
I've got a different different suggestion, for running 8 servos.
Calculate two time intervals for each servo you want to run. One will be the "on" time, nominally 1000usec to 2000usec, but it may be shorter or longer if you're operating outside the standard limits. The other interval would be the first one subtracted from 2500usec. Set one of the output pins high, and set timer 1 to interrupt at the end of the first interval. When the interrupt occurs, set the pin low and load the second interval to timer 1. When this triggers, you'll have run through a total of 2.5msec, or 1/8 of the total 20msec for a servo cycle, and it's time to start the next servo. Repeat this for each servo, then start again, at which point the full 20msec will have passed. It should be obvious how to make this work if you have fewer than 8 servos.
While the program is not servicing interrupts, make it sit in a loop monitoring the serial port. When a serial character comes in (RCIF tests high) do whatever the decoding process requires. It probably won't take long, but make sure that each character is dealt with before the next one arrives. You also mustn't try to change the parameters for any channel whose intervals are currently controlling the timer!
I think this would work. It's what I'd try to do, anyway. |
|
![](templates/subSilver/images/spacer.gif) |
bdemir
Joined: 08 Jan 2009 Posts: 3
|
|
Posted: Tue Apr 06, 2010 9:30 am |
|
|
Hi, thanks very much for your suggestions, I will try them both. But the thing that confuses me is that on the serial channel the data always flows and never stops. Because of that I cannot do the the other jobs.
On the other hand I have another idea . I want to use two pic. One of them will get the data from the serial rs232 and will separate this data into 4 different characters and send these 4 characters to second pic which will produce the PWM. The main reason in using two pic is that the order of incoming data is very important.
In the first pic I defined 5 different rs232 connections with different stream names. first connection gets all of the data from rs232 and the other 4 connections will send the characters from 4 different pins. Then in the second pic I defined 4 rs232 connections with different stream names and then produce the PWM. But I couldn't transfer the characters between two pics. Do you have any suggestions for that? Here is my codes:
PIC1 for RS232 data collection:
Code: |
#include <18F2550.h>
#DEVICE ADC=10
#fuses HS,NOWDT,NOMCLR,PROTECT,NOLVP,NODEBUG,NOBROWNOUT,PLL1,CPUDIV1,VREGEN
#use delay(clock=20000000)
#use rs232(baud=19200, xmit=PIN_A0,rcv=PIN_A1,stream=genel)
#use rs232(baud=19200, xmit=PIN_B0,rcv=PIN_B1,stream=servo1)
#use rs232(baud=19200, xmit=PIN_B2,rcv=PIN_B3,stream=servo2)
#use rs232(baud=19200, xmit=PIN_B4,rcv=PIN_B5,stream=servo3)
#use rs232(baud=19200, xmit=PIN_B6,rcv=PIN_B7,stream=servo4)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <input.c>
void Get_Data();
void Execute_Command();
int8 data[6];
int1 msj_ok;
void main(){
msj_ok=0;
while(true){
Get_Data();
if(msj_ok)
{
Execute_Command();
}
}
}
void Get_Data(){
int i;
for(i=0;i<6;i++){
data[i] = fgetc(genel);
if(data[0] != 251) //checking for start bit
break;
if(data[5] == 254) // checking for stop bit in order to have the data in right order
msj_ok = 1;
}
}
void Execute_Command(){
fputc(data[1],servo1);
fputc(data[2],servo2);
fputc(data[3],servo3);
fputc(data[4],servo4);
}
|
PIC2 for PWM:
Code: |
#include <18F4520.h>
#fuses HS,NOWDT,NOMCLR,PROTECT,NOLVP,NODEBUG
#use delay(clock=20000000)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)
#use rs232(baud=19200, xmit=PIN_C0, rcv=PIN_A0,stream=servo1)
#use rs232(baud=19200, xmit=PIN_C4, rcv=PIN_A1,stream=servo2)
#use rs232(baud=19200, xmit=PIN_C5, rcv=PIN_A4,stream=servo3)
#use rs232(baud=19200, xmit=PIN_A6, rcv=PIN_A5,stream=servo4)
#org 0x3F00,0x3FFF {}
#opt 9
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <input.c>
#define timer_value 65536
#byte PORTB=0xF81
#bit PWM1=PORTB.0
#bit PWM2=PORTB.1
#bit PWM3=PORTB.2
#bit PWM4=PORTB.3
int16 d1,d2,d3,d4;
int8 a1,a2,a3,a4;
int16 delay(int8 angle)
{
int16 d;
d= (angle*10)+1050;
return d;
}
#INT_TIMER1
void isr() {
set_timer1(15536);
d1= delay(a1);
d2= delay(a2);
d3= delay(a3);
d4= delay(a4);
PWM1=1; // 1
delay_us(d1);
PWM1=0;
PWM2=1; // 2
delay_us(d2);
PWM2=0;
PWM3=1; // 3
delay_us(d3);
PWM3=0;
PWM4=1; // 4
delay_us(d4);
PWM4=0;
}
void main() {
setup_timer_1(T1_INTERNAL | T1_DIV_BY_2);
set_timer1(15536);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
set_tris_d(0xFB);
set_tris_b(0xF0);
output_d(0);
output_b(0);
a1=40;
a2=40;
a3=40;
a4=40;
while(TRUE){
a1=fgetc(servo1);
a2=fgetc(servo2);
a3=fgetc(servo3);
a4=fgetc(servo4);
}
}
|
_________________ Batu Demir |
|
![](templates/subSilver/images/spacer.gif) |
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Tue Apr 06, 2010 3:44 pm |
|
|
I think adding a second processor will just multiply your problems. You don't need it.
It seems as though your math, in the routine delay(), forces the incoming data to be less than 145 (any more and you'd exceed 2500usec, and even that's an unlikely amount). This means that the start and stop characters can't be mistaken for data.
If that's the case, you can cure what I see as two severe problems in the program. First, you risk having the thing freeze if you ever lose a character or if the processor gets an extra one, because the start and stop characters have to be seen at the right times. Second, the fgetc() function will hold up operation until a character arrives.
If it were my project, I'd put a kbhit() call before each fgetc() and only try to read a character if there actually is one (though I always just check the RCIF flag instead). And I'd do something like this:
Code: |
void Get_Data(){
static int i;
int8 z;
if (!kbhit())
return; // No new char, get out
z = fgetc(gene1);
if (z == 251)
i = 0; // 251 always starts a packet
else if (z != 254)
if (i < 4) // Not the stop byte
data[i++] = z; // So store data, but don't overfill array
else if (i == 4) // It is the stop byte, but is it in the right place?
msj_ok = 1; // Yes!
}
|
No promises that that's totally right, but it's where I'd start. |
|
![](templates/subSilver/images/spacer.gif) |
bdemir
Joined: 08 Jan 2009 Posts: 3
|
|
Posted: Wed Apr 07, 2010 4:40 pm |
|
|
Thank you very much. Tomorrow morning my first job is to try your advice ![Smile](images/smiles/icon_smile.gif) _________________ Batu Demir |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|