|
|
View previous topic :: View next topic |
Author |
Message |
John Morley
Joined: 09 Aug 2004 Posts: 97
|
Soft UART and Int_Ext |
Posted: Wed Sep 13, 2006 11:11 am |
|
|
Hi All,
I need to add a second serial port to a project that uses the PIC16F73. I can't get this to work using the Int_EXT interrupt and pin B0 as my receive data input. I have an interrupt service routine for buffering incoming serial data that works quite well with Int_RDA, so I've modified it a bit but it does not work with Int_EXT.
Here is the ISR:
Code: |
#INT_EXT // Interrupt driven RS232 routine
void rs232_isr(void)
{
char temp; // local data storage
//char string[20];
//fgets(string, Aerocomm);
//fprintf(Console, "\n\r%s", string);
//return;
temp = fgetc(Aerocomm); // get rx data
// If we got a '#', then the command is beginning.
if (temp == '#')
{
Serial_Index = 0;
RxBuffer[Serial_Index] = temp;
Serial_Index++;
return;
}
// If we got a CR, then the command is completed.
if (temp == CR)
{
RxBuffer[Serial_Index] = temp;
RX_Command_Ready = TRUE;
return;
}
// Save the character to the receive buffer.
RxBuffer[Serial_Index]=temp;
// Check for buffer overflow.
if ( Serial_Index >= (RX_SIZE - 1) )
Serial_Index = 0;
else
Serial_Index++;
}
|
To test this routine I'm sending this string: #N04:08:00<CR>
Note the commented lines at the top of the ISR. If I use fgets() to read the incoming data, it reads the serial string 100% of the time. I don't want to do this however, in case the string is ever corrupted, etc., and would like to collect the data character by character.
Here are the fuses, delays and serial port setups:
Code: |
#include <16f73.h>
#fuses HS, NOWDT, NOPROTECT
//-----<Compiler>-----
// Tell compiler clock speed is 20.00 MHZ Crystal
#use delay(clock=20000000)
//-----<General>-----
#define RS232_OUT PIN_A4 // RS232 Output to Terminal
#define Aerocomm_Tx PIN_B4 // TTL serial data to Aerocomm module
#define Aerocomm_Rx PIN_B0 // TTL serial data from Aerocomm module
#define CR 0x0d // ASCII Value for a Carriage Return
//-----<Serial>-----
#use rs232(baud=9600, xmit=RS232_Out, stream = Console, Restart_WDT)
#use rs232(baud=9600, xmit=Aerocomm_Tx, rcv=Aerocomm_Rx, stream = Aerocomm, Restart_WDT)
//-----<RS232>-----
int RX_Command_Ready; // TRUE If a receive packet is ready
#define RX_SIZE 20 // RS232 buffer for serial reception
char RxBuffer[RX_SIZE]; // RS232 serial RX buffer
int Serial_Index = 0; // RS232 RX data IN index
|
Here is the code that I use to set up the External Interrupt:
Code: |
// Here we configure and enable the external interrupt for the software UART.
setup_adc_ports( NO_ANALOGS );
ext_int_edge(h_to_l);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
|
A couple of quick calculations seems to indicate that this should work with a 9600 baud rate and a 20 MHz clock with sufficient machine cycles to read the incoming data.
Any ideas? _________________ John Morley |
|
|
mmprestine
Joined: 13 Jan 2004 Posts: 29 Location: Green Bay, Wisconsin
|
|
Posted: Wed Sep 13, 2006 1:32 pm |
|
|
You may need to add the invert option to the #USE RS232 statement.
Matt |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Wed Sep 13, 2006 7:16 pm |
|
|
You did not say what was actually wrong. Are you receiving garbage characters?
Are both ports meant to be receiving concurrently? - If so as a minimum you will have to ensure the ext int has higher priority or the bit timing will be screwed up.
You can optimize the ext_int handler. You do not need the tempory character.
Code: | #INT_EXT // Interrupt driven RS232 routine
void rs232_isr(void)
{
RxBuffer[Serial_Index] = fgetc(Aerocomm); // get rx data
// If we got a '#', then the command is beginning.
switch(RxBuffer[Serial_Index])
{
case '#' :
RxBuffer[0] = RxBuffer[Serial_Index];
Serial_Index = 1;
return;
case CR : // If we got a CR, then the command is completed.
RX_Command_Ready = TRUE;
return;
default: // update the buffer pointer
Serial_Index = (Serial_Index + 1) % RX_SIZE
}
} |
_________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
Ttelmah Guest
|
|
Posted: Thu Sep 14, 2006 7:09 am |
|
|
Actually though, the version with the extra temporary store, will be faster.
Accessing an array variable, involves reading the index, adding this to the origin of the array, putting the resulting 16bit value, into the table address registers, then accessing the value through the indirect addressing register on the PIC. Typially about 10 instructions. Accessing an integer at a fixed location in memory, involves just one instruction.
Raising the priority of INT_RDA, will only help this 'pre-empt' the other interrupt. If the code is already in the INT_RDA handler, the timing will still go to pot.
It might be worth trying the option 'SAMPLE_EARLY' on the software serial setup. The problem is that the software serial, when 'fgetc' is called, checks to see if the signal is low (or high with INVERT), and then waits half a bit time, before reading the bit. It then repeats at bit time intervals, to get all the bits. Now with the interrupt based handler, when fgetc is called, the signal has already been in this low (or high) state, for perhaps aout 30+ instruction times, and the sampling is then 'late' all the way through the character. Sample early, makes the routine start reading immediately.
You also don't show the setup line for the external interrupt. The interrupt needs to be set to interrupt on the falling edge of the start bit (int_h_to_l), for normal serial (or the rising edge with inverted serial).
Best Wishes |
|
|
John Morley
Joined: 09 Aug 2004 Posts: 97
|
|
Posted: Thu Sep 14, 2006 9:22 am |
|
|
Hi,
Thanks for the suggestions Asmallri and Ttelmah!
The problem is that the complete string is never received. I sit in a While loop inside Main looking for Rx_Command_Ready to be true, but it never happens. I've been reluctant to put much extra code inside the ISR for fear of adding too many extra machine cycles in case timing is critical. When I add this code to the ISR:
Code: |
fprintf(Console, "%s\n\r", RXBuffer);
|
The first character of my string ('#') is received 100% of the time, but nothing else is received, or the ISR doesn't fire again for the additional characters.
I am setting the interrupt edge for (h_to_l), and the interrupt IS firing as expected (at least once per string transmission).
My compiler version (v3.162) apparently does not support the 'SAMPLE_EARLY' option. I wonder when that was added?
This problem doesn't have anything to do with interrupt priority as I have stripped the INT_RDA ISR out of my code during my troubleshooting.
If this is a timing related issue, I do have the option of reducing the baud rate of the incoming data. _________________ John Morley |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Thu Sep 14, 2006 9:45 am |
|
|
Hi John,
What other interrupts do you have enabled in the application? _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
John Morley
Joined: 09 Aug 2004 Posts: 97
|
|
Posted: Thu Sep 14, 2006 10:41 am |
|
|
Hi Andrew,
Actully, no other interrupts. I've reduced my code to a while(1) loop inside of Main checking for the RX_Command_Ready to become True, and the ISR itself. Still, no luck. The ISR is firing, and I get the first character, but nothing else.
I guess I'll try other baud rates and see if that is a factor? _________________ John Morley |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Thu Sep 14, 2006 5:58 pm |
|
|
Do you have a LED you could toggle for testing? If so inside the ISR toggle it each time on entry. You should see it toggle each time a character arrives. This will narrow the probem down for you. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
Ttelmah Guest
|
|
Posted: Fri Sep 15, 2006 2:25 am |
|
|
How are the characters being sent?.
You are printing a _string_ from the printf, which requires the character buffer to be null terminated. Unless this is true, the print, will carry on printing other things, and may take ages to do so. Also, even if the buffer is properly terminated, you are printing three characters minimum (the line feed/carriage return, plus the single character present from the first receive). This will delay the code for one character time, meaning that a second character will now be missed (there is only 1.9999 characters of buffering in the transmit hardware). So even if the code was working OK without the printf, it now won't be...
Toggle the pin as Asmallri suggests. This only takes a few uSec, and will avoid introducing timing problems.
Your compiler is very old. I didn't really feal that it worked properly for the '18' chips, till the mid 200's, however for the 16 chips it generally did get going OK earlier. Sample early is probably about 50 versions latter.
Best Wishes |
|
|
John Morley
Joined: 09 Aug 2004 Posts: 97
|
|
Posted: Fri Sep 15, 2006 8:53 pm |
|
|
HI All,
OK, I've added a couple of lines of code inside my receive ISR to toggle an LED. It blinks sporadically, but only when data is being sent to the receiver. Based on this I'm pretty sure that the ISR is being triggered at least once for each time the transmitted data is received.
I am sending data from a second PIC using this code:
Code: |
while(1)
{
Output_high(Relay1_Out);
fprintf(Aerocomm, "#N04:08:00\r");
delay_ms(1000);
Output_low(Relay1_Out);
delay_ms(9000);
}
|
As you can see it sends a string terminated with a <CR> every 10 seconds. The transmit code also blinks an LED so I know when data is being sent.
In my receive program I added a printf inside of the While(1) loop in Main that prints the contents of the RxBuffer every couple of seconds. It looks like the first couple of characters in my string are being received correctly some of the time, followed by garbage, and other times it's just 100% garbage.
At this point it appears like some characters at the beginning of the receive string are being received some of the time, but never the whole string including the <CR> terminator. The most correct characters I've seen is 5, but most often it is more like 2 or 3.
I'm not sure if this might be a compiler problem or not, but regardless I'm going to call CCS on Monday and upgrade to the last v3.XXX version of PCM.
Any other thoughts guys?? _________________ John Morley |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Sep 15, 2006 9:21 pm |
|
|
Quote: | #define RS232_OUT PIN_A4 // RS232 Output to Terminal
#define Aerocomm_Tx PIN_B4 // TTL serial data to Aerocomm
#define Aerocomm_Rx PIN_B0 // TTL serial data from Aerocomm
#use rs232(baud=9600, xmit=RS232_Out, stream = Console, Restart_WDT)
#use rs232(baud=9600, xmit=Aerocomm_Tx, rcv=Aerocomm_Rx, stream = Aerocomm, Restart_WDT) |
Why do you have to run two soft UARTs ? The whole problem with
a soft UART is that the processor must "camp on" the UART pin while
it's receiving or transmitting a byte. It can't be interrupted, or in
the case of receiving, it can't be delayed from gaining access to the
pin, or you might lose the byte.
You have a hardware UART in the 16F73 on pins C6 and C7. Use it. |
|
|
John Morley
Joined: 09 Aug 2004 Posts: 97
|
|
Posted: Fri Sep 15, 2006 9:56 pm |
|
|
Hi PCM,
Actually, in the final implementation of my system, wireless data will be received by one software UART using the B0 pin for reception, and then retransmitted over an RS485 network using the hardware UART on C6 and C7. The other software UART that is being shown now is only being used for diagnostics. At the moment, everything is stripped from my program except the wireless reception module which I am now trying to get working.
BTW, this is part of a distributed lighting control system, for lighting control in 5 buildings. A base unit sends commands wirelessly to five possible remotes. If addressed, the remote then communicates via RS485 to switch on/off the appropriate light. There will never be any simultaneous data transmission on the soft and hard UARTs.
I've got all the individuall pieces of code, except the wireless data reception, working flawlessly. Once I solve this dilemna, I'll combine all the pieces to complete the final system.
Thanks for the input! _________________ John Morley |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sat Sep 16, 2006 7:22 am |
|
|
Something totally different, but why are using the old PIC16F73 in a new design? Something funny in the Microchip product line is that the newer chips have more features and are cheaper. My main problem with the PIC16F73 is that it has no breakpoint register for the In Circuit Debugger (ICD) which makes debugging a lot more difficult.
For example you could consider the PIC16F690 which is cheaper and as a bonus has Data EEPROM, larger RAM, 10-bit ADC instead of 8-bit, 2 comperators and 1 ICD breakpoint. Problem could be that with 20 pins it has not enough I/O pins, in that case have a look at the 28-pin 16F916 or 40 pin 16F917.
Using the Filter options on the Microchip website it is easy to compare all features of the mentioned chip versions. |
|
|
John Morley
Joined: 09 Aug 2004 Posts: 97
|
|
Posted: Tue Sep 19, 2006 8:02 pm |
|
|
Hi All,
Well, after being away from this problem for a few days, I took a look at it again today. First, I did what I should have done from the beginning. I'm trying to get two PICs to communicate wirelessly using a pair of Aerocomm AC4490 wireless modems. These modules can be configured a direct serial cable replacement. Anyway, I disconnected the modems and connected the PICs directly together. Voila! Perfect communications! Now, I reconnected the Aerocomm modems and found that I'm receiving only the first couple of characters of my receive string. Another thing I should have done earlier was to put a scope on the Rx pin of the receiving PIC. What I found might explain the problem I am seeing: The AC4490 is a 3.3V part but it can be powered with +5V as I am doing. The I/O of the module (including the Tx and Rx pins) are all 3.3V out max however. The only difference I see on the scope between the working condition (PIC to PIC directly) and the non-working condition (PIC to PIC with wireless modem) is the 0 to 5V swing of the direct connect, versus the 0 to 3.3V swing of the modem connection. I could not find anything in the datasheet that addressed this issue, so I'm not sure if this could be an explanation for why I am not receiving all the characters with the wireless modems in place?
Finally, I am using the 16F73 for code development as I have some prototype boards left over from a previous project. The final version will probably use a more modern PIC.
Thanks,
John _________________ John Morley |
|
|
Ttelmah Guest
|
|
Posted: Wed Sep 20, 2006 2:21 am |
|
|
Tie a diode, to the 3.3v rail, with a pull up resistor to the 5v rail, to generate a nominally about 3.9v rail. Then tie a pull up resistor to this new rail, onto the signal. 3.3v outputs, are normally able to be pulled up to 0.6v above the internal supply without problems. A 3.3v output, is adequate to drive a TTL 5v input, but not a 'schmitt' 5v input. The serial inputs on the PIC, have schmitt input buffers (actually require 4v, to be 100% guaranteed to work when running from a 5v supply). Aternatively, just stick a TTL buffer between the signal and the PIC.
Best Wishes |
|
|
|
|
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
|