Helpful ReplyHot!EZBL over RS-485

Author
bblessing
Super Member
  • Total Posts : 690
  • Reward points : 0
  • Joined: 2008/12/04 06:44:21
  • Location: Cincinnati, OH
  • Status: offline
2018/08/21 13:19:31 (permalink)
0

EZBL over RS-485

I don't have a specific problem, but was able to get EZBL to work over RS-485 with a few quick fixes. First I copied over _UxTXInterrupt from uart1_fifo.c to my Hardware Initializer file, tossed the weak attribute, and made the changes below. Obviously RB13 is the transmit enable line, which is tied to both transmit and receive enable. Next, I changed the following line in main for a much longer boot task: NOW_SetNextTaskTime(&EZBL_bootTask, NOW_ms*128u). Yes this does slow things down a bit, but I can handle 19 seconds total time. This was done with a dsPIC33EP64GS504. I have achieved similar results with a PIC24FJ256GA106 and PIC24FJ256GA108.
 
I would point out that this is still experimental and does fail if I attempt to program it when the application is running. However, another attempt will work. If anyone has a better method, I'd be happy to adopt it :-). For now, though, I hope that this helps somebody out.
 

void __attribute__((interrupt, no_auto_psv)) _U1TXInterrupt(void)
{
    _U1TXIF = 0;

    // Transmit a byte, if possible, if pending
    // NOTE: The FIFO internal data structures are being accessed directly
    // here rather than calling the TX_FIFO_Read*() functions because
    // any function call in an ISR will trigger a whole lot of compiler
    // context saving overhead. The compiler has no way of knowing what
    // registers any given function will clobber, so it has to save them
    // all. For efficiency, the needed read-one-byte code is duplicated here.
    while(!U1STAbits.UTXBF)
    {
        if(UART1_TxFifo.dataCount == 0u)
            break;

        _LATB13 = 1;
        // Transfer a byte from software FIFO to hardware TX FIFO
        U1TXREG = *UART1_TxFifo.tailPtr++;
        if(UART1_TxFifo.tailPtr >= UART1_TxFifo.fifoRAM + UART1_TxFifo.fifoSize)
            UART1_TxFifo.tailPtr = UART1_TxFifo.fifoRAM;
        EZBL_ATOMIC_SUB(UART1_TxFifo.dataCount, 1);
        UART1_TxFifo.activity.tx = 1;
    }
    while (!U1STAbits.TRMT);
    _LATB13 = 0;
}

#1
HowardSchlunder
MCU16 Applications
  • Total Posts : 744
  • Reward points : 0
  • Joined: 2007/06/14 16:26:19
  • Location: Chandler, AZ
  • Status: offline
Re: EZBL over RS-485 2018/08/21 15:36:10 (permalink) ☄ Helpfulby bblessing 2018/08/22 11:06:58
5 (3)
For half-duplex mediums, you might want to change the EZBL_FLOW_THRESHOLD value, for example, at global/file-scope, add:
    EZBL_SetSYM(EZBL_FLOW_THRESHOLD, 96);
The default was 18 bytes in EZBL v2.04 and is 32 bytes in EZBL v2.10.
 
The net effect of making this change will be that the bootloading logic will generate a lot less outbound software flow control messages and better throughput could be achieved. Each message is a 16-bit integer advertising how many bytes of free space are currently available in the software RX FIFO, less any free space previously advertised, but not yet delivered to the RX FIFO (or recognized by software, even though the data may actually be already sitting in the FIFO due to background ISR processing).
 
 
For the best performance and a solution that doesn't depend on the bootloader task execution frequency, I recommend implementing a custom, blocking TX_FIFO_OnWrite() callback function and delete or disable the _U1TXInterrupt() ISR. In your UART1_TX_FIFO_OnWrite() callback, you would:
1. Wait for all RX activity or potential RX activity to cease
2. Set your TX Enable line
3. Copy reqWriteLen bytes of data from *writeSrc to U1TXREG
4. Purge the existing data from destFIFO by calling EZBL_FIFORead((void*)0, destFIFO, bytesPushed);
5. Poll for hardware transmit completion
6. Clear the TX Enable and revert to RX mode
7. Return reqWriteLen
 
Implementing step 1 might entail polling until:
U1STAHbits.RIDLE && ((UART1_RxFifo.dataCount >= EZBL_bootCtx.bytesRequested) || (NOW_32() - startTime > NOW_ms*2u))
where startTime is set to NOW_32() before the test and continuously during the polling loop whenever (!U1STAHbits.RIDLE). The idea is to timeout after the greater of several character idle times or a couple milliseconds to ensure the medium is idle and the remote host node won't transmit anything else. A fixed couple milliseconds is helpful since things like USB to UART transceivers will operate at the mercy of the USB bus, which typically implements a 1ms polling interval for Full Speed USB devices and adds a relatively fixed round trip communications latency. 
 
In normal operation dataCount should match bytesRequested when the PC node stops transmitting, but the timeout is still necessary since the end of file activities involve advertising buffer free space, even though the host node doesn't have any .bl2 file data left to transmit to the Bootloader.
 
 
The _U1TXInterrupt() code can be left in place but disabled by setting the interrupt priority to 0. Statically you could use:
EZBL_SetSYM(UART1_TX_ISR_PRIORITY, 0);
At run-time you could write directly to the IPCx registers to control this, or in EZBL v2.10, via the EZBL_FIFOSetIntPri(&UART1_TxFifo, 0); API. Of course, deleting all code that sets the _U1TXIE bit will also work, but finding this code could be error prone since it could theoretically be written to indirectly via calls to EZBL_FIFOIntEnable(), EZBL_FIFOIntEnableSet(), UART1_FIFO_EnableInterrupts(), EZBL_SetIntEn(), EZBL_WrIntEn(), EZBL_InvIntEn(), UART1_TX_FIFO_OnWrite(), etc., even though the current releases of EZBL don't actually call all of these.
 
 
post edited by HowardSchlunder - 2018/08/21 15:37:37
#2
bblessing
Super Member
  • Total Posts : 690
  • Reward points : 0
  • Joined: 2008/12/04 06:44:21
  • Location: Cincinnati, OH
  • Status: offline
Re: EZBL over RS-485 2018/08/21 17:25:26 (permalink)
0
I'll test that out as soon as I can! Thank you!

I have to admit that working with EZBL has been a humbling experience. Out of the box it worked great for a project utilizing an FTDI USB/UART chip. Digging into the guts of the project has shown me techniques that I've never seen in practice. Hopefully I'll be able to step up my game.

If you were instrumental in writing EZBL, which wouldn't be a surprise as your name was all over the source code for the old MLA TCP/IP stack, then my hat goes off to you. It truly is something to behold.
#3
HowardSchlunder
MCU16 Applications
  • Total Posts : 744
  • Reward points : 0
  • Joined: 2007/06/14 16:26:19
  • Location: Chandler, AZ
  • Status: offline
Re: EZBL over RS-485 2018/08/22 15:09:13 (permalink) ☄ Helpfulby bblessing 2018/08/24 10:48:36
5 (3)
Yes, I might have had some involvement writing EZBL. However, bootloaders can downgrade expensive hardware into bricks or enable Internet hackers to reprogram your ECU as you drive down the freeway, so I uhh, prefer to think that some "applications engineering team at Microchip" was primarily responsible for EZBL's development.
 
I tested my prior suggestion on an Explorer 16/32 + PIC24FJ1024GB610 PIM @ 460800 baud and monitored a GPIO (TX_EN) pin + U2RX and U2TX using a logic analyzer. I observed successful bootloading and half-duplex communications that should be compatible with an RS-485 or RS-422 network using the following code. This example can be simply added to ex_boot_uart\main.c under EZBL v2.10 without digging into the UART code in ezbl_lib.a or rewriting the transmit ISR (although the ISR does become a minor amount of dead code by ignoring it).
 
The only adjustment I needed compared to my prior description was some compensation while reading EZBL_bootCtx.bytesRequested. EZBL_Install2Flash() adds the free-space flow control advertisements to this variable just before transmitting said flow control advertisements. This makes the RX Idle test always end by the slower timeout fallback if the variable is used without un-adding the advertisement first.
 
EZBL_SetSYM(EZBL_FLOW_THRESHOLD, 96);
EZBL_SetSYM(UART2_TX_ISR_PRIORITY, 0);

unsigned int UART2_TX_FIFO_OnWrite(unsigned int bytesPushed, void *writeSrc, unsigned int reqWriteLen, EZBL_FIFO *destFIFO)
{
unsigned short expectedRXCount;
destFIFO->activity.softTx = 1;

if(reqWriteLen == 0)
return 0;

// Delete data in software TX FIFO - we will bypass the buffer and do a blocking/polled TX instead
EZBL_FIFORead(0, destFIFO, bytesPushed);

// Determine how many RX bytes should exist when remote node will go silent
expectedRXCount = EZBL_bootCtx.bytesRequested;
if(reqWriteLen == 2u) // EZBL_Install2Flash() pre-adds EZBL_bootCtx.bytesRequested by free space TX messages, so undo this offset
{
expectedRXCount -= (((unsigned short)((unsigned char*)writeSrc)[1])<<8) | ((unsigned short)((unsigned char*)writeSrc)[0]);
if(expectedRXCount > EZBL_bootCtx.bytesRequested)
expectedRXCount = EZBL_bootCtx.bytesRequested;
}

// Wait for RX idle
do
{
unsigned long startTime = NOW_32();
while((UART2_RxFifo.dataCount < expectedRXCount) && (NOW_32() - startTime < NOW_ms*2u));
} while(!U2STAbits.RIDLE);

// Copy write data to hardware TX FIFO
LEDOn(0x02); // Set TX Enable control
for(bytesPushed = 0; bytesPushed < reqWriteLen; bytesPushed++)
{
while(U2STAbits.UTXBF);
U2TXREG = (unsigned int)((unsigned char*)writeSrc)[bytesPushed];
}
while(!U2STAbits.TRMT); // Wait for hardware FIFOed data to finish transmitting
LEDOff(0x02); // Clear TX Enable and restore RX mode

return reqWriteLen;
}

#4
bblessing
Super Member
  • Total Posts : 690
  • Reward points : 0
  • Joined: 2008/12/04 06:44:21
  • Location: Cincinnati, OH
  • Status: offline
Re: EZBL over RS-485 2018/08/24 10:50:12 (permalink)
0
Sorry for the late reply, as I have been away from any hardware for the past couple of days. It didn't take long to show just how much better your method is: 2.175s vs. 19s to program. Thank you again for this!
#5
SergioP
New Member
  • Total Posts : 1
  • Reward points : 0
  • Joined: 2018/09/10 05:34:19
  • Location: 0
  • Status: offline
Re: EZBL over RS-485 2018/09/11 01:01:32 (permalink)
0
Hello,
First of all, thank you very much for your contributions. They are awesome.
 
I want to share my own experience with Howard´s code:
I have an rs485 setting and I was getting an error until I deleted the compensation while reading EZBL_bootCtx.bytesRequested. After that I successfully loaded a program.
 
Howard, do you know what could be happening? You would be very kind solving this mystery ;)
 
Here is part of the "Log" automatically saved by MPLAB:
 

 0.165855: RX  2 @ 40:
    13 FF                                              ..             
 0.197849: RX  2 @ 42:
    13 FF                                              ..             
 0.197857: RX  2 @ 44:
    11 FF                                              ..             
 0.197862: RX  2 @ 46:
    60 00                                              `.             
 0.198059: TX 96 @ 64:
    80 01 00 00 00 00 00 00 4A 02 04 00 00 00 20 1D    ........J..... .
    00 10 1C 00 14 1C 00 18 1C 00 1C 1C 00 20 1C 00    ............. ..
    20 1D 00 20 1D 00 24 1C 00 28 1C 00 2C 1C 00 30     .. ..$..(..,..0
    1C 00 34 1C 00 38 1C 00 3C 1C 00 2C 02 00 44 1C    ..4..8..<..,..D.
    00 48 1C 00 4C 1C 00 50 1C 00 54 1C 00 58 1C 00    .H..L..P..T..X..
    5C 1C 00 20 1D 00 60 1C 00 64 1C 00 20 1D 00 68    \.. ..`..d.. ..h
 0.223375: COM read error: bytesLeft = 2, chunkSize = 2, ret = 0, *writePtr = 0x00, flags = 0x00000008
    comStat = {cbInQue = 0, cbOutQue = 0, fCtsHold = 0, fDsrHold = 0, fEof = 0, fRlsdHold = 0, fTxim = 0, fXoffHold = 0, fXoffSent = 0}
    Retrying read call
 0.243951: RX  1 @ 48:
    80                                                 .              
 2.173454: RX  1 @ 49:
    00                                                 .              
 2.173713: TX 128 @ 160:
    1C 00 6C 1C 00 70 1C 00 74 1C 00 78 1C 00 7C 1C    ..l..p..t..x..|.
    00 80 1C 00 84 1C 00 88 1C 00 8C 1C 00 90 1C 00    ................
    36 02 00 40 02 00 9C 1C 00 A0 1C 00 A4 1C 00 A8    6..@............
    1C 00 AC 1C 00 B0 1C 00 B4 1C 00 B8 1C 00 BC 1C    ................
    00 C0 1C 00 C4 1C 00 C8 1C 00 CC 1C 00 20 1D 00    ............. ..
    D0 1C 00 D4 1C 00 D8 1C 00 DC 1C 00 E0 1C 00 E4    ................
    1C 00 E8 1C 00 EC 1C 00 F0 1C 00 F4 1C 00 F8 1C    ................
    00 20 1D 00 20 1D 00 FC 1C 00 00 1D 00 04 1D 00    . .. ...........
 2.176581: RX  2 @ 50:
    00 EC                                              ..             
 2.180340: RX  1 @ 52:
    FF                                                 .              
 4.382221: Timeout reading from 'COM3'
 
 
 
By the way, configuring the UART with UEN =2  makes unnecessary to manually set and clear the TX Enable line (Or, at least, it works with my configuration)
#6
HowardSchlunder
MCU16 Applications
  • Total Posts : 744
  • Reward points : 0
  • Joined: 2007/06/14 16:26:19
  • Location: Chandler, AZ
  • Status: offline
Re: EZBL over RS-485 2018/09/19 14:51:07 (permalink)
0
Hello SergioP,
 
The COM read error at time = 0.223375s is a PC-side ezbl_comm.exe fread() call failure against the PC's "\\.\COM3" communications resource path.
 
The Bootloader only transmits 16-bit flow control messages or the 16-bit 0x0000 termination code, followed by a 16-bit status/error code. Right when the PC COM read error occurred, a normal 16-bit flow control message was expected. The 0x00 RX bytes @ offset 49 and 50 are a 0x0000 termination message, and the 0xEC and 0xFF RX bytes @ offset 51 and 52 are the EZBL_ERROR_COM_READ_TIMEOUT error code. The singular 0x80 RX byte @ 48 is not something your EZBL Bootloader ever generated and transmitted (apparent since you have the default 96 byte RX FIFO defined in your Bootloader, which is always the first flow control advertisement after the bootloader has finished erasing or blank checking the existing App flash regions).
 
My conclusion is that you have severe errors occurring on the wire of your communications medium. The PC has lost one or more RX bytes and corrupted 1 or more bits to see the 0x80 orphaned byte, and simultaneously, the Bootloader has lost numerous RX bytes from the PC's transmissions @ 64 and @ 160 in order to timeout expecting more data to arrive. These conditions all point to bus contention on a half-duplex medium. Both nodes are blindly jabbering away, not willing or able to listen to the other node before opening its own mouth and correspondingly hearing truncated garbage when they finish talking and revert to RX mode again.
 
Whatever compensation code you deleted needs to be reinstated. It serves an essential function and is required for compatibility with a half-duplex medium. Without it, the bootloader will transmit when it must instead remain listening. RS-485 does not have hardware carrier sense, so software must implement a stateful algorithm to know when the remote node is able to listen.
 
Since you indicated some kind of failure with the compensation code, you may need to modify it in some way to make it more pessimistic, such as by adding an additional, fixed RX to TX turnaround delay after the expected medium silence. I don't know what kind of RS-485 transceiver you have connected to your PC, but it may require one or two USB polling intervals to elapse before it switches from TX mode back to RX mode. I.e. try adding > 2ms of bootloader pre-TX holdoff in addition to the EZBL_bootCtx.bytesRequested tracking code.
#7
Jim Nickerson
User 452
  • Total Posts : 5283
  • Reward points : 0
  • Joined: 2003/11/07 12:35:10
  • Location: San Diego, CA
  • Status: online
Re: EZBL over RS-485 2018/09/20 06:59:47 (permalink)
0
As Howard states you require enough time for the 485 to switch from Tx to Rx.
I have also found adding "Passive Failsafe bias resistors" help in limiting the false characters when switching from Tx to Rx and the bus is idle as described in this pdf for example http://www.ti.com/lit/an/slyt324/slyt324.pdf
#8
Jump to:
© 2018 APG vNext Commercial Version 4.5