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.

TM4C129LNCZAD: Switching UART2 Pins

Part Number: TM4C129LNCZAD

I am trying to switch the GPIO pins of UART2 so I can send and receive data on PA6,7 and switch to pins PD4,5 to send and receive data.

void switch_uart2(bool mode)
{

    if (mode == 0)
    {
        //Computer Pins
        //UART 2 Config:
        ROM_GPIOPinConfigure(GPIO_PA6_U2RX);
        ROM_GPIOPinConfigure(GPIO_PA7_U2TX);
        ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7);
    }
    else
    {
        //Slave MCU Pins
        ROM_GPIOPinConfigure(GPIO_PD4_U2RX);
        ROM_GPIOPinConfigure(GPIO_PD5_U2TX);
        ROM_GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);
    }
}

When I try to switch from PA6,7 to PD4,5 data is still sent on PA6,7. Is it possible to switch GPIO pins of UART2?

//Switch UART2 GPIO Pins.
switch_uart2(1);
//Send slave measure command.
ROM_UARTCharPutNonBlocking(UART2_BASE, 'M');

Thanks,

Allan

  • Hello Allan,

    So this is an interesting case... I haven't seen port switching for UART before and thought the answer would be straight forward but it isn't as simple as it looks. I want to review this with my colleague tomorrow and get his thoughts as well because there are a few possible solutions but none of them are what I would call logical compared to what one would expect.

    The root issue is that the UART peripheral doesn't have a dedicated pin muxing register so its not clear how it selects between those two ports. Meanwhile the GPIO peripheral is just getting up both ports for UART operation and there isn't really a built in way to just 'undo' that via TivaWare APIs without resetting the peripheral which is a bit drastic and would impact other ports on the peripheral itself.

    Maybe I just overlooked something in a 'doh' moment here because it feels to me to be pretty illogical to not have a way to do that simply.

    Best Regards,

    Ralph Jacobi

  • Ralph,

    I am a bit of a noob at this, but I might have the UART2 GPIO switching part working. I modified the switch_uart2() function to,

    void switch_uart2(bool mode)
    {
    
        if (mode == 0)
        {
            //Computer Pins
            //UART 2 Config:
            ROM_GPIOPinConfigure(GPIO_PA6_U2RX);
            ROM_GPIOPinConfigure(GPIO_PA7_U2TX);
            ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7);
            //Slave Pins
            GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_4);
            GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_5);
        }
        else
        {
            //Slave MCU Pins
            ROM_GPIOPinConfigure(GPIO_PD4_U2RX);
            ROM_GPIOPinConfigure(GPIO_PD5_U2TX);
            ROM_GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);
            //Slave Pins
            GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6);
            GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_7);
        }
    }

    When "M A" is received on uart2 at gpio pins PA6,7 I am able to switch uart2 to PD4,5.

    if (rx_mode == 0)
        {
            char command = uart2_cmd[0];
            char channel = uart2_cmd[2];
            switch(command)
            {
            case 'M':
                switch(channel)
                {
                case 'A':
                    //Switch UART2 GPIO Pins.
                    switch_uart2(1);
                    //Send slave measure command.
                    ROM_UARTCharPutNonBlocking(UART2_BASE, 'M');
                    //Change the receive mode to 1 (slave).
                    rx_mode = 1;
                    
                    ......

    I have run into a timing issue. When "M" is transmitted on uart2 it takes about 300us for the slave MCU to transmit data back to the main microcontroller. 

    BLUE => UART2 TX, RED UART2 RX

    The uart2 interrupt handler is called again, but it does not receive the data that the slave is transmitting.

    uart2_cmd is consistently filled with 0, 32, 65. While the slave mcu is transmitting 62,45,0,40,162,245,60.

    I thought about adding a timer that would delay sending the "M" character to the slave MCU while uart switching is taking place.

    Do you have any suggestions on how to fix this timing issue?

    Thanks,

    Allan

  • Ralph,

    I think I am getting pretty close on the UART2 switching problem, but have gotten stuck. I have added a timer to delay the transmission of the "M" character to the slave MCU on PD4,5. The timer is enabled when the computer sends "M A" on PD6,7,

    void UART2IntHandler(void)
    {
        uint32_t ui32Status;
    
        //
        // Get the interrrupt status.
        //
        ui32Status = ROM_UARTIntStatus(UART2_BASE, true);
    
        //
        // Clear the asserted interrupts.
        //
        ROM_UARTIntClear(UART2_BASE, ui32Status);
    
        //
        // Loop while there are characters in the receive FIFO.
        //
        //
        while(ROM_UARTCharsAvail(UART2_BASE))
        {
            //
            // Read the next character from the UART and write it back to the UART.
            //
            char buffer = ROM_UARTCharGetNonBlocking(UART2_BASE);
            uart2_cmd[uart2_cmd_index] = buffer;
            uart2_cmd_index++;
        }
    
        //Process packets from the computer.
        if (rx_mode == 0)
        {
            char command = uart2_cmd[0];
            char channel = uart2_cmd[2];
            switch(command)
            {
            case 'M':
                switch(channel)
                {
                case 'A':
                    //Switch UART2 GPIO Pins.
                    switch_uart2(1);
                    //Send slave measure command.
                    ROM_TimerEnable(TIMER0_BASE, TIMER_A);
                    //Change the receive mode to 1 (slave).
                    rx_mode = 1;
                    break;
                case 'B':
                    break;
                case 'C':
                    break;
                case 'D':
                    break;
                }
                break;
            case '?':
                UARTSend(&help_msg, 234);
                //UARTSend((uint8_t *)"Commands:\r\n", 12);
                break;
            }
    
            uart2_cmd_index = 0;
        }
        else
        {
    
            if (uart2_cmd[1] == '+' || uart2_cmd[1] == '-')
            {
                //adc_a = uart2_cmd[2] << 24 + uart2_cmd[3] << 16 + uart2_cmd[4] << 8 + uart2_cmd[5];
                char adc_s[14];
                usprintf(&adc_s, ">%c%010d<", uart2_cmd[1], (uart2_cmd[2] << 24 | uart2_cmd[3] << 16 | uart2_cmd[4] << 8 | uart2_cmd[5]));
                UARTSend(&adc_s,14);
    
                MAP_TimerDisable(TIMER0_BASE, TIMER_A);
                switch_uart2(0);
                rx_mode = 0;
                uart2_cmd_index = 0;
            }
            else
            {
                //UARTSend(&uart2_cmd,8);
            }
    
        }
    }

    After a delay the timer sends the "M" character to the slave MCU.

    void Timer0IntHandler(void)
    {
        uint32_t ui32Status;
        //
        // Get the interrrupt status.
        //
        ui32Status = ROM_TimerIntStatus(TIMER0_BASE, true);
    
        //
        // Clear the asserted interrupts.
        //
        ROM_TimerIntClear(TIMER0_BASE, ui32Status);
    
        ROM_IntMasterDisable();
        //Send slave measure command.
        ROM_UARTCharPutNonBlocking(UART2_BASE, 'M');
        ROM_IntMasterEnable();
    }

    The problem I am running into is the UART2 event handler is never called again after enabling timer 2. UART2 data is still incoming, just never handled by the uart2 interrupt handler.

    Do you have any advice on how to fix this?

    Thanks,

    Allan

  • Hi Allan,

    So after discussing this with my colleague, I have some added details about what is going on here.

    When I try to switch from PA6,7 to PD4,5 data is still sent on PA6,7. Is it possible to switch GPIO pins of UART2?

    So what is happening is that the UART peripheral doesn't have any dedicated registers to direct which GPIO peripherals it is sending the TX signal too, so when the UART sends data over TX, it is 0broadcast to the GPIO peripheral entirely. At that point the GPIO peripheral knows which pins are configured for UART and any of those pins which are then get the signal from the UART peripheral connected to the output pin, and so it will send the TX data out across all pins that are configured for UART.

    Based on this, your code to turn the other port back into a standard GPIO Output is the most efficient manner to solve this issue as then the GPIO peripheral is only trying to send the UART data out over the port you intend as the other UART port is now a GPIO output.

    The problem I am running into is the UART2 event handler is never called again after enabling timer 2. UART2 data is still incoming, just never handled by the uart2 interrupt handler.

    Do you have any advice on how to fix this?

    Looking at your code, you are disabling interrupts, sending UART data, and then enabling interrupts. My guess is that the UART2 interrupt got triggered during the time you had interrupts disabled and since that was the case it's just sitting there and can't be re-triggered until its cleared.

    I don't really advise on sending UART data in the ISR of another peripheral so you may want to consider instead using a global flag and then sending the UART data from your application instead so you don't need to disable interrupts.

    Best Regards,

    Ralph Jacobi

  • Ralph,

    I am still running into an issue were data received on PD4,5 does not trigger the UART2 interrupt.

    I have configured the GPIO so data received on either PD4 or PA6 will trigger an interrupt.

    //UART 2 Config:
    ROM_GPIOPinConfigure(GPIO_PD4_U2RX);
    ROM_GPIOPinConfigure(GPIO_PD5_U2TX);
    ROM_GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);
    
    ROM_GPIOPinConfigure(GPIO_PA6_U2RX);
    ROM_GPIOPinConfigure(GPIO_PA7_U2TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7);

    I can transmit on both but only receive on PA6.

    Do you have any suggestions on how to get the interrupt handler to respond when data is received on PD4?

    Thanks,

    Allan

  • Hello Allan,

    Are you using this code when trying to receive data on Port D?

            //Slave MCU Pins
            ROM_GPIOPinConfigure(GPIO_PD4_U2RX);
            ROM_GPIOPinConfigure(GPIO_PD5_U2TX);
            ROM_GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);
            //Slave Pins
            GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6);
            GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_7);

    If not and both Port A and Port D are configured for UART RX, it could be an issue with how the shared RX signals are being selected / routed and if so I am not sure there is a solution for it. I don't have insight onto the specifics of the design for this MCU so I wouldn't be able to really offer more detail there.

    If you have disabled Port A as a valid UART port in the GPIO module though, then that is something we would need to debug further to understand what might be going on.

    Best Regards,

    Ralph Jacobi

  • Hello Ralph,

    Sorry for the confusion. Let me clarify what I am trying to do. I have completely removed switch_uart2(). I am now trying to assign PA6,7 and PD4,5 in int main(). Below is the complete int main() function.

    int
    main(void)
    {
        //
        // Run from the PLL at 120 MHz.
        // Note: SYSCTL_CFG_VCO_240 is a new setting provided in TivaWare 2.2.x and
        // later to better reflect the actual VCO speed due to SYSCTL#22.
        //
        g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                                 SYSCTL_OSC_MAIN |
                                                 SYSCTL_USE_PLL |
                                                 SYSCTL_CFG_VCO_240), 120000000);
                                                 
        //
        // Enable the GPIO ports.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOM);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOP);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOR);
    
        //
        // Enable the peripherals used by this example.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART2);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART3);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART5);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART7);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    
        //
        // Enable processor interrupts.
        //
        ROM_IntMasterEnable();
    
        //Setup the GPIO Pins for UART.
        //
        // Set GPIO A0 and A1 as UART pins.
        //
        ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
        ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
        ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //UART 2 Config:
        ROM_GPIOPinConfigure(GPIO_PD4_U2RX);
        ROM_GPIOPinConfigure(GPIO_PD5_U2TX);
        ROM_GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);
    
        ROM_GPIOPinConfigure(GPIO_PA6_U2RX);
        ROM_GPIOPinConfigure(GPIO_PA7_U2TX);
        ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7);
    
    
        //UART 3 Config:
        ROM_GPIOPinConfigure(GPIO_PJ0_U3RX);
        ROM_GPIOPinConfigure(GPIO_PJ1_U3TX);
        ROM_GPIOPinTypeUART(GPIO_PORTJ_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //UART 5 Config:
        ROM_GPIOPinConfigure(GPIO_PC6_U5RX);
        ROM_GPIOPinConfigure(GPIO_PC7_U5TX);
        ROM_GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_6 | GPIO_PIN_7);
    
        //UART 7 Config:
        ROM_GPIOPinConfigure(GPIO_PC4_U7RX);
        ROM_GPIOPinConfigure(GPIO_PC5_U7TX);
        ROM_GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5);
    
        // Configure the UART(s).
    
        //
        // Configure the UART2 for 115,200, 8-N-1 operation.
        //
        ROM_UARTConfigSetExpClk(UART2_BASE, g_ui32SysClock, 115200,
                                (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                                 UART_CONFIG_PAR_NONE));
    
         //
         // Configure the UART5 for 115,200, 8-N-1 operation.
         //
         ROM_UARTConfigSetExpClk(UART5_BASE, g_ui32SysClock, 115200,
                                 (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                                  UART_CONFIG_PAR_NONE));
    
         //
         // Configure the UART7 for 115,200, 8-N-1 operation.
         //
         ROM_UARTConfigSetExpClk(UART7_BASE, g_ui32SysClock, 115200,
                                 (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                                  UART_CONFIG_PAR_NONE));
    
        //
        // Configure the UART3 for 115,200, 8-N-1 operation.
        //
        ROM_UARTConfigSetExpClk(UART3_BASE, g_ui32SysClock, 115200,
                                 (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                                  UART_CONFIG_PAR_NONE));
        //
        // Enable the UART2 interrupt.
        //
        ROM_IntEnable(INT_UART2);
        ROM_UARTIntEnable(UART2_BASE, UART_INT_RX | UART_INT_RT);
        //
        // Enable the UART5 interrupt.
        //
        ROM_IntEnable(INT_UART5);
        ROM_UARTIntEnable(UART5_BASE, UART_INT_RX | UART_INT_RT);
        //
        // Enable the UART7 interrupt.
        //
        ROM_IntEnable(INT_UART7);
        ROM_UARTIntEnable(UART7_BASE, UART_INT_RX | UART_INT_RT);
        //
        // Enable the UART3 interrupt.
        //
        ROM_IntEnable(INT_UART3);
        ROM_UARTIntEnable(UART3_BASE, UART_INT_RX | UART_INT_RT);
    
        //
        // Loop forever counting electrons.
        //
        //USER RX -> 1
        //CH A RX -> 2
        //CH B RX -> 3
        //CH C RX -> 4
        //CH D RX -> 5
        while(1)
        {
            switch(system_state)
            {
            case 1: //User Commands
                switch(uart2_cmd[0])
                {
                case 'M':
                    switch(uart2_cmd[2])
                    {
                    case 'A':
                        //Switch UART2 GPIO Pins to Channel A.
                        switch_uart2(1);
                        UART2Send("M",1);
                        system_state = 2;
                        break;
                    case 'B':
                        break;
                    case 'C':
                        break;
                    case 'D':
                        break;
                    }
                    break;
                case '?':
                    UART2Send(&help_msg, 234);
                    system_state = 0;
                    break;
                }
                break;
            case 2: //Channel A RX
    
                if (uart2_cmd[1] == '+' || uart2_cmd[1] == '-')
                {
                    switch_uart2(0);
    
                    char adc_s[14];
                    usprintf(&adc_s, ">%c%010d<", uart2_cmd[1], (uart2_cmd[2] << 24 | uart2_cmd[3] << 16 | uart2_cmd[4] << 8 | uart2_cmd[5]));
    
                    UART2Send(&adc_s,14);
                    uart2_cmd_index = 0;
                }
                else
                {
                    UART2Send("M",1);
                    uart2_cmd_index = 0;
                }
                break;
            case 3: //Channel B RX
                break;
            case 4: //Channel C RX
                break;
            case 5: //Channel D RX
                break;
            }
        }
    }

    Using a computer I am able to send the string "M A". The application sends out an "M" character repeatedly on port A and D simultaneously. Using an oscilloscope I can confirm that data is being received in response to the "M" character being sent. The problem is the UARt2 interrupt handler is only called when data is received on port A and not on port D. Below is the UART2 interrupt handler,

    void UART2IntHandler(void)
    {
        uint32_t ui32Status;
    
        // Get the interrrupt status.
        ui32Status = ROM_UARTIntStatus(UART2_BASE, true);
    
        // Clear the asserted interrupts.
        ROM_UARTIntClear(UART2_BASE, ui32Status);
    
        // Loop while there are characters in the receive FIFO.
        while(ROM_UARTCharsAvail(UART2_BASE))
        {
            char buffer = ROM_UARTCharGetNonBlocking(UART2_BASE);
            uart2_cmd[uart2_cmd_index] = buffer;
            uart2_cmd_index++;
        }
    
        if (system_state == 0)
        {
            system_state = 1;
        }
    
    }

    I was reading through the ROM user guide and I may have found an answer to the problem. 

    If the same signal is assigned to two different GPIO port pins, the signal is assigned to the port
    with the lowest letter and the assignment to the higher letter port is ignored.
    

    So I guess this isn't going to work? What are your thoughts?

    Thanks,

    Allan

  • Hi Allan,

    Thanks for posting the full code, that definitely helps. But more over thanks for finding that note from the ROM User's Guide. That's what I was expecting to be how the device handles the dual port assignment but couldn't find a confirmation for yet.

    Correct that it won't work as it is done currently. You would need to use the switching function so that only one port is assigned to UART2 at a time. Or use a different UART module where you won't run into these sorts of complications (I'm presuming a hardware layout is gating this, but resolving that may have to be the long-term solution...)

    Best Regards,

    Ralph Jacobi

  • Hi Ralph,

    I was hoping to avoid using a different UART module, because that would require rework on 100 boards. I guess I will just have to rework them. Thanks for your help.

    Best,

    Allan