This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C129ENCPDT: UART Transmit Interrupt Not Being Triggered

Part Number: TM4C129ENCPDT
Other Parts Discussed in Thread: EK-TM4C129EXL,

We are running a TM4C129ENCPDT on a EK-TM4C129EXL and trying to send and receive characters between the board and a PC running PuTTY.

We are using the board's ICDI virtual UART, the jumpers are set correctly, and the TivaWare example program at C:\ti\TivaWare_C_Series-2.1.4.178\examples\boards\ek-tm4c129exl\uart_echo is working perfectly. That program does not use interrupts. It just runs in a loop, reads a character from the receiver when one becomes available and writes it back to the transmitter.

Now we want to use our interrupt based code. The receive interrupt is working perfectly. The transmit interrupt is never triggered. Consequently characters are received and processed correctly but characters are never transmitted.

If we take our transmit code out of the interrupt handler and stick it in the main loop of the program, then the program works as expected with interrupt handling of receives but polled handling of transmitting. We need transmitting to be interrupt based as well. Not only that, but because we are not transmitting all the time, only in bursts, we need to enable the transmit interrupt as soon as anything is placed into our software buffer and we need to disable it when our software buffer becomes empty. But right now we're just telling it to always be enabled just to get it working.

We have tried UARTFIFOEnable() and UARTFIFOLevelSet() with various watermark levels but that makes no difference. We have tried UARTFIFODisable() but that doesn't help either.

This is the initialization code:


void SetupUart0(void)
{
    Global.SysClockFreqHz = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), SYSTEM_CLOCK_FREQ_HZ);

    SysCtlPeripheralEnable(GPIO_PORTA_BASE);

    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    while (SysCtlPeripheralReady(SYSCTL_PERIPH_UART0) != true)
    {
    }

    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    UARTConfigSetExpClk(UART0_BASE, Global.SysClockFreqHz, 115200, (UART_CONFIG_PAR_NONE | UART_CONFIG_STOP_ONE | UART_CONFIG_WLEN_8));

    // Set UART to interrupt when Tx FIFO is almost empty or any char received
    //UARTFIFOEnable(UART0_BASE);
    //UARTFIFOLevelSet(UART0_BASE, UART_FIFO_TX1_8, UART_FIFO_RX1_8);

    IntEnable(INT_UART0);
    UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT | UART_INT_TX);
}

The commented code is left over from trying different things with the FIFO.


This is the interrupt handler:


void Uart0InterruptHandler(void)
{
    uint32_t InterruptFlags;

    InterruptFlags = UARTIntStatus(UART0_BASE, false);
    UARTIntClear(UART0_BASE, InterruptFlags);

    if (InterruptFlags & UART_INT_TX)
    {
        // Room in Tx FIFO
        while (UARTSpaceAvail(UART0_BASE))
        {
            UARTCharPutNonBlocking(UART0_BASE, BufGet(&Tx));
        }
    }

   if (InterruptFlags & (UART_INT_RX | UART_INT_RT))
   {
      // Data in Rx FIFO
      while (UARTCharsAvail(UART0_BASE))
      {
         BufPut(&Rx, UARTCharGetNonBlocking(UART0_BASE));
      }
   }
}


As I said, removing the while loop with UARTSpaceAvail() and UARTCharPutNonBlocking() from the interrupt handler and putting it in the main loop makes the code work, but then transmit is polled, not interrupt based.

This is what the registers contain after initialization:

  • In case it matters we are using CCSv7 version 7.1.0.00016 and the toolchains are gcc-arm-none-eabi-4_9-2015q3 and ti-cgt-arm_16.9.3.LTS. The program behaves identically under both toolchains.
  • I don't see anything here to start the transmit.

    Robert
  • How is that done? I'm new to TM4C.

    The platforms I'm familiar with work this way: as soon as you enable the transmit interrupt, the transmit interrupt is asserted because the FIFO is empty or below the watermark. The transmit interrupt handler puts as many bytes as it can into the FIFO. If the FIFO fills up, the transmit interrupt handler exits and waits for the interrupt to be asserted again. If the transmit interrupt handler runs out of bytes to put in the FIFO, it disables the transmit interrupt. Later, when software calls a "send" function to send more data, that send function enables the transmit interrupt, starting the process again.

    It seems that things work differently in TM4C. What additional step must we take to tell it to begin transmitting?
  • There are two kinds of UARTs out there (Originating IIRC with Motorola and National Semiconductor). Ones that use a level sensitive interrupt as you've described (interrupt always active if transmission possible, interrupt must be continuously enabled/disabled) and ones that use edge triggered interrupt (interrupt fires only once when room to send, transmission must be started by the sending process). The latter kind is actually more numerous. The level triggered UART interrupt style was only common at Motorola.

    The way you deal with edge triggered interrupts is to start the transmission when necessary. Usually called "priming the pump" for obvious reasons.

    So in transmit interrupt

    •  if queue is empty raise flag
    •  else fill FIFO from queue

    In transmit routine

    • Place string in queue
    • If queue is full or string fully placed in queue check flag
    • If flag set the clear flag and place the first byte in the UART FIFO

    There is an old DOS reference "C Programmer's Guide to Serial Communications" by Joseph Campbell that cover this well. Not much good for PCs anymore but still a good embedded reference.

    Robert

  • Hi,
    Somehow you need to kick start the UART to generate the interrupt. You will need to start off with some writes to the FIFO (if you are using the FIFO feature) with the desired interrupt FIFO Level Select. For example, you can configure with 1/8 the FIFO level. This means you need to kick start the UART in your main with two data written to the FIFO. Once two data are written to the FIFO, the UART will generate interrupt. In the ISR you can write subsequent data to the FIFO. If you don't do this, you are in a chicken and egg situation. There is no data in the FIFO and hence there is no interrupt.

      There is also an UART interrupt based interrupt in the workshop Lab12.

      Please also make sure have:

      IntMasterEnable(); // enable processor interrupts

  • Charles Tsai said:
    For example, you can configure with 1/8 the FIFO level. This means you need to kick start the UART in your main with two data written to the FIFO. Once two data are written to the FIFO, the UART will generate interrupt

    One should be suffient should it not Charles? At least on every UART I've worked with and w/o reviewing I'm pretty sure my current TM4C code does it this way as well. The UART generates an interrupt not only when dropping below the FIFO level but also on FIFO empty.

    Robert

  • Hi Robert,

     Thanks. According to the datasheet,  in FIFO mode the interrupt is generated after the programmable trigger level of data is written. If FIFO is disabled and if no data is present in the transmitter then interrupt is also generated. Below is the excerpt from the datasheet.

    The transmit interrupt changes state when one of the following events occurs:

    ■ If the FIFOs are enabled and the transmit FIFO progresses through the programmed trigger

    level, the TXRIS bit is set. The transmit interrupt is based on a transition through level, therefore

    the FIFO must be written past the programmed trigger level otherwise no further transmit interrupts

    will be generated. The transmit interrupt is cleared by writing data to the transmit FIFO until it

    becomes greater than the trigger level, or by clearing the interrupt by writing a 1 to the TXIC bit.

    ■ If the FIFOs are disabled (have a depth of one location) and there is no data present in the

    transmitters single location, the TXRIS bit is set. It is cleared by performing a single write to the

    transmit FIFO, or by clearing the interrupt by writing a 1 to the TXIC bit.

  • Well, that's broken. Glad you brought it to my attention. There is a bit of a work-around using an EOT trigger.

    Robert
  • Thank you for your very clear answer. Knowing about the two types (edge vs level triggered transmit interrupts) certainly helped me to understand what is going on here. After reading your explanation I noticed that TivaWare comes with a uartstdio module (under TivaWare_C_Series-2.1.4.178\utils) and sure enough it includes a function UARTPrimeTransmit() that stuffs as many bytes as it can into the transmit FIFO.

    Unfortunately, though, when the FIFO is being used (UARTFIFOEnable() has been called) then as Charles Tsai points out it seems that one byte is not enough to get the thing going. I have it set up with 1/8 FIFO level and it didn't work. It only worked once I put at least 2 bytes, and if you use higher FIFO levels then you will need to put more bytes to kick start the UART. With the FIFO turned off (UARTFIFODisable()) one character is enough because one character is all the transmit buffer you have.

    I got this to work, but I said "unfortunately" because this makes the code ugly!

    We ported over our library that we developed in the past. It worked great on several 8-, 16- and 32-bit parts. The way it works is that all send operations eventually boil down to a single function that enqueues one character at a time into our software queue. It's done this way so that we can encapsulate our lockless ISR-safe queue code in one location and then all other code that needs to send data, whether it's printf() or anything else, just calls this function for each character and doesn't need to worry about it. But this means that our UART library doesn't know when a complete "string" has been enqueued. It only knows that there's a character to send, and it has no idea if more are coming. With the level triggered UARTs of those systems the byte enqueueing function would just make sure that the transmit interrupt was enabled and the interrupt handler would disable it if no more bytes in queue. Now, with this edge triggered scenario, and only because it requires more than one byte to prime the pump, this means that we cannot do the priming in our character enqueueing function, because it doesn't know if enough characters are coming. If you send short messages, they are not sent completely until more bytes need to be sent, which could be much later. So, this in turn means that we need to check for priming and do the FIFO stuffing in our main loop, constantly polling the flag and stuffing the FIFO if necessary. So, as a result, our implementation ends up being both polled and interrupt-based, with duplication of the functionality in two places. Because the main code ends up doing half the work anyway, I don't see any advantage to using the transmit interrupt at all. Well, there's an advantage when you send very long strings or a whole file. In the uartstdio module I mentioned, they still turn the TX interrupt on and off, in addition to all the other management you have to do around the FIFO, so I went ahead and did that as well. It seems to work correctly now.

    Thanks again
  • Beyond "broken" - that is one of the most tortured writings (yet) presented.
    Multiple "gotchas" behind each/every "bend" - just awaiting our (slight) misstep!

    While it has been mentioned (just here/now) that there is an, "Interrupt based, UART example w/in "Workshop Lab 12" - can the, "Cry here for a competent, more (i.e. somewhat) publicized/findable, code example" be denied?

    Not the fault of those (now) here - yet this should have reached (someone's) "To Do" list.     (Avoidance is not always the "best" option.)

  • Yep, and this peripheral appears designed to work for a minority of cases and as twelve12 pointed out not for the most common usage. At least the receive timeout oddity is easily dealt with and you could make the pretence of it being useful even if it catches almost everyone off guard.

    Both break the 'principle of least surprise'

    Robert
  • cb1_mobile said:
    can the, "Cry here for a competent, more (i.e. somewhat) publicized/findable, code example" be denied?

    Indeed, in fact it such an example should be able to describe how to deal with the following situation

    • 4 byte FIFO threshold
    • Two bytes are written to the FIFO
    • Some unknown number of character times later 3 bytes are written to the FIFO
    • Questions: will the transmit interrupt ever fire? How do we know?

    Robert

  • Uh oh, what is the "receive timeout oddity"?! And how do I avoid it?
  • At least the receive timeout oddity is simple to deal with.

    Unlike every other UART I've ever used TI separated the receive timeout into a separate interrupt distinct from the receive interrupt. All you have to do is treat it as a receive interrupt.

    IE
    
    
    switch (status)
    
        case receive_timeout:
        case receive:
              ...
              break;
    

    so simple, just unexpected.

    I've seen no useful application of that separation, although there has been some attempt at justification.

    Robert

  • Robert Adsett said:
    Well, that's broken. Glad you brought it to my attention. There is a bit of a work-around using an EOT trigger.

    Indeed it looks like the only way to fix this is to change the transmit interrupt to trigger on end of transmission. Likely to make the traffic a little bursty but at lest you shouldn't miss transmit interrupts and you can still use the receive FIFO.  So far I've not seen anything else with any guarantee of working.

    Robert

  • Interesting. I think I saw that in the example code and implemented mine the same way. Thanks for clearing that up :-)
  • If there's an additional interrupt that you can get at end of interrupt, then is it possible to enable both interrupts and give them the same treatment, the same way as the "receive oddity"?
  • twelve12pm said:
    If there's an additional interrupt that you can get at end of interrupt, then is it possible to enable both interrupts and give them the same treatment, the same way as the "receive oddity"?

    Not that I can see. The EOT interrupt replaces the FIFO threshold interrupt. If it was in addition it would solve most of the problem. As it is it's the only thing I can see that will work but there will be a small performance degradation. In many applications that is not likely to matter.

    Maybe if I'm overlooking an interrupt?

    Robert

  • Is it somehow true that we "inmates" (appear) to be (somewhat) running the asylum?        Can that prove optimal?      

    Dare it be asked, "Wherefore art the, "UART Architecture/Management" experts?"

  • Foiled again!

    More importantly, have you used DMA with the UARTs on TM4C? The code on UART0 is just for diagnostics. But we're getting ready to spin a board with multiple TM4C that communicate with each other through the UARTs. I'm not sure of the exact numbers right now but I think they'll exchange a kilobyte of data with each other 100 times per second, and I'm hoping to run the UARTs at 4 MHz. If it doesn't work efficiently, then I guess the whole design is out the window.
  • Hi Robert,
    When EOT bit is set in the UARTCTL register the transmit interrupt is generated once the FIFO is completely empty and all data including the stop bits have left the shift register. For EOT interrupt to generate, you still need to first write one data to the FIFO in the main. What I wanted to say is that you can't wait for EOT interrupt to happen without first having some data written to the FIFO. But you are right this is another way to continue the interrupt-based re-transmission by using EOT instead of FIFO threshold.
  • twelve12pm said:
    More importantly, have you used DMA with the UARTs on TM4C?

    Nope, given the nature of serial communication I've never seen a case where it made sense (consider what happens if a byte is dropped or added).

    twelve12pm said:
    But we're getting ready to spin a board with multiple TM4C that communicate with each other through the UARTs. I'm not sure of the exact numbers right now but I think they'll exchange a kilobyte of data with each other 100 times per second, and I'm hoping to run the UARTs at 4 MHz.

    What kind of data? And are you sure you can get 4MHz transceivers?

    Robert

  • Charles Tsai said:
    When EOT bit is set in the UARTCTL register the transmit interrupt is generated once the FIFO is completely empty and all data including the stop bits have left the shift register.

    Yes. All other FIFOed UARTs that I've ever used provide an interrupt that occurs upon FIFO emptying. TI is the exception in not providing such.

    Charles Tsai said:
    . For EOT interrupt to generate, you still need to first write one data to the FIFO in the main.

    Of course, like all other edge triggered UARTS

    Charles Tsai said:
    But you are right this is another way to continue the interrupt-based re-transmission by using EOT instead of FIFO threshold.

    As near as I can tell, it's the only way to do so safely. At least no other safe method has been presented.

    Robert

  • They're on the same board with relatively short traces so they will not be going through transceivers. The data will be commands, responses, and other state information sent between the multiple processors. There will have to be a way to detect errors, at the very least a CRC32. All I know right now is that it will certainly require a lot of testing and if it's not reliable at 4 MHz then we will have to lower it. I think it should be reliable because we've done faster interprocessor communication in the past, though that was with SPI and on other MCUs.
  • I was thinking more about the type of data and commands and the amount of duplication. That bandwidth more than saturates CAN but if there was enough duplicate traffic it might fit. I've also heard of people using Ethernet for this sort of traffic.

    I'd wonder though if the interface to use isn't FIFO's or maybe even shared memory. You should get a lot of bandwidth that way but you do have to pay for the FIFO ICs.

    Explains why you are not worried about transceivers although I'd want to check the overhead.

    Robert

  • We used FIFOs in the past. On one project we tried to use shared dual core memory. It never worked correctly but that's because the project got scrapped early on. In any event, dual core memory is costly and any external memory uses up a lot of MCU pins for all the address, data, and control pins, so that drives up the costs even more. The TM4C129ENCPDT datasheet says you can crank up the UARTs to 7.5MHz (or 15 MHz for "high speed") and we're only traveling a couple of inches... If we had to go across the room or farther then I would go directly with ethernet. I'm hoping that we won't use up all the CPU bandwidth just for communication! It will certainly be interesting to see how it works. I was deliberately thinking of going 4 MHz because that's (a bit more than) half of the maximum, and we've done SPI at that speed (and faster) quite reliably.
  • twelve12pm said:
    ...datasheet says you can crank up the UARTs to 7.5MHz

    Yet - w/out mention of any added "error rate" - or (actual) operating conditions - or data payload - such claim may not prove fully compelling.

    Having past served as Engineering Mgr. at a "similar, semi-giant" - it was noted that such, "optimistic findings" resulted when the, "Remote Device was (perfectly) "dialed-in" (i.e. Frequency painstakingly set) - and that scenario will NOT overlap yours.      In addition - in the real-world - might one MCU "run fast" - the other "slow" - what then?     (such frequency errors ARE additive)       

    And - the deviations enforced by "PVT" (Process Changes, Voltage, Temperature) and simple aging - must be anticipated - and guard-banded.     All this cries for a (more) conservative approach - and solid test/verification.

    At 4MHz we've found board layout to be important - short, direct routes and good "separation" from other potentially impacting signals - all "push you toward success."     Yet "Caution" should sit close by,  data integrity should trump high speed.

    Note too - that the (lesser) MCU here (4C123) most always requires a "pull-up R" upon the TX line - which may impact max speed...     (and such need may overflow to your device, as well...)

  • @cb1_mobile, I am very glad you mentioned about the pull-up resistors. I spoke to the designer about that and it turns out that we have that in our design, 10k to VCC. The TX of one MCU is the RX of the other, so in effect both lines have pull-ups. He said he's placing each pull-up closest to the RX pin that it's going into.

    Now, at least in my mind, it seems to me that the UART should be more reliable than SPI, because the UART samples each bit 16 times whereas SPI (as far as I know) only samples each bit once. The UART also provides framing for each byte, so more opportunities to detect errors. But that's just me. Maybe SPI contains a different kind of magic smoke.
  • twelve12pm said:
    the UART samples each bit 16 times

    May I disagree - I believe (just) 3 such "Voting Samples" are the "chosen ones."      (this topic came up recently (here) - I recalled that "3" number - and such was (finally) agreed upon.)

    https://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/588322?tisearch=e2e-quicksearch&keymatch=UART%20bits

    You "seize upon" pull-up - yet your "Need for speed" - minus well chosen "cautions" - appears to pass w/little concern - that alarms...

    As to UART (Async) vs SPI (Sync) I'd vote for clocked data (almost) every time.      Recall in my earlier post - the possibility that one of your interconnected MCUs runs "fast" - the other, "slow!"      How can that increase reliability?       The fact that an "extra" signal line (i.e. the data clock) is tolerated must signal some advantage w/ "SPI" - that being, "Signal Reliability/Robustness!"     

    You may glean some insight into the "Value of SPI vs. UART" by surveying the occurrence of "accessory ICs" - servicing each serial format.       SPI - overwhelmingly - Wins!      (so much so that  "Slaughter Rule" is often called...)

    I should note that firm/I are "fans" of UART - especially so when the communication flows (beyond) the board - and proper transceivers/level shifters (even radio modules) are deployed.

  • The advantage of SPI is not robustness. It is, if anything, less robust. The advantage of SPI for a slave device is the simplicity of a simple clocked shift register gated by a chip select. A UART interface would need for the slave device to have a clock, natural for a micro but not for many slave devices.

    Somewhat perversely that simplicity of the chip select has made microprocessor implementations of SPI more complex than UART implementations.

    Robert
  • Yep, usually either 3 or 1 voting time. The 16x comes from the 16x baud rate clock used to find those 3 centre time points to sample.

    Robert
  • twelve12pm said:
    I don't see any advantage to using the transmit interrupt at all.

    Just adding my two cents to this long conversation...

    The way we've implemented, and as I understand it, the buffer transmission controller must be aware of:

    - Whether there are still bytes to be transmitted (from the buffer to FIFO)

    - If there is room to place bytes into the FIFO

    So I find no need for an EOT interrupt!

    Let's say you have 3 bytes to transmit, and the FIFO is empty. The transmission controller would do something as while(FIFO_HAS_ROOM_AVAILABLE & THERE_ARE_BYTES_TO_BE_SENT);

    The three bytes will be sent immediately, end of story. That would work for 1 byte, 16 bytes, whatever. The loop would exit because the buffer was fully transmitted, hence there is nothing more to be worried on the application end, and it can assume "everything has been transmitted". Do you really need to know, here, that the last byte actually left the hardware? The correct transmission requires other approaches, acknowledge protocols and so on.

    Now, let's say you have 25 bytes. The first 16 bytes will be sent right away, and you can go back to whatever your MCU needs to do - you will get an interrupt after "some" of those bytes left the FIFO (let's say half of them, on default configuration). When you get that interrupt, you have some time to service it, simply by calling that "controller" again with the very same logic. By the time you start sending the remaining 9 bytes, there's a good chance that one extra byte left the hardware, so all 9 will be gone on the second call. If not, no problems - the last byte will simple be sent on the third call of the controller.

    Fine tune the FIFO level for interrupt. If you set the interrupts right after 2 bytes left the FIFO, you will be calling the controller too often. If you set it to interrupt after 14 bytes left, you will have the benefit of "a lot of FIFO space" on your next controller call - but if your code is busy doing something else, maybe you will waste transmission time because the last 2 bytes got sent before you were able to send more.

    This is easier to be explained with flowcharts, but I hope the text makes sense. We found that such strategy provides a VERY reliable UART transmission, does not mess with the receiving, and does not demand too much processing.

    Bruno

  • Bruno Saraiva said:

    The way we've implemented, and as I understand it, the buffer transmission controller must be aware of:

    - Whether there are still bytes to be transmitted (from the buffer to FIFO)

    It's this part that I think is, in general very difficult to impossible to do. In fact the only way I can see to do it in general would be to add a timer to count down the time that the characters would stay in the FIFO. relying on probabilities just leaves you with difficult to track bugs.

    Bruno Saraiva said:
    - If there is room to place bytes into the FIFO

    This part is trivial

    Bruno Saraiva said:
    Let's say you have 3 bytes to transmit, and the FIFO is empty.

    First question, how do you know the FIFO is empty?

    Bruno Saraiva said:
    The three bytes will be sent immediately, end of story

    Only the start of the story. Second question when/will an interrupt fire? If you add more characters to the queue do you need to send them to the FIFO or can you rely on the interrupt?

    Bruno Saraiva said:
    . Do you really need to know, here, that the last byte actually left the hardware? The correct transmission requires other approaches, acknowledge protocols and so on.

    How else do you know when to add more bytes to the FIFO?

    Bruno Saraiva said:
    Now, let's say you have 25 bytes. The first 16 bytes will be sent right away, and you can go back to whatever your MCU needs to do - you will get an interrupt after "some" of those bytes left the FIFO (let's say half of them, on default configuration

    Yes

    Bruno Saraiva said:
    When you get that interrupt, you have some time to service it, simply by calling that "controller" again with the very same logic. By the time you start sending the remaining 9 bytes, there's a good chance that one extra byte left the hardware, so all 9 will be gone on the second call.

    Relying on probability does not, in general, lead to robust software.

    Bruno Saraiva said:
    If not, no problems - the last byte will simple be sent on the third call of the controller.

    Good, now what happens if you queue up an additional 3 bytes immediately after the last has left your queue?

    What happens if you queue up 3 bytes immediately after the FIFO interrupt fires?

    I think I see a way or two clear of this but it'll need some care to prevent race conditions.

    Robert

  • Robert Adsett said:
    (- Whether there are still bytes to be transmitted (from the buffer to FIFO)). It's this part that I think is, in general very difficult to impossible to do.

    Not at all. You misread. Controller needs to know if there are pending bytes from BUFFER, which need to be placed in FIFO. After they're in FIFO, mission is accomplished. So this is just a matter of buffer pointers.

    Robert Adsett said:
    First question, how do you know the FIFO is empty?

    It was empty by definition for the sake of the example. To know that the FIFO is empty we have this weird thing, which I don't even want to think of re-understanding:

    (!(HWREG(Serial_UART[uartid][SERIAL_BASE]+UART_O_FR) & UART_FR_TXFF))

    Robert Adsett said:
    How else do you know when to add more bytes to the FIFO?

    You can add more bytes to the FIFO immediately after you get a TX interrupt! Again, you don't need to know if all bytes are gone through the wire.

    Robert Adsett said:
    Good, now what happens if you queue up an additional 3 bytes immediately after the last has left your queue?

    You never queue 3 bytes, or 8 bytes, or whatever bytes. You queue one by one, while there is room in the FIFO. And you control the pointers of your buffers: one points to what has already been sent to FIFO, the other points to the last BYTE that needs to be sent.

    Robert Adsett said:
    Relying on probability does not, in general, lead to robust software.

    That was a mean comment! You wrote before reading the rest!!! Objection!!!!

    Hope these additional comments cleared things a bit!

    Bruno

  • Bruno Saraiva said:

    It was empty by definition for the sake of the example. To know that the FIFO is empty we have this weird thing, which I don't even want to think of re-understanding:

    (!(HWREG(Serial_UART[uartid][SERIAL_BASE]+UART_O_FR) & UART_FR_TXFF))

    Isn't there some nice neat TivaWare function that does that?

  • Bruno Saraiva said:
    It was empty by definition for the sake of the example.

    That was rather my point. You must consider both the empty and the non-empty condition. You only considered the empty condition.

    Bruno Saraiva said:
    To know that the FIFO is empty we have this weird thing, which I don't even want to think of re-understanding:

    That's what determines whether or not an interrupt will occur so that condition and its effect absolutely must be considered.

    Bruno Saraiva said:
    Robert Adsett
    Relying on probability does not, in general, lead to robust software.

    I did.

    Bruno Saraiva said:
    Robert Adsett
    Good, now what happens if you queue up an additional 3 bytes immediately after the last has left your queue?

    That doesn't answer the question. It sidesteps it w/o addressing it.

    I mentioned I thought I saw a way clear and I think I do. It does take some work to avoid races and that increases latency considerably compared to a conventional UART FIFO

    First start with the conventional UART

    Application code

    TransmitByte( char byte)
    {
    
    Add byte to queue
    if PrimePumpFlag set {
        disable interrupt
        place byte in UART FIFO
        clear PrimePumpFlag
        enable interrupt
        }
    }

    There are cases where you could dispense with the interrupt protection around the priming. If you want to be safe against future changes though, then even if that case applies now the interrupt protection should still be present. Note that the order of operations is important here.

    Interrupt Code

    UARTTransmit()
    {
        if ! queue_empty {
             copy queue to FIFO until queue empty or FIFO full
             }
        else {
             Set PrimePumpFlag
             }
    }

    This will not work with the TI UART since in the case where the UART FIFO is not filled past the FIFO threshold there will be no interrupt and the PrimePumpFlag will never be set so transmission is never re-started.

    The easy safe way to fix this is to convert the transmit interrupt to an EOT interrupt with some attendant loss of bandwidth. I think I have a better approach though.

    Starting with the interrupt code. It actually simplifies since there is no way to set the priming flag.

    UARTTransmit()
    {
        if ! queue_empty {
             copy queue to FIFO until queue empty or FIFO full
             }
    }

    The application code now needs additional protection and is at least arguably more complex

    TransmitByte( char byte)
    {
    
    disable interrupt
    if queue is empty  and !FIFO is full {
        place byte in UART FIFO
        }
    else {    
        Add byte to queue
        }
    enable interrupt
    }

    The expanded protection period is necessary to prevent race conditions. I don't think I've left any race conditions but it needs review, I've only done a preliminary check on it.

    Two side effects of this

    1. increased latency
    2. increased number of transmit interrupts with nothing to do.

    A note on the interrupt protection, as long as the transmit never comes from a higher priority than the transmit interrupt then the disabling only needs to block the transmit interrupt. If not the disable must be more inclusive and additional blocks are needed in the interrupt code as well.

    Robert

  • I don't actually think so!!!
    If I recall, this xmas-tree here was copied from within the UartCharPut() function. Because this one is blocking, it has to check for FIFO space, and this is what was used by TivaWare authors.
  • Bruno Saraiva said:
    Because this one is blocking, it has to check for FIFO space

    That just needs a FIFO full check. See UARTSpaceAvail. Should boil down to a simple flag check.

    Robert

  • cb1_mobile said:

    You "seize upon" pull-up - yet your "Need for speed" - minus well chosen "cautions" - appears to pass w/little concern - that alarms...

    As to UART (Async) vs SPI (Sync) I'd vote for clocked data (almost) every time.      Recall in my earlier post - the possibility that one of your interconnected MCUs runs "fast" - the other, "slow!"      How can that increase reliability?       The fact that an "extra" signal line (i.e. the data clock) is tolerated must signal some advantage w/ "SPI" - that being, "Signal Reliability/Robustness!"     

    Now I'm nervous!

    Point taken about the advantages of synchronous communication. (Incidentally, what do you think of a USART? That can give you framing AND a clock! Too bad TM4C doesn't have that.)

    I have to study this further and run some tests...

  • twelve12pm said:
    Incidentally, what do you think of a USART? That can give you framing AND a clock!

    Really, got a pointer? All the USARTs I've seen do not provide any framing in synchronous mode (that'd be a bit oxymoronic I think). They were just a way of providing an SPI like interface.

    Robert

  • For some reason my eyes have not found UARTSpaceAvail.

    Either way, the content is the same:

    UARTSpaceAvail:
    return((HWREG(ui32Base + UART_O_FR) & UART_FR_TXFF) ? false : true);

    The above mentioned code:
    (!(HWREG(ui32Base +UART_O_FR) & UART_FR_TXFF))
  • That's not the FIFO empty function you mentioned earlier ;)

    Robert
  • Yes, it is! I just replaced the ui32Base parameter for a clearer reading, for the one was my code's specific variable. Other than that, it is exactly the same register mask to read if the Tx FIFO has available space.
  • Bruno, my last reply was a bit flippant. I just figured you had miss-written.

    However,  FIFOEmpty and SpaceAvailable are quite different things. In fact you can make the case (true but probably not useful) that Space Available more closely matches SpaceUsed than it does FIFOEmpty. Consider for a 16 byte FIFO

    • Space Available 1 to 16 bytes in FIFO are available
    • FIFO Empty 16 bytes in FIFO available
    • Space used 0 to 15 bytes in FIFO available
    • FIFO Full 0 bytes available

    and the logical inverse relationships

    • Space Available = !FIFO Full
    • Space Used = !FIFO Empty

    FIFO Full is the flag that's readily available and it's the one you check.

    I suspect it's possible to approximate FIFO empty by checking for transmit holding register being empty but I have not verified that flag is available.

    Robert