|
|
View previous topic :: View next topic |
Author |
Message |
arunkish
Joined: 23 Dec 2008 Posts: 83
|
Hardware and Software Uart Problem |
Posted: Tue Oct 16, 2012 11:08 pm |
|
|
Hello
I am using PIC18F4620 @ 4Mhz and using both software and hardware UARTS.
Code: |
#use rs232(baud=4800,xmit=PIN_C6,rcv=PIN_C7,ERRORS)
#use rs232(baud=2400,xmit=PIN_D2,rcv=PIN_D3,ERRORS,FORCE_SW,STREAM=pcout)
|
Code for Receiving From Hardware Uart
Code: |
#INT_RDA
void serial_reciv_int(void)
{
byte irda_cnt;
int_rdacount=0;
for(irda_cnt=0;irda_cnt<=2;irda_cnt++)
{
rxchar = getc();
rxmessage[irda_cnt]=rxchar;
}
int_rdacount=rxmessage[2];
for(irda_cnt=0;irda_cnt<=int_rdacount-1;irda_cnt++)
{
rxchar = getc();
rxmessage[irda_cnt]=rxchar;
}
receivedrx=1;
}
|
Format I use to send string to pcout stream
Code: | fprintf(pcout,"READYYYYYYYY\x0D"); |
With the above code, everything works fine without problem. I will be able to send / receive from Hardware uart and at the same time i will also be able to send characters to software uart (pcout stream).
But when I use the below code in the main, inside a while loop to receive characters from software uart - pcout stream, the hardware uart is not receiving anything until I send a carriage return to the software uart from my pc. After that both hardware and software uart will be able to send / receive characters.
In other words, adding the below code disables the receive function of the hardware uart until carriage return is returned in software uart.
Code: |
if (kbhit(pcout))
{
fgets(pcstring,pcout);
//fprintf(pcout,"Received!");
}
|
Please advise. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Wed Oct 17, 2012 1:11 am |
|
|
Start with the basic mantra:
INT_RDA, _must_ do just one thing. Receive _one_ character, and exit. Nothing else.
INT_RDA, says that a _single_ character is waiting to receive. The handler needs to just receive this one character.
This can be done is a few _uSec_. However as currently written, your INT_RDA, sits for 20+mSec waiting for characters. This destroys any ability to do anything else.
Then, you need to be giving both streams names, and using fgets everywhere you want to talk to the streams. When using more than one stream, you must use names throughout.
Now, handled properly INT_RDA, will take about 80 CPU cycles. At 4Mhz, this is 80uSec. Serial requires timing accuracy at about 4% error to be acceptable. If a character arrives while sending a bit on the software UART, this would give a bit timing error of:
(1/2400)+80uSec = 0.496mSec rather than the 0.416mSec required. Error of 19% in the bit time. Not acceptable.
You could make this work, by increasing the CPU clock.
Alternatively, just use a software UART for the 4800bps link, and the hardware UART for the 2400bps link, with buffer based interrupt transmission. Add the keyword 'DISABLE_INTS' to the software UART definition, and since this is always running _faster_ than the hardware UART link, interrupts will be disabled during the individual character transmission/reception, but will happen at the end of each character, hence fast enough to always keep up with the slower link.
Your approach is currently 'fundamentally flawed'. You need to rethink, and understand the limitations/abilities of the hardware/software.
Best Wishes |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Oct 17, 2012 3:40 am |
|
|
I have to agree to everything Ttelmah says.
Then my additional comment is that software UARTs are a P.I.T.A. I'm sure you can get them working and many other people created working projects as well, but they imply extra restrictions on your code design and will cost you time & money when not expected. Now there are PIC18 processors with 2 UARTs I would give a strong recommendation to upgrade to a newer PIC model and only use the 2 hardware UARTs. From a quick look it seems like the PIC18F46K22 is pin compatible to your PIC18F4620.
The good news is that these newer PICs have more features, use less current and are cheaper as well. For example, high volume price for the PIC18F4620 = $4.27 and for the PIC18F46K22 = $2.16 |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Wed Oct 17, 2012 8:29 am |
|
|
You can't argue with the point that having two hardware UARTs makes things much easier.
However, people always seem eager to fail when they have to deal with UARTs. It's always "Let's have an interrupt" before thinking for a moment about how the code will run. If you don't plan to have your program run once and then go to sleep, a main() routine is going to be running in a loop. If that loop takes less time to execute than a character takes to come in via the UART, you don't need an interrupt; you can poll the flag instead. That means you can use a single interrupt more productively, to run an accurate timing routine. So you can receive data on a software UART first by responding to the start bit, then counting 8 data bits and grabbing each one right in the center. If you can set up a clock that responds at the right times (meaning no extraneous interrupts!) this will always work.
What you cannot do without some pain is run a software UART in full-duplex mode. That would be more of a gee-whiz stunt than a serious project. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Wed Oct 17, 2012 10:33 am |
|
|
Yes.
This is made worse in this case though by having the software UART running at half the speed of the hardware. Here, in the time taken to send one character using the software, two characters could arrive on the hardware, and you have 'no hope' even with the fastest possible loop sending characters....
Best Wishes |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Fri Oct 19, 2012 6:32 am |
|
|
John P wrote: | You can't argue with the point that having two hardware UARTs makes things much easier.
However, people always seem eager to fail when they have to deal with UARTs. It's always "Let's have an interrupt" before thinking for a moment about how the code will run. If you don't plan to have your program run once and then go to sleep, a main() routine is going to be running in a loop. If that loop takes less time to execute than a character takes to come in via the UART, you don't need an interrupt; you can poll the flag instead. That means you can use a single interrupt more productively, to run an accurate timing routine. So you can receive data on a software UART first by responding to the start bit, then counting 8 data bits and grabbing each one right in the center. If you can set up a clock that responds at the right times (meaning no extraneous interrupts!) this will always work.
What you cannot do without some pain is run a software UART in full-duplex mode. That would be more of a gee-whiz stunt than a serious project. |
There is no black magic in running a full duplex interrupt based software UART. The transmit logic is much simpler than the receive logic. Running a single timer interrupt at a multiple of the bit rate can be used for both transmit and receive processing. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Fri Oct 19, 2012 6:41 am |
|
|
Yes. I have actually posted here in the past a simplified code for one directional I/O like this, using a timer as you say, just as an example, with the notes on how to upgrade if required.
Best Wishes |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Oct 19, 2012 8:30 am |
|
|
re software uarts.
interrupts ruin everything.
i will not use a soft UART in a 'design for hire'
as i believe it is irresponsible to do so.
the ONLY reliable soft UART is TX only - in an environment where there
are no interrupts possible
just my 2 cents |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Fri Oct 19, 2012 8:50 am |
|
|
asmboy wrote: | re software uarts.
interrupts ruin everything.
i will not use a soft UART in a 'design for hire'
as i believe it is irresponsible to do so.
the ONLY reliable soft UART is TX only - in an environment where there
are no interrupts possible
just my 2 cents |
Are we talking about the same thing? A CCS software UART is a very simple bit of code and, with the exception of output only, I would not use for production code.
A software UART implementing interrupts does not "ruin" anything. I have implemented multiple products using software interrupt driven UARTs that have reliably performed in the field for years. Given the chance, I would always use a HW UART over a SW UART but if you have a good understanding of processors, interrupt priorities, interrupt latency and interrupt coding practices, then it is possible to build reliable SW UARTs. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Oct 19, 2012 9:19 am |
|
|
Quote: |
A software UART implementing interrupts does not "ruin" anything.
|
i mean primarily INTS other than JUST RX DATA based on a B port RX pin with edge INTS for handling - and with NO TX capability as well.
Other than than that ONE INT exception
BUT NO other INTS can be allowed or it fails eventually by having BAD RX or fractured TX
Thats the one exception i can think of.
Any other INTS in the code will sooner or later spoil SOFT RX and / or SOFT TX. its unavoidable given enough run time. |
|
|
n-squared
Joined: 03 Oct 2006 Posts: 99
|
|
Posted: Fri Oct 19, 2012 12:45 pm |
|
|
Hi
I wrote this code for PIC18F87J11 running at 32MHz, but it can be easily modified to run on almost any other device.
This code runs perfectly in a real product.
Notice that both external interrupt and timer interrupt are defined as "high".
Code: |
//======- COM2 definitions =================================
#bit COM2_OUT = PORTG.4
#bit COM2_IN = PORTB.3
#define COM2_POLARITY 1 // inverse output
#define COM2_RX_LEN 16
#define COM2_TX_LEN 16
bit COM2_tx; // transmitting a byte
UINT COM2_tword; // transmit buffer for start, 8 data, stop bits
UCHAR COM2_tbits; // transmit bit counter
UCHAR COM2_tbuf[COM2_TX_LEN]; // transmit data buffer
UCHAR COM2_tcnt; // bytes to transmit
UCHAR COM2_txi, COM2_txo; // buffer input/output pointers
UCHAR COM2_rx; // receive bit count
UCHAR COM2_rbits; // receive bits
UCHAR COM2_rbuf[COM2_RX_LEN]; // receive data buffer
UCHAR COM2_rcnt; // received bytes
UCHAR COM2_rxi, COM2_rxo; // buffer input/output pointers
//======- COM2 software UART ===============================================
#ignore_warnings 225
#int_EXT3 high
void RX_start_bit(void) // UART input on RB3
{
/* TMR2 = 0;
PR2 = 150; // set timer to middle of first data bit
TMR2IF = 0; // clear interrupt flag
TMR2ON = 1; // enable interrupt*/
setup_timer_2(T2_DIV_BY_4,135,8);
set_timer2(0);
COM2_tx = 0; // disable transmission
COM2_rx = 0; // initialize bit counter
disable_interrupts(int_EXT3);
clear_interrupt(int_timer2); // start bit collection interrupt
enable_interrupts(int_timer2);
}
//==============================================================
#int_TIMER2 high
void COM2_isr(void) // software UART @9600bps
{
if (COM2_tx) // transmitting?
{
COM2_OUT = (make8(COM2_tword, 0) & 1) ^ COM2_POLARITY;
COM2_tword >>= 1;
if (!--COM2_tbits)
{
if (!COM2_tcnt) // no more chars to send?
{
COM2_tx = 0;
disable_interrupts(int_TIMER2);
clear_interrupt(int_ext3); // no more chars to send, switch to receive mode
enable_interrupts(int_EXT3); // prepare for next char
}
else // more chars to send
{
COM2_tword = (0xff00 | COM2_tbuf[COM2_txo]) << 1; // add start and stop bits
COM2_tbits = 10;
if (++COM2_txo >= COM2_TX_LEN)
COM2_txo = 0;
COM2_tcnt--;
}
}
}
else // in receive mode
{
// PR2 = 102; // return to 104uS intervals
if (!COM2_rx)
setup_timer_2(T2_DIV_BY_4,102,8);
set_timer2(5);
COM2_rbits >>= 1;
if (PORTB3)
COM2_rbits |= 0x80;
if (++COM2_rx >= 8)
{
COM2_rcnt++;
COM2_rbuf[COM2_rxi] = COM2_rbits;
if (++COM2_rxi >= 16)
COM2_rxi = 0;
disable_interrupts(int_TIMER2);
clear_interrupt(int_ext3);
enable_interrupts(int_EXT3); // prepare for next char
TMR2ON = 0; // disable interrupt
}
}
}
//==================================================================-
void COM2_send_chr(UCHAR chr)
{
while (COM2_tcnt >= COM2_TX_LEN); // wait for space in transmit buffer
if (!COM2_tx)
{
COM2_tword = (0xff00 | chr) << 1; // add start and stop bits
COM2_tbits = 10;
COM2_tx = 1;
clear_interrupt(int_TIMER2);
enable_interrupts(int_TIMER2); // start transmission
}
else
{
COM2_tbuf[COM2_txi] = chr;
if (++COM2_txi >= COM2_TX_LEN)
COM2_txi = 0;
COM2_tcnt++;
}
}
//=============================================================================
void COM2_send_buf(UCHAR *buf, UCHAR len)
{
disable_interrupts(int_EXT3); // disable RX
COM2_rxi = COM2_rxo = COM2_rcnt = 0; // reset input buffer
setup_timer_2(T2_DIV_BY_4,102,8); // 104uS interrupt at 32MHz for 9600 Baud on B0,B1 (Rx,Tx)
clear_interrupt(int_TIMER2);
COM2_tx = 0;
while (len--)
{
if (!TMR2ON)
TMR2ON = 1;
while (COM2_tcnt >= COM2_TX_LEN); // wait for space in transmit buffer
COM2_send_chr(*buf++);
}
// enable_interrupts(int_EXT3);
}
//=============================================================================
void COM2_init(void)
{
/* TRISG4 = 0;
COM2_rxi = COM2_rxo = COM2_rcnt = 0;
COM2_txi = COM2_txo = COM2_tcnt = 0;
COM2_OUT = 0;
COM2_tx = 0;
setup_timer_2(T2_DIV_BY_4,102,2); // 104uS interrupt at 32MHz for 9600 Baud on B3,G4 (Rx,Tx)
ext_int_edge(3, H_TO_L);
clear_interrupt(int_EXT3);
enable_interrupts(int_EXT3); // enable RX*/
}
//=============================================================================
UCHAR COM2_get_chr(void)
{
UCHAR x;
x = COM2_rbuf[COM2_rxo];
if (++COM2_rxo >= COM2_RX_LEN)
COM2_rxo = 0;
if (COM2_rcnt)
COM2_rcnt--;
return x;
}
|
good luck
Noam _________________ Every solution has a problem. |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Fri Oct 19, 2012 7:02 pm |
|
|
[quote="asmboy"] Quote: |
... Any other INTS in the code will sooner or later spoil SOFT RX and / or SOFT TX. its unavoidable given enough run time. |
This is true of the standard (simple) CCS software UART implementation. It is not true for a correctly implemented interrupt driven SW UART such as I have previously described. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Sat Oct 20, 2012 12:58 am |
|
|
Yes.
It is perhaps worth saying I have an old board, designed before any small PIC's had UART's. Used the original 16C84, which was the first chip Microchip did with flash memory. Actually designed a year _before_ Microchip claimed they launched this PIC (code was written in 1992), which does full duplex 9600bps software serial using two state machines in an interrupt called every 34.7uSec, and some examples are still running now, twenty years latter. Didn't have any year 2000 problems (my RTC is based on the Unix model, which will potentially have a wrap problem in 2017 - original spec was for a 25year design life....), which logs around 99.99999% comms reliability, and handles interrupts from half a dozen other devices while this is going on.
I queried with Microchip, when they put up a web page, listing the launch of the chip in 1993, pointing out that this unit was made before this, and they said that the first batch was considered 'engineering samples', and the official launch was the next year.
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
|