public inbox for ecos-discuss@sourceware.org
 help / color / mirror / Atom feed
* RE: [ECOS]  Re: Half Duplex RS485
@ 2008-01-25  4:16 wangcui
  2008-01-26 20:41 ` Laurie Gellatly
  2008-01-27  4:52 ` [ECOS] Uart missing chars when in Release Laurie Gellatly
  0 siblings, 2 replies; 17+ messages in thread
From: wangcui @ 2008-01-25  4:16 UTC (permalink / raw)
  To: Laurie Gellatly, Grant Edwards, ecos-discuss

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="gb2312", Size: 4736 bytes --]



Here is the patch, shows all modifications in my working folder.
Acutally, I did more than FIFO stuff, I also made it support 2 diffent 16x5x chips on my target(different register-offset-step), and something else.

BTW, I prefer to solve half-duplex problem on hardware level, which avoid lots of effort on software level.

> From: laurie.gellatly@netic.com
> To: iucgnaw@msn.com; grante@visi.com; ecos-discuss@sources.redhat.com
> Subject: RE: [ECOS] Re: Half Duplex RS485
> Date: Fri, 25 Jan 2008 12:59:54 +1100
>
> Hi Wang,
> I'd be interested to see what you had to modify to make the FIFO work.
>
> The eCos source does not know about the fractional divider.
> Did you enable fix that as well?
>
> ...Laurie:{)
>
> -----Original Message-----
> From: wangcui [mailto:iucgnaw@msn.com]
> Sent: Friday, 25 January 2008 12:33 PM
> To: Laurie Gellatly; Grant Edwards; ecos-discuss@sources.redhat.com
> Subject: RE: [ECOS] Re: Half Duplex RS485
>
>
>
> Just FYI, the LPC2XXX's UART0 is not fully functional. So when the 16x5x
> serial driver check FIFO(in serial_config_port()), it will fail, thus FIFO
> is disabled for UART0. But UART1 works fine.
>
> To resolve it, I have to modified 16x5x driver code, force enable FIFO for
> UART0 and UART1.
>
>> From: laurie.gellatly@netic.com
>> To: grante@visi.com; ecos-discuss@sources.redhat.com
>> Date: Thu, 24 Jan 2008 08:12:17 +1100
>> Subject: RE: [ECOS] Re: Half Duplex RS485
>>
>> Wang/Grant, thanks for the replies.
>>
>>>> My project has RS485 half duplex driven by UART0 of an
>>>> LPC2112. At present I've modified pc_serial_start_xmit and
>>>> pc_serial_stop_xmit to change a pin state so that the same
>>>> wires can be used for transmit and receive.
>>>>
>>>> This does not see to work when the FIFO is enabled.
>>>
>>>Then you probably did it wrong. :)
>>>
>>>I imagine that RTS is shutting off too soon. The problem is
>>>that pc_serial_stop_xmit() is called when the driver has no
>>>more data to send _to_ the UART. That's not when you need to
>>>shut off RTS. You need to shut off RTS when the UART is done
>>>sending data and both the FIFO and shift register are empty.
>>>
>>>You're probably shutting off RTS while the UART still has data
>>>in the tx FIFO and the tx shift register.
>> Funny thing is that it appears that the first part of the
>> transmission is lost.
>>
>>>> Has anyone else done RS485 half duplex?
>>>
>>>Many, many times.
>>>
>>>> Did you modify these routines or write your own?
>>>
>>>I usually pick a UART that supports half-duplex operation, and
>>>then just enabled that feature in the UART. [I use a custom
>>>eCos serial driver that supports quite a few more advanced UART
>>>features than the standard driver (e.g. flow control,
>>>half-duplex, inter-byte timeouts, 9-bit modes, FIFO control,
>>>etc.).
>>>
>>>If you don't have a proper UART, you need to enable the tx
>>>shift register empty interrupt and use that to trigger code
>>>that de-asserts RTS. If you're using a broken UART that
>>>doesn't have a tx shift register empty interrupt, then you'll
>>>have to poll for the tx shift register empty status. If you're
>>>using a really broken UART that doesn't have a _working_
>>>shift-register empty status[1], then you may have to start a
>>>timer that will wake you up at the point in time where RTS
>>>needs to be changed.
>>>
>>>> Did you get the FIFO to work?
>>>
>>>When there was one, yes.
>>>
>>>
>>>[1] There are broken UARTs (including a few PC chipsets) whose
>>> shift-register empty bit gets set _before_ the stop bit has
>>> been sent. In that case, you may need to use some sort of
>>> time-delay to wait until after the stop bit has been sent
>>> to toggle RTS. On a properly implimented RS-485 bus, there
>>> should be pull-up and pull-down resisters so that the bus
>>> idles in the mark state (same value as a stop bit), but to
>>> be on the safe side you should leave the bus driver on
>>> until after the stop bit has been sent.
>>
>> Grant, do you know if the LPC series have 'broken' UARTS?
>> Specifically LPC2212 and LPC2103?
>> U0TSR looks promising as an indication of when the bits
>> 'have left the building' - is it accurate or do I
>> need to add a time delay?
>>
>> Really appreciate the help. ...Laurie:{)
>>
>> --
>> Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
>> and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss
>>
>
> _________________________________________________________________
> ÌìÁ¹ÁË£¬ÌíÒÂÁË£¬ÐĶ¯ÁË£¬¡°Æß¼þ¡±ÁË
> http://get.live.cn
>

_________________________________________________________________
MSN ÖÐÎÄÍø£¬×îÐÂʱÉÐÉú»î×ÊѶ£¬°×Áì¾Û¼¯ÃÅ»§¡£
http://cn.msn.com

[-- Attachment #2: 16x5x.patch --]
[-- Type: text/plain, Size: 10771 bytes --]

diff -r -u5 -N -x CVS /cygdrive/z/Share/Resource/eCos/anoncvs/ecos/packages/devs/serial/generic/16x5x/current/src/ser_16x5x.c /cygdrive/d/work/dev/ecos/packages/devs/serial/generic/16x5x/current/src/ser_16x5x.c
--- /cygdrive/z/Share/Resource/eCos/anoncvs/ecos/packages/devs/serial/generic/16x5x/current/src/ser_16x5x.c	2007-06-22 19:41:49.000000000 +0800
+++ /cygdrive/d/work/dev/ecos/packages/devs/serial/generic/16x5x/current/src/ser_16x5x.c	2008-01-25 11:47:46.232562700 +0800
@@ -62,15 +62,18 @@
 #include <cyg/hal/hal_io.h>
 
 // Only compile driver if an inline file with driver details was selected.
 #ifdef CYGDAT_IO_SERIAL_GENERIC_16X5X_INL
 
-#ifndef CYGPRI_IO_SERIAL_GENERIC_16X5X_STEP
-#define CYGPRI_IO_SERIAL_GENERIC_16X5X_STEP 1
-#endif
-
-#define SER_REG(_x_) ((_x_)*CYGPRI_IO_SERIAL_GENERIC_16X5X_STEP)
+//here we use a 'step' variable to indicate the address delta between a 16X5X channel's registers
+//user should new a variable named 'step' and set appropriate value before access 16X5X's register.
+//For example:
+//{
+//  int step = ser_chan->step;
+//  HAL_WRITE_UINT8(base+REG_thr, c);
+//}
+#define SER_REG(_x_) ((_x_)*step)
 
 // Receive control Registers
 #define REG_rhr SER_REG(0)    // Receive holding register
 #define REG_isr SER_REG(2)    // Interrupt status register
 #define REG_lsr SER_REG(5)    // Line status register
@@ -180,10 +183,15 @@
 // selec_baud[] must be define by the client
 
 typedef struct pc_serial_info {
     cyg_addrword_t base;
     int            int_num;
+    int            int_prio;
+    int            step;
+    int            baud_num;
+    int           *select_baud;
+    int          (*baud_generator)(unsigned int baud);
     cyg_interrupt  serial_interrupt;
     cyg_handle_t   serial_interrupt_handle;
 #ifdef CYGPKG_IO_SERIAL_GENERIC_16X5X_FIFO
     enum {
         sNone = 0,
@@ -233,12 +241,17 @@
 serial_config_port(serial_channel *chan, 
                    cyg_serial_info_t *new_config, bool init)
 {
     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
     cyg_addrword_t base = ser_chan->base;
-    unsigned short baud_divisor = select_baud[new_config->baud];
+    int step = ser_chan->step;
+    unsigned short baud_divisor;
     unsigned char _lcr, _ier;
+    
+    if (new_config->baud >= ser_chan->baud_num) return false;  // Invalid configuration
+
+    baud_divisor = ser_chan->select_baud[new_config->baud];
     if (baud_divisor == 0) return false;  // Invalid configuration
 
     // Disable port interrupts while changing hardware
     HAL_READ_UINT8(base+REG_ier, _ier);
     HAL_WRITE_UINT8(base+REG_ier, 0);
@@ -253,21 +266,22 @@
     if (init) {
 #ifdef CYGPKG_IO_SERIAL_GENERIC_16X5X_FIFO
         unsigned char _fcr_thresh;
         cyg_uint8 b;
 
-        /* First, find out what kind of device it is. */
-        ser_chan->deviceType = sNone;
-        HAL_WRITE_UINT8(base+REG_mcr, MCR_LOOP); // enable loopback mode
-        HAL_READ_UINT8(base+REG_msr, b);         
-        if (0 == (b & 0xF0)) {   // see if MSR had CD, RI, DSR or CTS set
-            HAL_WRITE_UINT8(base+REG_mcr, MCR_LOOP|MCR_DTR|MCR_RTS);
-            HAL_READ_UINT8(base+REG_msr, b);
-            if (0xF0 != (b & 0xF0))  // check that all of CD,RI,DSR and CTS set
-                ser_chan->deviceType = s8250;
+        if (ser_chan->deviceType == sNone) {
+            /* First, find out what kind of device it is. */
+            HAL_WRITE_UINT8(base+REG_mcr, MCR_LOOP); // enable loopback mode
+            HAL_READ_UINT8(base+REG_msr, b);         
+            if (0 == (b & 0xF0)) {   // see if MSR had CD, RI, DSR or CTS set
+                HAL_WRITE_UINT8(base+REG_mcr, MCR_LOOP|MCR_DTR|MCR_RTS);
+                HAL_READ_UINT8(base+REG_msr, b);
+                if (0xF0 != (b & 0xF0))  // check that all of CD,RI,DSR and CTS set
+                    ser_chan->deviceType = s8250;
+            }
+            HAL_WRITE_UINT8(base+REG_mcr, 0); // disable loopback mode
         }
-        HAL_WRITE_UINT8(base+REG_mcr, 0); // disable loopback mode
 
         if (ser_chan->deviceType == s8250) {
             // Check for a scratch register; scratch register 
             // indicates 16450 or above.
             HAL_WRITE_UINT8(base+REG_scr, 0x55);
@@ -308,10 +322,11 @@
               CYGNUM_IO_SERIAL_GENERIC_16X5X_FIFO_TX_SIZE;
             // Enable and clear FIFO
             HAL_WRITE_UINT8(base+REG_fcr, _fcr_thresh); 
         }
         else {
+            CYG_FAIL("Can't enable FIFO on this device.");
             ser_chan->tx_fifo_size = 1;
             HAL_WRITE_UINT8(base+REG_fcr, 0); // make sure it's disabled
         }
 
         ser_chan->tx_fifo_avail = ser_chan->tx_fifo_size;
@@ -348,36 +363,39 @@
 
 #ifdef CYG_IO_SERIAL_GENERIC_16X5X_BAUD_GENERATOR
     // Fill in baud rate table - used for platforms where this cannot
     // be determined statically
     int baud_idx, baud_val;
-    if (select_baud[0] == 9999) {
+
+    if (ser_chan->select_baud[0] == 9999) {
+        CYG_ASSERTC(ser_chan->baud_generator != NULL);
+
         // Table not yet initialized
         // Assumes that 'select_baud' looks like this:
         //   static int select_baud[] = {
         //       9999,  -- marker
         //       50,    -- first baud rate
         //       110,   -- second baud rate
         // etc.
-        for (baud_idx = 1;  baud_idx < sizeof(select_baud)/sizeof(select_baud[0]);  baud_idx++) {
-            baud_val = CYG_IO_SERIAL_GENERIC_16X5X_BAUD_GENERATOR(select_baud[baud_idx]);
-            select_baud[baud_idx] = baud_val;
+        for (baud_idx = 1;  baud_idx < ser_chan->baud_num;  baud_idx++) {
+            baud_val = ser_chan->baud_generator(ser_chan->select_baud[baud_idx]);
+            ser_chan->select_baud[baud_idx] = baud_val;
         }
-        select_baud[0] = 0;
+        ser_chan->select_baud[0] = 0;
     }
 #endif
 
 #ifdef CYGDBG_IO_INIT
-    diag_printf("16x5x SERIAL init - dev: %x.%d\n", 
-                ser_chan->base, ser_chan->int_num);
+    diag_printf("16x5x SERIAL init - dev: base=%x.step=%d.int_num=%d\n", 
+                ser_chan->base, ser_chan->step, ser_chan->int_num);
 #endif
     // Really only required for interrupt driven devices
     (chan->callbacks->serial_init)(chan);
 
     if (chan->out_cbuf.len != 0) {
         cyg_drv_interrupt_create(ser_chan->int_num,
-                                 CYG_IO_SERIAL_GENERIC_16X5X_INT_PRIORITY,
+                                 ser_chan->int_prio == -1 ? CYG_IO_SERIAL_GENERIC_16X5X_INT_PRIORITY : ser_chan->int_prio,
                                  (cyg_addrword_t)chan,
                                  pc_serial_ISR,
                                  pc_serial_DSR,
                                  &ser_chan->serial_interrupt_handle,
                                  &ser_chan->serial_interrupt);
@@ -409,10 +427,11 @@
 #ifndef CYGPKG_IO_SERIAL_GENERIC_16X5X_FIFO
     cyg_uint8 _lsr;
 #endif
     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
     cyg_addrword_t base = ser_chan->base;
+    int step = ser_chan->step;
 
 #ifdef CYGPKG_IO_SERIAL_GENERIC_16X5X_FIFO
     if (ser_chan->tx_fifo_avail > 0) {
         HAL_WRITE_UINT8(base+REG_thr, c);
         --ser_chan->tx_fifo_avail;
@@ -436,10 +455,11 @@
 {
     unsigned char c;
     cyg_uint8 _lsr;
     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
     cyg_addrword_t base = ser_chan->base;
+    int step = ser_chan->step;
 
     // Wait for char
     do {
         HAL_READ_UINT8(base+REG_lsr, _lsr);
     } while ((_lsr & LSR_RSR) == 0);
@@ -469,10 +489,11 @@
     case CYG_IO_SET_CONFIG_SERIAL_HW_RX_FLOW_THROTTLE:
       {
           cyg_uint8 _mcr;
           pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
           cyg_addrword_t base = ser_chan->base;
+          int step = ser_chan->step;
           cyg_uint32 *f = (cyg_uint32 *)xbuf;
           unsigned char mask=0;
           if ( *len < sizeof(*f) )
               return -EINVAL;
           
@@ -509,26 +530,34 @@
 static void
 pc_serial_start_xmit(serial_channel *chan)
 {
     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
     cyg_addrword_t base = ser_chan->base;
-    cyg_uint8 _ier;
+    int step = ser_chan->step;
+    cyg_uint8 _ier, _lsr;
     
     HAL_READ_UINT8(base+REG_ier, _ier);
     _ier |= IER_XMT;                    // Enable xmit interrupt
     HAL_WRITE_UINT8(base+REG_ier, _ier);
 #ifdef CYGPKG_IO_SERIAL_GENERIC_16X5X_XMIT_REQUIRE_PRIME
-    (chan->callbacks->xmt_char)(chan);
+    //some chip(at least LPC2XXX's embedded DUART) may not generate ISR_Tx interrupt after enable IER_XMT,
+    //so we try transmitting here to trigger the ISR_Tx intertupt.
+    HAL_READ_UINT8(base+REG_lsr, _lsr);
+    if ((_lsr & LSR_THE)) {
+        // transmitter holding register ready
+        (chan->callbacks->xmt_char)(chan);
+    }
 #endif
 }
 
 // Disable the transmitter on the device
 static void 
 pc_serial_stop_xmit(serial_channel *chan)
 {
     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
     cyg_addrword_t base = ser_chan->base;
+    int step = ser_chan->step;
     cyg_uint8 _ier;
 
     HAL_READ_UINT8(base+REG_ier, _ier);
     _ier &= ~IER_XMT;                   // Disable xmit interrupt
     HAL_WRITE_UINT8(base+REG_ier, _ier);
@@ -550,10 +579,11 @@
 pc_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
 {
     serial_channel *chan = (serial_channel *)data;
     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
     cyg_addrword_t base = ser_chan->base;
+    int step = ser_chan->step;
     cyg_uint8 _isr;
 
     // Check if we have an interrupt pending - note that the interrupt
     // is pending of the low bit of the isr is *0*, not 1.
     HAL_READ_UINT8(base+REG_isr, _isr);
@@ -645,14 +675,16 @@
                 }
             }
             break;
 #endif
         default:
-            // Yes, this assertion may well not be visible. *But*
-            // if debugging, we may still successfully hit a breakpoint
-            // on cyg_assert_fail, which _is_ useful
-            CYG_FAIL("unhandled serial interrupt state");
+            #if 0
+            //some chip(at least Philips SC16C2550B) may generate a interrupt without valid source,
+            //so we print a error message rather than die here.
+            diag_printf("unhandled serial interrupt state""\r\n");
+            #endif
+            break;
         }
 
         HAL_READ_UINT8(base+REG_isr, _isr);
     } // while
 

[-- Attachment #3: Type: text/plain, Size: 148 bytes --]

-- 
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2008-02-06 15:25 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-01-25  4:16 [ECOS] Re: Half Duplex RS485 wangcui
2008-01-26 20:41 ` Laurie Gellatly
2008-01-28 10:34   ` wangcui
2008-01-27  4:52 ` [ECOS] Uart missing chars when in Release Laurie Gellatly
2008-01-27  5:14   ` [ECOS] " Grant Edwards
2008-01-27  6:46     ` Laurie Gellatly
2008-01-27  9:54       ` Grant Edwards
2008-01-27 11:20         ` Laurie Gellatly
2008-01-27 15:55           ` Grant Edwards
2008-01-28  0:11             ` Laurie Gellatly
2008-01-28  2:37               ` Grant Edwards
2008-01-28  5:53                 ` Laurie Gellatly
2008-01-28 15:58                   ` Grant Edwards
2008-01-28 20:40                     ` Grant Edwards
2008-01-29 23:35                       ` Byron Jacquot
2008-02-06  9:35                       ` Laurie Gellatly
2008-02-06 15:25                         ` Grant Edwards

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).