View previous topic :: View next topic |
Author |
Message |
MJ Guest
|
Software UART on 18LF4550 receives, but does not send |
Posted: Tue Apr 04, 2006 10:25 am |
|
|
I'm baffled with this; hopefully someone can see an obvious error that I must be overlooking. I'm simply testing basic "echo back the input" serial comms before integrating into another design, but am stuck here...
Hardware: PDIP 18LF4550 connected through a MAX232 level shifter to a PC serial port. I'm set up to use software RS232 on D1 (Rx) and D2 (Tx) because I want the hardware SPI and the SDO pin is shared with the hardware UART.
Software: RealTerm serial monitor program
Results: Consistently, the PIC sends its 'header' information ('Running...' and 'Buffered data =>') that should come back ahead of the bytes that were sent; however, it never echoes back the bytes that I sent to it. It's as though the PIC is not receiving what I send to it through the PC--so either the bytes aren't getting to the PIC or the PIC is not processing them correctly (either in the circuit or in the embedded code)
I've changed the TRISD to be inputs and outputs accordingly (they're set to all inputs in the attached code, but I've tried all outputs and Rx=input/Tx=output); switched the Rx and Tx pins (in the code as well as hardware) with the same result mentioned above.
Serial.c code listing:
Code: | #include "Serial.h"
#define BUFFER_SIZE 32
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
#int_rda
void serial_isr() {
int t;
buffer[next_in]=getc();
t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out)
next_in=t; // Buffer full !!
}
#define bkbhit (next_in!=next_out)
BYTE bgetc() {
BYTE c;
while(!bkbhit) ;
c=buffer[next_out];
next_out=(next_out+1) % BUFFER_SIZE;
return(c);
}
void main() {
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_wdt(WDT_OFF);
setup_timer_0(RTCC_INTERNAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
setup_comparator(NC_NC_NC_NC);
setup_vref(VREF_LOW|-2);
setup_low_volt_detect(FALSE);
setup_oscillator(OSC_8MHZ|OSC_TIMER1|OSC_PLL_OFF);
// Set the ports to outputs
set_tris_a( 0x00 );
set_tris_b( 0x00 );
set_tris_c( 0x00 );
set_tris_d( 0xff );
enable_interrupts(global);
enable_interrupts(int_rda);
printf("\r\n\Running...\r\n");
// The program will delay for 10 seconds and then display
// any data that came in during the 10 second delay
do {
delay_ms(3000);
printf("\r\nBuffered data => ");
while(bkbhit)
putc( bgetc() );
} while (TRUE);
}
|
Serial.h code listing:
Code: |
#include <18F4550.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT //Code not protected from reading
#FUSES BROWNOUT_NOSL //Brownout enabled during operation, disabled during SLEEP
#FUSES BROWNOUT //Reset when brownout detected
#FUSES BORV20 //Brownout reset at 2.0V
#FUSES NOPUT //No Power Up Timer
#FUSES NOCPD //No EE protection
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NODEBUG //No Debug mode for ICD
#FUSES LVP //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES PBADEN //PORTB pins are configured as analog input channels on RESET
#FUSES NOWRTC //configuration not registers write protected
#FUSES NOWRTB //Boot block not write protected
#FUSES NOEBTR //Memory not protected from table reads
#FUSES NOEBTRB //Boot block not protected from table reads
#FUSES NOCPB //No Boot Block code protection
#FUSES MCLR //Master Clear pin enabled
#FUSES LPT1OSC //Timer1 configured for low-power operation
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#FUSES PLL1 //No PLL PreScaler
#use delay(clock=8000000)
#use rs232(baud=9600,parity=N,xmit=PIN_D2,rcv=PIN_D1,bits=8)
|
|
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Apr 04, 2006 10:51 am |
|
|
#int_rda is the hardware interrupt. You cannot use it with a software interrupt. I would probably bit bang the SPI instead of having to support a software uart. |
|
|
dmendesf
Joined: 31 Dec 2005 Posts: 32
|
|
Posted: Tue Apr 04, 2006 1:48 pm |
|
|
Really you should use the hardware serial and software spi... in SPI a slave never starts talking without being requested, and in serial communicatios this is pretty common. In order to kbhit() work in a software serial you must cal it like 10x the baud rate... I´ts difficult to write a program like that (say goodbye to a lot of delays...). Just my opinion. The other parent is right about the serial interrupt too: it only works with a hardware serial. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Apr 04, 2006 1:58 pm |
|
|
If you did want to create a receiver interrupt for a soft UART,
people normally use INT_EXT on Pin B0 for this. |
|
|
MJ Guest
|
Thanks very much |
Posted: Tue Apr 04, 2006 2:08 pm |
|
|
All are good points--hardware vs software interrupts, using hardware UART and bit-banging the SPI, and using an external interrupt for software UART. I wish I had posted this request before I made prototype PCBs that implement it as-shown above.. Argh. Live and learn I guess.
I guess I'm still a bit confused why it's not responding to the externally-transmitted data, though--is it not getting into the kbhit() buffer because I'm not using the hardware UART? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Apr 04, 2006 2:18 pm |
|
|
You're checking the bkbhit macro in your loop. Since you're not using
the hardware UART receiver pin, there will never be a receiver interrupt.
With no interrupt, the #int_rda isr is never called and therefore
next_in and next_out always stay equal, and bkbhit will always be false.
If you want to see it do something, use some code like this:
Code: |
char c;
while(1)
{
c = getc();
putc(c);
}
|
|
|
|
dmendesf
Joined: 31 Dec 2005 Posts: 32
|
|
Posted: Tue Apr 04, 2006 2:20 pm |
|
|
It's not working because the interrupt is never called... so you never call getc(). Try this: (I'm not promising that it will work, neither that it will scale up to a full program)
Code: |
#include "Serial.h"
#define BUFFER_SIZE 32
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
int t;
#define bkbhit (next_in!=next_out)
BYTE bgetc() {
BYTE c;
while(!bkbhit) ;
buffer[next_in]=getc();
t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out)
next_in=t; // Buffer full !!
c=buffer[next_out];
next_out=(next_out+1) % BUFFER_SIZE;
return(c);
}
void main() {
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_wdt(WDT_OFF);
setup_timer_0(RTCC_INTERNAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
setup_comparator(NC_NC_NC_NC);
setup_vref(VREF_LOW|-2);
setup_low_volt_detect(FALSE);
setup_oscillator(OSC_8MHZ|OSC_TIMER1|OSC_PLL_OFF);
// Set the ports to outputs
set_tris_a( 0x00 );
set_tris_b( 0x00 );
set_tris_c( 0x00 );
set_tris_d( 0xff );
enable_interrupts(global);
enable_interrupts(int_rda);
printf("\r\n\Running...\r\n");
// The program will delay for 10 seconds and then display
// any data that came in during the 10 second delay
do {
delay_ms(3000);
printf("\r\nBuffered data => ");
while(bkbhit)
putc( bgetc() );
} while (TRUE);
}
|
|
|
|
MJ Guest
|
|
Posted: Tue Apr 04, 2006 3:33 pm |
|
|
Ahh--that makes sense now; I'll give it a shot in a little bit and let you know. Thanks again! |
|
|
MJ Guest
|
|
Posted: Wed Apr 05, 2006 11:35 am |
|
|
Thanks to all for the help--here's what I've found out...
Interrupt: No interrupt is being called because I'm not using the hardware UART and I haven't designated anything else to call it (such as interrupt-on-change pin). Therefore, dmendesf's suggested code change did not work for this reason (but I do appreciate the input).
Polling: PCM programmer's code suggestion did work because it's constantly polling for any changes in the UART module, even though it's software-driven. If I changed nothing in the hardware setup, this is the route I'd have to go (polling continuously for changes). I guess an alternative would be to use another interrupt source such as PIN_B0.
I'm going to change my design to implement hardware UART and bit-banged serial primarily b/c my usage scenario has the UART always ready to receive (so I can expect things asynchronously), whereas the SPI controls an A/D chip. I control when the A/D chip performs its conversions, so had I recognized that difference in control schemes earlier I would have designed it differently.
Thanks for all the input,
Jeff |
|
|
dmendesf
Joined: 31 Dec 2005 Posts: 32
|
|
Posted: Wed Apr 05, 2006 6:27 pm |
|
|
MJ wrote: | Thanks to all for the help--here's what I've found out...
Interrupt: No interrupt is being called because I'm not using the hardware UART and I haven't designated anything else to call it (such as interrupt-on-change pin). Therefore, dmendesf's suggested code change did not work for this reason (but I do appreciate the input).
|
I've modified your code to NOT use interrupts, so if it's now working it's because your queue algorithm doesn't work (or I've introduced a bug in it, but I doubt that). Think again: where in that code an interrupt is being serviced? I'm only talking about this because probably you have misunderstood what the interrupts does, so your doubt persists... |
|
|
|