PSoC 4 Software UART receive

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
Michael_K
Level 3
Level 3
25 replies posted 10 likes given 50 sign-ins

Hello everybody,

 

I have the following problem: I have two glued boxes which have only 2 free GPIOs and where I have no acceess to the other GPIOs. The two boxes should be able to communicate via UART. For Tx( transmit) there is a ready-to-use block which I can put on each GPIO.

 

For Rx, there is no such a ready-to-use block an I have taken the source code from another forum post.(https://community.infineon.com/t5/PSoC-4/Software-Based-Uart-Receive/m-p/220861)

 

I can only receive characters if there is no delay in the source code. If I have a CyDelay() in my source code, then the Software Based Uart Receive no longer works.

Is there a solution to this? If you use an SCB UART block, you can only use certain GPIO pins. Can the GPIO pin somehow be forwarded to another GPIO pin? I am testing it on the Piosnieer Kit 042 with a PSoC 4200, but on the box itself is a PSoC 4100.

Best Regards

Michael

0 Likes
1 Solution

Michael,

If you're forced to use P2[0] for your Rx input then you're basically forced to use the SW Rx UART.

To make your SW more reliable you need to find out what in your code is preventing your SW UART from executing reliably.

I use a debugging technique called "framing pulses".  Since you indicated you have two free GPIOs (I'm assuming P2[0] is not included) you could use these two GPIOs as outputs for viewing on a scope.

Framing pulses are one way to profile your code in real-time.

How to create framing pulses:

  • In TopDesign, allocate one (or more) GPIO pins as outputs.  Chose the pins in the DWR/Pins that are available to the outside of the box.  Call the pins FRM_1 and FRM_2.
  • In your SW chose the code sections you want to profile.  For example, as a starting,  in your code you might want to profile serialEvent().
  • Place a line FRM_1_Write(1) at the beginning of the function.
  • Place a line FRM_1_Write(0) at the end of the function.  (See Example below.)
  • void serialEvent()
    {
    FRM_1_Write(1);    // Begin frame pulse
        if (start_bit_flag) {
                rx_data = receive_byte() ;
                //sprintf(str, "%c\n", (char)rx_data);
                //HARD_UART_UartPutString(str);
                rx_string[indexCmd] = rx_data;
                indexCmd++;
                
                if (rx_data == 0x0D || rx_data == 0x0A)
                {
                    stringComplete = 1;
                }
                start_bit_flag = 0 ;
    
                soft_rx_ClearInterrupt() ;
                soft_rx_isr_ClearPending() ;
                soft_rx_isr_Enable() ;  
           } 
            if(stringComplete)
            {
                HARD_UART_UartPutString(rx_string);
               // int result = strcmp(rx_string, "RED\n");
               // int result2 = strcmp(rx_string, "BLUE\n");
               // sprintf(str, "strcmp result %d  %d  ",result,result2);
                HARD_UART_UartPutString(str);
                 
                if( !strcmp(rx_string, "RED\n"))
                {
                    RED_LED_Write(0);
                    BLUE_LED_Write(1);
                    sprintf(str, "RED Block ");
                HARD_UART_UartPutString(str);
                HARD_UART_UartPutString("\r");
                }
                else if(!strcmp(rx_string, "BLUE\n"))
                {
                    RED_LED_Write(1);
                    BLUE_LED_Write(0);
                    sprintf(str, "BLUE Block ");
                HARD_UART_UartPutString(str);
                HARD_UART_UartPutString("\r");
                }
                else
                {
                     RED_LED_Write(1);
                    BLUE_LED_Write(1);
                    sprintf(str, "else Block ");
                HARD_UART_UartPutString(str);
                HARD_UART_UartPutString("\r");
                }
                memset(rx_string,0,sizeof(rx_string));
                stringComplete = 0;
                indexCmd = 0;    
            }
    FRM_1_Write(0);    // End frame pulse
    }
  • You can place the second frame (FRM_2) inside another function such as the start_bit_isr().  (See Example below.)
    CY_ISR(start_bit_isr)
    {
    FRM_2_Write(1);   // Begin the frame
        soft_rx_ClearInterrupt() ;
        soft_rx_isr_Disable() ;
        start_bit_flag = 1 ;
    FRM_2_Write(0);   // End the frame
    }​

Now with the framing pulses being viewed on scope, you can see the timing relationship between the pulses.  You can also see what a 'normal' pulse for the function should look like and if the pulse is too short or too long, you can debug further.

You can change the location of the framing pulse begin/end points in the code to better highlight where the issue might be.

If you can place a scope probe on the incoming Rx pin you can see the timing relationship of the input to the framing pulses.

I noticed that you are operating at 9600 baud.  I'm not familiar with this SW UART but in general SW UARTs have more difficultly being accurate as the baud rate is higher.

 

Len
"Engineering is an Art. The Art of Compromise."

View solution in original post

0 Likes
6 Replies
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

If you can change rx pin to P4[0]

002-Pins.JPG

Then you can change the configuration of the HARD_UART for full duplex.

001-UART_TX_RX.JPG

Otherwise, I don't have a good idea now.

moto

Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

Michael,

I disabled the SW RX UART and added a UART_Rx that only is Rx.

Len_CONSULTRON_0-1652353771574.png

With UART_Rx, either P0[4] or P3[0] can be assigned.

Len_CONSULTRON_1-1652353843265.png

If instead you convert HARD_UART to Full Duplex (Tx + Rx), then only P4[0] can be assigned for Rx.

I see that originally you intended P2[0] to be your Rx pin.   If you are stuck with using P2[0] you're somewhat out of luck.   The PSoC4 generally does not have the GPIO flexibility of other PSoCs.

Suggestions:

  • Avoid using CyDelay()s.   It's a good function to quickly create a delay for demo code or experimental code.   (Not great for production code in units shipped to customers)
  • If you have to use CyDelay()s NEVER use them in ISRs.   Normally when used at the main() task level, an CyDelay() can be interrupted.  This means that interrupts such as your soft_rx_isr as well as other interrupts can still be processed in reasonable time.   If the CyDelay() is encountered inside an interrupt routine, then it acts as a blocking function preventing all other interrupts from being processed.
  • You can use the SysTick resources to allow for delays.
Len
"Engineering is an Art. The Art of Compromise."
Michael_K
Level 3
Level 3
25 replies posted 10 likes given 50 sign-ins

It will be not sent to a customer in this form.

Thanks for your advice, but I have no way to get to pin P4.0 or P0.4. I don't use CyDelays() or other delays in ISRs in this example.

I will try the SysTick timer or put the source code of the receive_byte() function into the ISR, but that would be a bad solution.

Best Regards

Michael

0 Likes

Michael,

I agree that putting the source code for the receive_byte() function into the ISR is generally not a good idea.

I learned from a SW coding expert many years ago that the best solution for any ISR routine is to be "short and sweet".  I other words:

  • Get into the ISR quickly.
  • Do the minimum needed to find out what caused the interrupt.
  • Get any relevant data from the source of the interrupt.  This might mean you might need to buffer incoming data and signal the main() task that data is available.
  • Return from the interrupt with the least amount of time spent in the ISR.
  • In the main() task process the data.

 

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Michael,

If you're forced to use P2[0] for your Rx input then you're basically forced to use the SW Rx UART.

To make your SW more reliable you need to find out what in your code is preventing your SW UART from executing reliably.

I use a debugging technique called "framing pulses".  Since you indicated you have two free GPIOs (I'm assuming P2[0] is not included) you could use these two GPIOs as outputs for viewing on a scope.

Framing pulses are one way to profile your code in real-time.

How to create framing pulses:

  • In TopDesign, allocate one (or more) GPIO pins as outputs.  Chose the pins in the DWR/Pins that are available to the outside of the box.  Call the pins FRM_1 and FRM_2.
  • In your SW chose the code sections you want to profile.  For example, as a starting,  in your code you might want to profile serialEvent().
  • Place a line FRM_1_Write(1) at the beginning of the function.
  • Place a line FRM_1_Write(0) at the end of the function.  (See Example below.)
  • void serialEvent()
    {
    FRM_1_Write(1);    // Begin frame pulse
        if (start_bit_flag) {
                rx_data = receive_byte() ;
                //sprintf(str, "%c\n", (char)rx_data);
                //HARD_UART_UartPutString(str);
                rx_string[indexCmd] = rx_data;
                indexCmd++;
                
                if (rx_data == 0x0D || rx_data == 0x0A)
                {
                    stringComplete = 1;
                }
                start_bit_flag = 0 ;
    
                soft_rx_ClearInterrupt() ;
                soft_rx_isr_ClearPending() ;
                soft_rx_isr_Enable() ;  
           } 
            if(stringComplete)
            {
                HARD_UART_UartPutString(rx_string);
               // int result = strcmp(rx_string, "RED\n");
               // int result2 = strcmp(rx_string, "BLUE\n");
               // sprintf(str, "strcmp result %d  %d  ",result,result2);
                HARD_UART_UartPutString(str);
                 
                if( !strcmp(rx_string, "RED\n"))
                {
                    RED_LED_Write(0);
                    BLUE_LED_Write(1);
                    sprintf(str, "RED Block ");
                HARD_UART_UartPutString(str);
                HARD_UART_UartPutString("\r");
                }
                else if(!strcmp(rx_string, "BLUE\n"))
                {
                    RED_LED_Write(1);
                    BLUE_LED_Write(0);
                    sprintf(str, "BLUE Block ");
                HARD_UART_UartPutString(str);
                HARD_UART_UartPutString("\r");
                }
                else
                {
                     RED_LED_Write(1);
                    BLUE_LED_Write(1);
                    sprintf(str, "else Block ");
                HARD_UART_UartPutString(str);
                HARD_UART_UartPutString("\r");
                }
                memset(rx_string,0,sizeof(rx_string));
                stringComplete = 0;
                indexCmd = 0;    
            }
    FRM_1_Write(0);    // End frame pulse
    }
  • You can place the second frame (FRM_2) inside another function such as the start_bit_isr().  (See Example below.)
    CY_ISR(start_bit_isr)
    {
    FRM_2_Write(1);   // Begin the frame
        soft_rx_ClearInterrupt() ;
        soft_rx_isr_Disable() ;
        start_bit_flag = 1 ;
    FRM_2_Write(0);   // End the frame
    }​

Now with the framing pulses being viewed on scope, you can see the timing relationship between the pulses.  You can also see what a 'normal' pulse for the function should look like and if the pulse is too short or too long, you can debug further.

You can change the location of the framing pulse begin/end points in the code to better highlight where the issue might be.

If you can place a scope probe on the incoming Rx pin you can see the timing relationship of the input to the framing pulses.

I noticed that you are operating at 9600 baud.  I'm not familiar with this SW UART but in general SW UARTs have more difficultly being accurate as the baud rate is higher.

 

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
Michael_K
Level 3
Level 3
25 replies posted 10 likes given 50 sign-ins

Hello Len,

I have used something like this with the framing pulses to measure how long certain functions took. I will try it out. 

Unfortunately, I haven't been able to try it out with the SystickTimer yet because I just had something else important to do.

I will keep you up to date 🙂

Best Regards

Michael 

0 Likes