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.

[参考译文] MSPDRIVERLIB:基于中断的 UART TX 缺少第一个字符

Guru**** 2378650 points
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1486537/mspdriverlib-interrupt-based-uart-tx-missing-the-first-character

器件型号:MSPDRIVERLIB

工具与软件:

您好!

我目前正在实现基于中断的 UART API、以使用 DriverLib 函数处理发送和接收字符串。

由于某种原因,当我使用uart_puts()函数后uart_putc(),我正在传输的字符串的第一个字符uart_puts()丢失。(当我打印多个字符串时uart_puts()没有前面的uart_putc(),所以我怀疑中的一些操作uart_putc()可能会影响中的后续操作 uart_puts().)

请查找下面介绍的测试代码、API 实现和结果:

这里是 main ()中的测试代码。

#include "ti_msp_dl_config.h"
#include "uart.h"

static void sys_init(void);

int main(void)
{
    sys_init();

    uart_putc('c');
    uart_puts("123\r\n");
    uart_puts("456\r\n");

    while (1) 
    {
        /* Do nothing. */
    }
} /* main() */

/*!
 * @brief Initializes the system.
 */
void sys_init(void)
{
    SYSCFG_DL_init();
    uart_init();
} /* sys_init() */

以下是自定义'uart_putc ()'和'uart_puts ()'函数的实现方式:

#include "uart.h"
#include "ti_msp_dl_config.h"

static volatile const char *       g_p_char           = NULL;
static volatile uart_comm_status_t g_uart_comm_status = UART_STATUS_INIT;
static volatile char g_rx_char = 0;

/*!
 * @brief Performs any additional initializations necessary for the UART
 * peripheral.
 *
 * @par
 * The UART peripheral initialization is performed by the initialization
 * functions automatically generated by the sysconfig tool. See
 * 'ti_msp_dl_config.c' for more details.
 */
void uart_init(void)
{
    /* Disable interrupts and clear interrupt statuses until necessary. */
    DL_UART_Main_disableInterrupt(UART_COMM_INST, DL_UART_MAIN_INTERRUPT_TX);
    DL_UART_clearInterruptStatus(UART_COMM_INST, DL_UART_MAIN_INTERRUPT_TX);
    DL_UART_Main_disableInterrupt(UART_COMM_INST, DL_UART_MAIN_INTERRUPT_RX);
    DL_UART_clearInterruptStatus(UART_COMM_INST, DL_UART_MAIN_INTERRUPT_RX);

    /* To prevent any unexpected pending IRQ from triggering an interrupt when
     * enabling the IRQ, clear the pending IRQ before enabling it. */
    NVIC_ClearPendingIRQ(UART_COMM_INST_INT_IRQN);
    NVIC_EnableIRQ(UART_COMM_INST_INT_IRQN);
} /* uart_init() */

/*!
 * @brief Sends a character over UART.
 *
 * @par
 * This function by itself does not trigger the UART TX interrupt.
 */
void uart_putc(const char c)
{
    /* Wait until the TXDATA register is empty and there is no ongoing data
     * reception before proceeding with transmission. */
    while (DL_UART_Main_isBusy(UART_COMM_INST))
    {
        /* Do nothing. */
    }

    DL_UART_Main_transmitData(UART_COMM_INST, c);
} /* uart_putc() */

/*!
 * @brief Sends a null-terminated string over UART.
 * @param[in] p_str A pointer to a null-terminated string to be transmitted over
 * UART.
 */
void uart_puts(const char * p_str)
{
    /* Enable the interrupt to perform data transmission over UART using
     * interrupts. */
    DL_UART_Main_enableInterrupt(UART_COMM_INST, DL_UART_MAIN_INTERRUPT_TX);

    /* NOTE: Ensure that the 'g_p_char' setting is performed after enabling the
     * TX interrupt. This is because, when the TX interrupt is enabled while
     * the TXDATA register of the UART peripheral is empty, the TXINT bit will
     * be set and trigger the interrupt immediately. For the interrupt handler
     * to handle this case 'g_p_char' must be a null pointer. */
    g_p_char           = p_str;
    g_uart_comm_status = UART_STATUS_TX_STARTED;

    /* Wait until the TXDATA register is empty and there is no ongoing data
     * reception before proceeding with transmission. */
    while (DL_UART_Main_isBusy(UART_COMM_INST))
    {
        /* Do nothing. */
    }

    /* Initiate data transmission. */
    DL_UART_Main_transmitData(UART_COMM_INST, *g_p_char);

    /* Wait for the entire transmission to complete. */
    while (g_uart_comm_status != UART_STATUS_TX_COMPLETE)
    {
        /* Do nothing. */
    }
} /* uart_puts() */

/*!
 * @brief Interrupt handler for UART_COMM instance.
 *
 * @par
 * This handler processes UART interrupts based on the type of interrupt pending
 * (RX or TX). For TX interrupts, it handles the transmission of the next
 * character or completes the transmission when the end of the string is
 * reached.
 */
void UART_COMM_INST_IRQHandler(void)
{
    /* Check the pending interrupt type for the UART instance. */
    switch (DL_UART_Main_getPendingInterrupt(UART_COMM_INST))
    {
        case DL_UART_MAIN_IIDX_RX:
        {
            if (UART_STATUS_RX_STARTED != g_uart_comm_status)
            {
                /* Interrupt or user input(s) uncalled for. Ignore. */
                return;
            }

            g_rx_char = DL_UART_Main_receiveData(UART_COMM_INST);

            DL_UART_Main_disableInterrupt(UART_COMM_INST,
                                          DL_UART_MAIN_INTERRUPT_RX);
            g_uart_comm_status = UART_STATUS_RX_COMPLETE;

            break;
        }
        case DL_UART_MAIN_IIDX_TX:
        {
            if (NULL == g_p_char)
            {
                /* No character is pending to be transmitted, return. */
                return;
            }

            g_uart_comm_status = UART_STATUS_TX_INPROGRESS;

            /* Proceed to the next character to send. */
            g_p_char++;

            if (*g_p_char != '\0')
            {
                /* Wait until the TXDATA register is empty and there is no ongoing data
                 * reception before proceeding with transmission. */
                while (DL_UART_Main_isBusy(UART_COMM_INST))
                {
                    /* Do nothing. */
                }

                /* There are more characters to send, transmit the next one. */
                DL_UART_Main_transmitData(UART_COMM_INST, *g_p_char);
            }
            else
            {
                /* If the string is finished, disable TX interrupt and update
                 * status. */
                DL_UART_Main_disableInterrupt(UART_COMM_INST,
                                              DL_UART_MAIN_INTERRUPT_TX);
                g_p_char           = NULL;
                g_uart_comm_status = UART_STATUS_TX_COMPLETE;
            }
            break;
        }
        default:
            break;
    }
} /* UART_COMM_INST_IRQHandler() */

结果如下:

1和4缺失。

c23
56

当我在看:

c123
456

如果有人能帮助我理解我在使用 DriverLib 函数时存在任何错误、我将不胜感激。

谢谢!

李京宰

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    UART_PUTs()开头的块注释讨论了立即触发 TXINT、但没有考虑到 TXINT 不立即触发的情况。 这是你通过首先调用 uart_putc()触发的,所以当 uart_puts()开始时,前一个字节仍然在飞行中,这将状态机设置为1次关闭。

    然后, TXINT 会在 isBusy()循环中稍后(可能)触发,因此在 isBusy()返回 false 之前,整个字符串(从[1]开始)会被发送。 然后 UART_puts()写入*g_p_char (* NULL == 0x00通常)、然后返回(TX_COMPLETE)。 由于0x00在下一个 uart_puts()调用中正在飞行,该过程将重复。  

    或许最简单的展开方法可能是让 uart_puts ():[a]设置 g_p_char [b]写入第一个字节[c]启用 TXINT。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Bruce、您好!

    我感谢澄清。 我想这也解释了我观察到的情况。 按如下方式对序列重新排序解决了问题:

    /*!
     * @brief Sends a null-terminated string over UART.
     * @param[in] p_str A pointer to a null-terminated string to be transmitted over
     * UART.
     */
    void uart_puts(const char * p_str)
    {
        g_p_char           = p_str;
        g_uart_comm_status = UART_STATUS_TX_STARTED;
    
        /* Wait until the TXDATA register is empty and there is no ongoing data
         * reception before proceeding with transmission. */
        while (DL_UART_Main_isBusy(UART_COMM_INST))
        {
            /* Do nothing. */
        }
    
        /* Initiate data transmission. */
        DL_UART_Main_transmitData(UART_COMM_INST, *g_p_char);
    
        /* NOTE: The TXINT bit of the UART will be set as soon as the first
         * character of the string is sent and the TXDATA register becomes empty. To
         * trigger the pending interrupt and transmit the rest of the characters,
         * enable the TX interrupt. */
        DL_UART_Main_enableInterrupt(UART_COMM_INST, DL_UART_MAIN_INTERRUPT_TX);
    
        /* Wait for the entire transmission to complete. */
        while (g_uart_comm_status != UART_STATUS_TX_COMPLETE)
        {
            /* Do nothing. */
        }
    } /* uart_puts() */
    

    谢谢!

    李京宰