|
|
View previous topic :: View next topic |
Author |
Message |
jroth Guest
|
problems with high baud rates and #use rs232 |
Posted: Thu Jun 10, 2004 3:15 pm |
|
|
my program uses a timer1 interrupt to run PWM on a number of pins of a PIC 16F877. i listen over serial (#use rs232) to commands from a Rabbit RCM2200 microcontroller. the program works fine at 28.8k baud rate, but i am running into problems when i try to increase the baud rate to 57,600. i am clocking the PIC at 20 MHz.
i'm not sure how #use RS232 configures the USART, and perhaps the problem is linked to this ... maybe a buffer gets overloaded with too much incomming data.
another problem could be the interrupt frequency ... but i tried slowing this to 1/10 of the current speed, and it still can't handle 57.6k.
the program will run for a few seconds at 57.6k, but then crashes - it won't communicate over serial. i am sending data at the rate of 30 characters a second, so i'm surprised by the results.
here is the source:
Code: | #include <16f877.h>
#include <stdlib.h>
#FUSES HS,NOWDT,NOPROTECT,NOBROWNOUT,NOCPD,NOLVP //High Speed Osc, No code protect,
//No brownout protect, no something??, no low-voltage programming
#USE FAST_IO(A)
#USE FAST_IO(B)
#USE FAST_IO(C) //Use Fast IO means compiler assumes you've set tristate
#USE FAST_IO(D) //registers for the ports correctly before read/write ops
#USE FAST_IO(E)
#use delay (clock=20000000) //tells delay clock speed for calculating time
#use rs232(baud=57600, xmit=PIN_C6,rcv=PIN_C7, brgh1ok, errors) //serial setup stuff
#DEFINE NUM_LEDS 30 //really need a comment here, huh
int CONST LED_PINS [NUM_LEDS]= //LED Pin definition tablex
{
PIN_A0,PIN_A1,PIN_A2,
PIN_A3,PIN_A5,PIN_E0,
PIN_E1,PIN_E2,PIN_C0,
PIN_C1,PIN_C2,PIN_C3,
PIN_D0,PIN_D1,PIN_D2,
PIN_B5,PIN_B6,PIN_B7,
PIN_B2,PIN_B3,PIN_B4,
PIN_D7,PIN_B0,PIN_B1,
PIN_D4,PIN_D5,PIN_D6,
PIN_D3,PIN_C4,PIN_C5 //Test LED
};
signed int speed [NUM_LEDS]; //used for modulation test
signed int LED_VALS [NUM_LEDS]; //holds LED PWM values
signed int NEW_LED_VALS [NUM_LEDS]; //update *this* table, interrupt copies it over automatically
// OR ELSE you get bad boundary condition errors b/c of optimizations
int control,control2; //Variables to hold serial data
int frame=0; //Counts how many times PWM interrupt has occured
int loop,loop2; //Temporary loop variables
int1 toggle=TRUE; //Used for testing in previous versions
//NEW : changed the number of frames to 50
#INT_TIMER1
void do_pwm()
{
loop=0;
for (loop=0;loop<NUM_LEDS;loop++) //Loop through all 30 LEDS
{
if (LED_VALS[loop]==frame) //If an LEDS value==frame counter, turn it off
*(LED_PINS[loop]/8) &= (0xFF^(1<<(LED_PINS[loop]&7))); //Turns off LED[loop]
}
frame+=1; //Increment the frame counter
if (frame==100) //If we're at frame 100 restart the PWM cycle
{
frame=0; //Reset frame to 0
for(loop=0;loop<NUM_LEDS;++loop) //Turn LEDS on
{
LED_VALS[loop]=NEW_LED_VALS[loop]; //Update all LED Values from the new table
if(LED_VALS[loop]!=0) //Only turn on if LED value not equal 0
*(LED_PINS[loop]/8) |= (1<<(LED_PINS[loop]&7)); //Turn LED on
}
}
//NEW : slowing the interrupt cycle
set_timer1(64935); // 400 instrctions between interrupts
//set_timer1(65335); //When timer rolls over at 65535 interrupt will occur again
//so 65535-65335=200 instructions later int happens again
}
void main()
{
int16 a=0;
signed int temp;
SETUP_ADC(ADC_OFF); //Turn ADC off
SETUP_UART(TRUE); //Turn on hardware UART
SET_UART_SPEED(57600); //Set hardware UART speed
SET_TRIS_A(0); //Port A all outputs
SET_TRIS_B(0); //Port B all outputs
SET_TRIS_C(0x80); //Port C all outputs except for serial recieve pin
SET_TRIS_D(0); //Port D all outputs
SET_TRIS_E(0); //Port E all outpus
SETUP_TIMER_0(RTCC_INTERNAL); //Turn on Timer0 (8bit)
SETUP_TIMER_1(T1_INTERNAL); //Turn on Timer1 (16bit) used for PWM interrupt
ENABLE_INTERRUPTS(GLOBAL); //Enable interrupts
ENABLE_INTERRUPTS(INT_TIMER1); //Turn on Timer1 interrupt for PWM
srand(37); //seed random # generator (for modulation test)
{
LED_VALS[loop2]=(rand()/400)+10;
speed[loop2]=((rand()/3000)-5);
if (speed[loop2]==0) speed[loop2]=1;
}
// printf("Welcome to LED Testing\n\r");
putc('x'); //Send an 'x' over serial to let rabbit know i'm alive
while (TRUE) //Main Loop
{
a++;
if (kbhit()) //check if there is a character in the serial buffer
if(getch()=='s') //if an 's' is recieved we want to set an led value
{
// printf("Set LED "); //used for serial debugging
while (!kbhit()) ; //wait in loop for next character [note loop is empty]
control=getch(); //control stores which LED to modify
if ( (control>0) && (control<=NUM_LEDS)) //Is control in the right range?
{
// printf("%u to ",control);
while(!kbhit()) ; //Wait for the new value
{
control2=getch()-1; //Grab the new value
NEW_LED_VALS[control-1]=control2; //Put the new value in the NEW table,
//Which is updated every cycle (100 frame)
// printf("%u\n\r",control2); //debuggin stuff
putc('s'); //Send out an 's' as confirmation
}
}
}
}
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jun 10, 2004 8:40 pm |
|
|
Well, your isr is massively long. Maybe you can't help that.
But at 57600 baud, one character period is 173 us.
The hardware USART's receive fifo is 2-deep, plus the
incoming shift register.
I didn't figure out exactly how long your ISR takes,
I just glanced at it. But it's got to go through at least
one of those 30-iteration for-loops every interrupt.
The amount of ASM code is massive, including the
calls. It adds up.
I think you need to time it. Either do it empirically
with logic analyzer or scope (put in code to put out
a pulse at the start of the ISR, and another pulse at
the end), or add it up by hand. That analysis will
tell you if it's too long and is causing you to lose chars. |
|
|
Sergio
Joined: 16 Oct 2003 Posts: 11 Location: Arkansas, USA
|
|
Posted: Thu Jun 10, 2004 10:31 pm |
|
|
I am using a software interrupt at 115200 baud (20Mhz xtal) without any problems. I did discover that kbhit was too slow for those speeds. Instead I rolled up a simpler one that just keeps looking if the line at the pin went down. I am making the assumption that if it goes down then a character is being sent.
I also never recive more than two characters at a time hence a buffer of only 2. Which makes buffering simpler.
I need to simplify it but here's my code:
#int_ext
void serial2()
{
Display_LCD_Flag=1;
timeout=0;
timeout_error=FALSE;
#use RS232(baud=115200, xmit=PIN_B6, rcv=PIN_B0) //Amulet Software port
while(RCV_PIN&&(++timeout<765)); //This gives about 2ms timeout
if(!RCV_PIN)
serial_rcv[0]=getc();
else
timeout_error=TRUE;
timeout=0;
while(RCV_PIN&&(++timeout<765)); //This gives about 2ms timeout
if(!RCV_PIN)
serial_rcv[1]=getc();
else
timeout_error=TRUE;
menu=serial_rcv[1];
} _________________ Sergio |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Fri Jun 11, 2004 6:50 am |
|
|
As PCM Programmer already said: 57000 baud results in 173us per character.
At 20MHz this is equivalent to 865 instructions. Your interrupt routine should take no longer to execute than this.
A quick study of the assembly code of your interrupt handler showed you are using a minimum of 420 instructions in every interrupt. This is safe, but every 100th interrupt you are using at least 1320 instructions and with all leds on this can go up to at least 2300 instructions. This is too long!
The UART has a 2 bytes FIFO buffer, but your interrupt routine takes too long to execute and you will get an occasional overrun error. This error will be automatically cleared by the 'errors' parameter in your #use rs232 statement. Your protocol will be 'out of sync' and your program hangs.
The following suggestions:
1) Make your interrupt routine faster. Especially every 100th loop is a killer, maybe you can spread this over multiple calls?
2) Add a timeout to your protocol, so it can recover from errors.
3) Remove the setup_uart() and set_uart_speed() calls, you don't need them as #use rs232 is setting everything ok. You only need those functions if you want to change a setting during runtime.
Carlo |
|
|
jroth Guest
|
|
Posted: Fri Jun 11, 2004 1:59 pm |
|
|
Thanks.
I optimized the interrupt loop and have been able to get the program to run at 57600 baud. |
|
|
|
|
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
|