View previous topic :: View next topic |
Author |
Message |
stefan_d6
Joined: 17 Aug 2006 Posts: 6 Location: Varna, Bulgaria
|
RS232 transmits data incorrectly. Why? |
Posted: Thu Aug 09, 2007 5:25 am |
|
|
This subject is often discused here but I couldn't find solution to my problem.
I use the following code to get info from PIC18F1320 according to the sent command from PC. If the command word is 'nXXXX' - the PIC sends to the PC the timer0 counts. If the command word is 'd+[0000 to 1024]' - the PC sets the PWM duty cycle.
The problem is that the PC sends randomly commands like [d,,,ю] or something like that, i.e. the data is transmitted incorrectly. I checked the levels of the according pins of MAX232 - they are +8.5/-8.5 V - are these levels correct. Where to look for the problem?
Code: | #include <18F1320.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_B1, rcv=PIN_B4,ERRORS)
//-----------------------------------------------------
// constants and vars used for commport communications:
#define R_BUFFER_SIZE 32
byte r_buffer[R_BUFFER_SIZE];
byte r_next_in = 0;
byte r_next_out = 0;
#include <stdlib.h>
#INT_RDA
void r_serial_isr() {
int t;
r_buffer[r_next_in] = getc();
t = r_next_in;
r_next_in = (r_next_in + 1) % R_BUFFER_SIZE;
if(r_next_in == r_next_out)
r_next_in = t; // Buffer full !!
delay_us(10);
}
#define bkbhit (r_next_in != r_next_out)
BYTE bgetc() {
BYTE c;
while(!bkbhit) ;
c = r_buffer[r_next_out];
r_next_out = (r_next_out + 1) % R_BUFFER_SIZE;
putc(c);
return(c);
}
//-----------------------------------------------------
void main()
{
float rps,rpm;
long turns,duty;
char cmd;
char s[4];
printf("\r\n ==== Test program - 22.07.2007 ====\r\n");
setup_ccp1(CCP_PWM);
setup_timer_2(T2_DIV_BY_1, 255, 1); //The cycle time will be (1/clock)*4*t2div*(period+1)
// tcycle = (1/20000000)*4*1*256 = 51.2us -> 19.53125kHz
set_pwm1_duty(0); // duty = tcycle*clock/t2div
// 20% -> 10.24us x 20MHz = 204.8 ~205
// 50% -> 25.6 x 20 = 512
setup_timer_0(RTCC_EXT_L_TO_H);
enable_interrupts(int_rda);
enable_interrupts(global);
while(true)
{
cmd =bgetc();
s[0]=bgetc();
s[1]=bgetc();
s[2]=bgetc();
s[3]=bgetc();
switch(cmd){
case 'n':delay_ms(1000);
turns=get_timer0();
rps=(float)turns/36;
rpm=rps*60;
printf("%6.3f",rpm);
set_timer0(0);
break;
case 'd':duty=atol(s);
set_pwm1_duty(duty);
printf("\r\nduty = %Lu \r\n", duty);
break;
default :printf("\r\nError\r\n");
break;}
}
} |
|
|
|
inservi
Joined: 13 May 2007 Posts: 128
|
|
Posted: Thu Aug 09, 2007 6:00 am |
|
|
Hello,
I think it is a problem in bgetc(). you send a byte at the same time that bytes are probably received. The send is interupted by the #INT_RDA and the timing can not be good.
Code: | BYTE bgetc() {
BYTE c;
while(!bkbhit) ;
c = r_buffer[r_next_out];
r_next_out = (r_next_out + 1) % R_BUFFER_SIZE;
-->putc(c);
return(c);
} |
dro. _________________ in médio virtus |
|
|
SET
Joined: 15 Nov 2005 Posts: 161 Location: Glasgow, UK
|
|
Posted: Thu Aug 09, 2007 9:30 am |
|
|
Quote: | Hello,
I think it is a problem in bgetc(). you send a byte at the same time that bytes are probably received. The send is interupted by the #INT_RDA and the timing can not be good. |
No this should be Ok if the hardware UART is being used. |
|
|
SET
Joined: 15 Nov 2005 Posts: 161 Location: Glasgow, UK
|
|
Posted: Thu Aug 09, 2007 9:35 am |
|
|
Code: | #INT_RDA
void r_serial_isr() {
int t;
r_buffer[r_next_in] = getc();
t = r_next_in;
r_next_in = (r_next_in + 1) % R_BUFFER_SIZE;
if(r_next_in == r_next_out)
r_next_in = t; // Buffer full !!
delay_us(10);
} |
You dont need the 10us delay.
Is your PC adding CR or LF by any chance? Then your parser will get out of sync. |
|
|
Ttelmah Guest
|
|
Posted: Thu Aug 09, 2007 9:42 am |
|
|
First comment. Get rid of the delay in the RDA interrupt. This is stupid, not needed, and _will_ cause problems (since delays are used in the 'main', interrupts will be disabled dring these...).
Second comment. Consider buffering the serial output as well. EX_STISR shows how to do this. The problem is that some of your 'reply' messages are long (potentially 16 characters for the worst case on the duty cycle reply), and for the whole of this time, the code cannot process data from the incoming buffer. If the message rate is reasonably high, this could lead to a buffer overflow...
Best Wishes |
|
|
inservi
Joined: 13 May 2007 Posts: 128
|
|
Posted: Thu Aug 09, 2007 11:19 am |
|
|
Sorry, i not realized that the send use the hardware UART to.
dro. _________________ in médio virtus |
|
|
stefan_d6
Joined: 17 Aug 2006 Posts: 6 Location: Varna, Bulgaria
|
|
Posted: Wed Aug 22, 2007 6:26 am |
|
|
I finally solved the problem.
I just changed the baud rate to 19 200 and rs232 works without errors. However I didn't find out way rs232 didn't work correctly with 9600 baud rate. I also removed int_rda and instead I use kbhit() - I think my code allows such construction without any loss of data. Am I right?
Code: | #include <18F1320.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#use rs232(baud=19200, xmit=PIN_B1, rcv=PIN_B4,ERRORS)
#include <stdlib.h>
#include <input.c>
void main()
{
float rps,rpm;
long turns,duty;
char cmd,no;
setup_ccp1(CCP_PWM);
setup_timer_2(T2_DIV_BY_1, 255, 1); //The cycle time will be (1/clock)*4*t2div*(period+1)
// tcycle = (1/20000000)*4*1*256 = 51.2us -> 19.53125kHz
set_pwm1_duty(0); // duty = tcycle*clock/t2div
// 20% -> 10.24us x 20MHz = 204.8 ~205
// 50% -> 25.6 x 20 = 512
setup_timer_0(RTCC_EXT_L_TO_H);
printf("--- Test program ---");
while(true)
{
if(kbhit())
{
cmd =getc();
switch(cmd)
{
case 'n':set_timer0(0);
delay_ms(1000);
no=getc();
turns=get_timer0();
rps=(float)turns/36;
rpm=rps*60;
printf("\r\n%6.3f",rpm);
break;
case 'd':duty=get_long();
set_pwm1_duty(duty);
printf("\r\nduty = %Lu",duty);
break;
default :printf("\r\nError");
break;
}
}
}
} |
|
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Wed Aug 22, 2007 9:17 am |
|
|
You should take a long look at Ttelmahs advice. Very very often the only way to get absolutely reliable RS232 reception is with a very short interrupt service routine that feeds a circular buffer. There must be now be dozens of issues with RS232 almost all were solved with the circular buffer approach. It's not that you can't find another way it is just not likely to work as well as Ttelmahs advice. |
|
|
|