CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Hardware and Software Uart Problem

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
arunkish



Joined: 23 Dec 2008
Posts: 83

View user's profile Send private message

Hardware and Software Uart Problem
PostPosted: Tue Oct 16, 2012 11:08 pm     Reply with quote

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: 19551

View user's profile Send private message

PostPosted: Wed Oct 17, 2012 1:11 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Oct 17, 2012 3:40 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Oct 17, 2012 8:29 am     Reply with quote

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: 19551

View user's profile Send private message

PostPosted: Wed Oct 17, 2012 10:33 am     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Fri Oct 19, 2012 6:32 am     Reply with quote

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: 19551

View user's profile Send private message

PostPosted: Fri Oct 19, 2012 6:41 am     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Fri Oct 19, 2012 8:30 am     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Fri Oct 19, 2012 8:50 am     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Fri Oct 19, 2012 9:19 am     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Fri Oct 19, 2012 12:45 pm     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Fri Oct 19, 2012 7:02 pm     Reply with quote

[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: 19551

View user's profile Send private message

PostPosted: Sat Oct 20, 2012 12:58 am     Reply with quote

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. Smile

Best Wishes
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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